Skip to content

Commit 6526e4a

Browse files
hchokshimeta-codesync[bot]
authored andcommitted
Add owner and referenceable_from? to const value prototype, populate const value types in context
Summary: - Populate const value types in the generator context - this is equivalent to `expected_type_` in mstch - Expose const value `owner` on Whisker prototype - Add `referenceable_from?` function, which is equivalent to `value:referenceable?` in mstch Reviewed By: echistyakov Differential Revision: D88299169 fbshipit-source-id: 996c7f9714dc8f1c7205fb482cd9f81b103e716b
1 parent 6b36381 commit 6526e4a

File tree

2 files changed

+110
-16
lines changed

2 files changed

+110
-16
lines changed

third-party/thrift/src/thrift/compiler/generate/t_whisker_generator.cc

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,14 @@ t_whisker_generator::make_prototype_for_const_value(
371371
const prototype_database& proto) const {
372372
prototype_builder<h_const_value> def;
373373
using cv = t_const_value::t_const_value_kind;
374-
def.property("type", [&proto](const t_const_value& self) {
375-
return resolve_derived_t_type(proto, self.ttype().deref());
374+
def.property("type", [this, &proto](const t_const_value& self) {
375+
// Prioritize AST populated ttype, fallback to inferred expected type in
376+
// context
377+
const t_type_ref& type = self.ttype().empty()
378+
? context().get_const_value_type(self)
379+
: self.ttype();
380+
return type.empty() ? whisker::make::null
381+
: resolve_derived_t_type(proto, type.deref());
376382
});
377383
def.property("bool?", [](const t_const_value& self) {
378384
return self.kind() == cv::CV_BOOL;
@@ -501,6 +507,20 @@ t_whisker_generator::make_prototype_for_const_value(
501507
}
502508
return w::array(result);
503509
});
510+
def.property("owner", mem_fn(&t_const_value::get_owner, proto.of<t_const>()));
511+
def.function(
512+
"referenceable_from?",
513+
[](const t_const_value& self, function::context ctx) {
514+
ctx.declare_arity(1);
515+
ctx.declare_named_arguments({});
516+
const t_const* from_const =
517+
ctx.raw().positional_arguments()[0].is_null()
518+
? nullptr
519+
: ctx.argument<whisker::native_handle<t_const>>(0).ptr().get();
520+
// value can be referenced if it is not anonymous, and is being
521+
// referenced from any const that's not the owner
522+
return self.get_owner() != nullptr && self.get_owner() != from_const;
523+
});
504524

505525
return std::move(def).make();
506526
}
@@ -1095,4 +1115,73 @@ void t_whisker_generator::render_to_file(
10951115
write_to_file(output_file, render(template_file, context));
10961116
}
10971117

1118+
void whisker_generator_context::register_visitors(
1119+
t_whisker_generator::context_visitor& visitor) {
1120+
using context = t_whisker_generator::whisker_generator_visitor_context;
1121+
visitor.add_interface_visitor(
1122+
[this](const context&, const t_interface& node) {
1123+
for (const t_function& function : node.functions()) {
1124+
function_parents_[&function] = &node;
1125+
}
1126+
});
1127+
visitor.add_structured_definition_visitor(
1128+
[this](const context&, const t_structured& node) {
1129+
for (const t_field& field : node.fields()) {
1130+
field_parents_[&field] = &node;
1131+
}
1132+
});
1133+
1134+
visitor.add_const_visitor([this](const context&, const t_const& node) {
1135+
if (node.value() != nullptr) {
1136+
visit_const_value(node.value(), node.type_ref());
1137+
}
1138+
});
1139+
visitor.add_field_visitor([this](const context&, const t_field& node) {
1140+
if (node.default_value() != nullptr) {
1141+
visit_const_value(node.default_value(), node.type());
1142+
}
1143+
});
1144+
visitor.add_function_param_visitor(
1145+
[this](const context&, const t_field& node) {
1146+
if (node.default_value() != nullptr) {
1147+
visit_const_value(node.default_value(), node.type());
1148+
}
1149+
});
1150+
}
1151+
1152+
void whisker_generator_context::visit_const_value(
1153+
const t_const_value* value, const t_type_ref& expected_type) {
1154+
if (value == nullptr || expected_type.empty()) {
1155+
return;
1156+
}
1157+
1158+
const_value_types_[value] = expected_type;
1159+
if (const auto* map = expected_type->try_as<t_map>();
1160+
map != nullptr && value->kind() == t_const_value::CV_MAP) {
1161+
for (const auto& [key, val] : value->get_map()) {
1162+
visit_const_value(key, map->key_type());
1163+
visit_const_value(val, map->val_type());
1164+
}
1165+
} else if (const auto* list = expected_type->try_as<t_list>();
1166+
list != nullptr && value->kind() == t_const_value::CV_LIST) {
1167+
for (const t_const_value* val : value->get_list()) {
1168+
visit_const_value(val, list->elem_type());
1169+
}
1170+
} else if (const auto* set = expected_type->try_as<t_set>();
1171+
set != nullptr && value->kind() == t_const_value::CV_LIST) {
1172+
for (const t_const_value* val : value->get_list()) {
1173+
visit_const_value(val, set->elem_type());
1174+
}
1175+
} else if (const auto* structured = expected_type->try_as<t_structured>();
1176+
structured != nullptr && value->kind() == t_const_value::CV_MAP) {
1177+
for (const auto& [key, val] : value->get_map()) {
1178+
if (const t_field* field = key->kind() == t_const_value::CV_STRING
1179+
? structured->get_field_by_name(key->get_string())
1180+
: nullptr) {
1181+
visit_const_value(val, field->type());
1182+
}
1183+
}
1184+
}
1185+
}
1186+
10981187
} // namespace apache::thrift::compiler

third-party/thrift/src/thrift/compiler/generate/t_whisker_generator.h

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -77,20 +77,7 @@ class whisker_generator_context {
7777
// contains references to its fields, but t_field does not contain a
7878
// corresponding reverse reference to the t_struct).
7979
void register_visitors(
80-
basic_ast_visitor<true, const_visitor_context&>& visitor) {
81-
visitor.add_interface_visitor(
82-
[this](const const_visitor_context&, const t_interface& node) {
83-
for (const t_function& function : node.functions()) {
84-
function_parents_[&function] = &node;
85-
}
86-
});
87-
visitor.add_structured_definition_visitor(
88-
[this](const const_visitor_context&, const t_structured& node) {
89-
for (const t_field& field : node.fields()) {
90-
field_parents_[&field] = &node;
91-
}
92-
});
93-
}
80+
basic_ast_visitor<true, const_visitor_context&>& visitor);
9481

9582
// Get the parent structured definition (back-reference) of a field.
9683
// The result will always be nullptr for fields from param lists and throws
@@ -105,6 +92,12 @@ class whisker_generator_context {
10592
return it != function_parents_.end() ? it->second : nullptr;
10693
}
10794

95+
/** Get the expected type of a const value, or `nullptr` if indeterminate. */
96+
const t_type_ref& get_const_value_type(const t_const_value& value) const {
97+
auto it = const_value_types_.find(&value);
98+
return it != const_value_types_.end() ? it->second : t_type_ref::none();
99+
}
100+
108101
private:
109102
// Field to parent back-references for fields in user-defined structured
110103
// definitions (i.e. struct, union, exception)
@@ -113,6 +106,18 @@ class whisker_generator_context {
113106
// Function to parent back-references (i.e. to a function's containing service
114107
// or interaction)
115108
std::unordered_map<const t_function*, const t_interface*> function_parents_;
109+
110+
/**
111+
* Map from const value to the expected type of that value.
112+
* t_const_value::ttype is not always populated - e.g. for nested consts
113+
* (list/map elements). Instead, we must recursively infer types from a root
114+
* const for such cases.
115+
*/
116+
std::unordered_map<const t_const_value*, t_type_ref> const_value_types_;
117+
118+
/** Recursively visit a const value and populate `const_value_types_`. */
119+
void visit_const_value(
120+
const t_const_value* value, const t_type_ref& expected_type);
116121
};
117122

118123
/**

0 commit comments

Comments
 (0)