Skip to content

Commit 0b18ffa

Browse files
Uday Garikipatimeta-codesync[bot]
authored andcommitted
Qualify enclosing-struct param in adapted_field_t storage type (unblock frozen2 + field @cpp.Adapter)
Summary: Field-level `cpp.Adapter` storage type `adapted_field_t<Adapter, id, ThriftType, EnclosingStruct>` emitted `EnclosingStruct` unqualified. Under `namespace apache::thrift::frozen` (frozen2 layouts) the bare name doesn't resolve, so `*_layouts.h` didn't compile and such structs couldn't be frozen. Adds `get_qualified_storage_type` / `get_qualified_native_type` to qualify only the enclosing-struct param, opt-in via a new `cpp_frozen2_storage_type` property. Default `get_storage_type` stays unqualified, so `*_types.h` is unchanged. Reviewed By: vitaut Differential Revision: D115490480 fbshipit-source-id: 5a144467edf9d22e670fe38a6010590b62eee062
1 parent c0048a4 commit 0b18ffa

8 files changed

Lines changed: 365 additions & 51 deletions

File tree

third-party/thrift/src/thrift/compiler/generate/cpp/name_resolver.cc

Lines changed: 77 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,25 @@ std::string gen_template_type(
6666

6767
const std::string& cpp_name_resolver::get_native_type(
6868
const t_field& field, const t_structured& parent) {
69+
return detail::get_or_gen(field_type_cache_, &field, [&]() {
70+
return gen_field_native_type(field, parent, /*qualify_parent=*/false);
71+
});
72+
}
73+
74+
const std::string& cpp_name_resolver::get_qualified_native_type(
75+
const t_field& field, const t_structured& parent) {
76+
// Only a field-level @cpp.Adapter names its enclosing struct; every other
77+
// field resolves identically to get_native_type.
78+
if (field.find_structured_annotation_or_null(kCppAdapterUri) == nullptr) {
79+
return get_native_type(field, parent);
80+
}
81+
return detail::get_or_gen(qualified_field_type_cache_, &field, [&]() {
82+
return gen_field_native_type(field, parent, /*qualify_parent=*/true);
83+
});
84+
}
85+
86+
std::string cpp_name_resolver::gen_field_native_type(
87+
const t_field& field, const t_structured& parent, bool qualify_parent) {
6988
const t_type& type = *field.type();
7089

7190
// Handle @cpp.Adapter on field.
@@ -78,50 +97,51 @@ const std::string& cpp_name_resolver::get_native_type(
7897
}
7998
const auto& adapter_on_field =
8099
annotation->get_value_from_structured_annotation("name").get_string();
81-
return detail::get_or_gen(field_type_cache_, &field, [&]() {
82-
// When the field also has @cpp.Type, use that as the base type for the
83-
// adapter wrapping instead of resolving from the type node.
84-
if (auto* cpp_type_annot =
85-
field.find_structured_annotation_or_null(kCppTypeUri)) {
86-
if (auto* name =
87-
cpp_type_annot->get_value_from_structured_annotation_or_null(
88-
"name")) {
89-
return gen_adapted_type(
90-
&adapter_on_field, field.id(), name->get_string(), parent);
91-
}
92-
if (auto* tmpl =
93-
cpp_type_annot->get_value_from_structured_annotation_or_null(
94-
"template")) {
95-
return gen_adapted_type(
96-
&adapter_on_field,
97-
field.id(),
98-
gen_container_type(
99-
type.get_true_type()->as<t_container>(),
100-
&cpp_name_resolver::get_native_type,
101-
&tmpl->get_string()),
102-
parent);
103-
}
100+
// When the field also has @cpp.Type, use that as the base type for the
101+
// adapter wrapping instead of resolving from the type node.
102+
if (auto* cpp_type_annot =
103+
field.find_structured_annotation_or_null(kCppTypeUri)) {
104+
if (auto* name =
105+
cpp_type_annot->get_value_from_structured_annotation_or_null(
106+
"name")) {
107+
return gen_adapted_type(
108+
&adapter_on_field,
109+
field.id(),
110+
name->get_string(),
111+
parent,
112+
qualify_parent);
104113
}
105-
return gen_field_type(field.id(), type, parent, &adapter_on_field);
106-
});
114+
if (auto* tmpl =
115+
cpp_type_annot->get_value_from_structured_annotation_or_null(
116+
"template")) {
117+
return gen_adapted_type(
118+
&adapter_on_field,
119+
field.id(),
120+
gen_container_type(
121+
type.get_true_type()->as<t_container>(),
122+
&cpp_name_resolver::get_native_type,
123+
&tmpl->get_string()),
124+
parent,
125+
qualify_parent);
126+
}
127+
}
128+
return gen_field_type(
129+
field.id(), type, parent, &adapter_on_field, qualify_parent);
107130
}
108131

109132
// Handle @cpp.Type on field.
110133
if (auto* annotation =
111134
field.find_structured_annotation_or_null(kCppTypeUri)) {
112135
if (auto name =
113136
annotation->get_value_from_structured_annotation_or_null("name")) {
114-
return detail::get_or_gen(
115-
field_type_cache_, &field, [&]() { return name->get_string(); });
137+
return name->get_string();
116138
} else {
117139
auto& tmplate =
118140
annotation->get_value_from_structured_annotation("template");
119-
return detail::get_or_gen(field_type_cache_, &field, [&]() {
120-
return gen_container_type(
121-
type.get_true_type()->as<t_container>(),
122-
&cpp_name_resolver::get_native_type,
123-
&tmplate.get_string());
124-
});
141+
return gen_container_type(
142+
type.get_true_type()->as<t_container>(),
143+
&cpp_name_resolver::get_native_type,
144+
&tmplate.get_string());
125145
}
126146
}
127147

@@ -322,6 +342,18 @@ const std::string& cpp_name_resolver::get_storage_type(
322342
});
323343
}
324344

345+
const std::string& cpp_name_resolver::get_qualified_storage_type(
346+
const t_field& field, const t_structured& parent) {
347+
auto ref_type = gen::cpp::find_ref_type(field);
348+
const std::string& native_type = get_qualified_native_type(field, parent);
349+
if (ref_type == cpp_reference_type::none) {
350+
return native_type;
351+
}
352+
return detail::get_or_gen(qualified_storage_type_cache_, &field, [&]() {
353+
return gen_storage_type(native_type, ref_type);
354+
});
355+
}
356+
325357
const std::string& cpp_name_resolver::get_reference_type(const t_field& node) {
326358
return detail::get_or_gen(field_reference_type_cache_, &node, [&]() {
327359
return gen_reference_type(node);
@@ -609,17 +641,18 @@ std::string cpp_name_resolver::gen_adapted_type(
609641
const std::string* adapter,
610642
int16_t field_id,
611643
const std::string& standard_type,
612-
const t_structured& parent) {
613-
return adapter == nullptr
614-
? standard_type
615-
: detail::gen_template_type(
616-
"::apache::thrift::adapt_detail::adapted_field_t",
617-
{
618-
*adapter,
619-
std::to_string(field_id),
620-
standard_type,
621-
get_underlying_name(parent),
622-
});
644+
const t_structured& parent,
645+
bool qualify_parent) {
646+
if (adapter == nullptr) {
647+
return standard_type;
648+
}
649+
return detail::gen_template_type(
650+
"::apache::thrift::adapt_detail::adapted_field_t",
651+
{*adapter,
652+
std::to_string(field_id),
653+
standard_type,
654+
qualify_parent ? get_underlying_namespaced_name(parent)
655+
: get_underlying_name(parent)});
623656
}
624657

625658
std::string cpp_name_resolver::gen_type_tag(

third-party/thrift/src/thrift/compiler/generate/cpp/name_resolver.h

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ class cpp_name_resolver {
6666
// Returns C++ type name for the given Thrift field.
6767
const std::string& get_native_type(
6868
const t_field& field, const t_structured& parent);
69+
// Like get_native_type(field, parent) but namespace-qualifies a field-level
70+
// @cpp.Adapter's enclosing-struct parameter (see get_qualified_storage_type).
71+
const std::string& get_qualified_native_type(
72+
const t_field& field, const t_structured& parent);
6973

7074
const std::string& get_native_type(const t_const& cnst);
7175

@@ -158,6 +162,12 @@ class cpp_name_resolver {
158162
const std::string& get_storage_type(
159163
const t_field& field, const t_structured& parent);
160164

165+
// Like get_storage_type, but namespace-qualifies a field-level @cpp.Adapter's
166+
// enclosing-struct parameter so the storage type is valid when emitted
167+
// outside the struct's own namespace.
168+
const std::string& get_qualified_storage_type(
169+
const t_field& field, const t_structured& parent);
170+
161171
// Returns the C++ reference type of the field.
162172
const std::string& get_reference_type(const t_field& node);
163173

@@ -195,6 +205,8 @@ class cpp_name_resolver {
195205
std::unordered_map<const t_stream*, std::string> stream_cache_;
196206
std::unordered_map<const t_const*, std::string> const_cache_;
197207
std::unordered_map<const t_field*, std::string> field_type_cache_;
208+
std::unordered_map<const t_field*, std::string> qualified_field_type_cache_;
209+
std::unordered_map<const t_field*, std::string> qualified_storage_type_cache_;
198210
std::unordered_map<const t_type*, std::string> standard_type_cache_;
199211
std::unordered_map<const t_field*, std::string> field_standard_type_cache_;
200212
std::unordered_map<const t_type*, std::string> underlying_type_cache_;
@@ -240,9 +252,16 @@ class cpp_name_resolver {
240252
int16_t field_id,
241253
const t_type& type,
242254
const t_structured& parent,
243-
const std::string* adapter) {
244-
return gen_adapted_type(adapter, field_id, gen_type(type), parent);
255+
const std::string* adapter,
256+
bool qualify_parent = false) {
257+
return gen_adapted_type(
258+
adapter, field_id, gen_type(type), parent, qualify_parent);
245259
}
260+
// Shared body behind get_native_type / get_qualified_native_type. When
261+
// qualify_parent is set, a field-level @cpp.Adapter's enclosing-struct param
262+
// is namespace-qualified.
263+
std::string gen_field_native_type(
264+
const t_field& field, const t_structured& parent, bool qualify_parent);
246265
std::string gen_standard_type(const t_type& node);
247266
std::string gen_standard_type(const t_type& node, type_resolve_fn resolve_fn);
248267
std::string gen_standard_type(const t_field& node);
@@ -254,11 +273,18 @@ class cpp_name_resolver {
254273
const std::string* templte = nullptr);
255274
static std::string gen_adapted_type(
256275
const std::string* adapter, const std::string& standard_type);
257-
static std::string gen_adapted_type(
276+
277+
// Generates the storage type for a field-level @cpp.Adapter
278+
// (adapted_field_t<Adapter, FieldId, ThriftType, EnclosingStruct>). The
279+
// enclosing-struct parameter is unqualified by default (readable where the
280+
// type is emitted inside the struct's own namespace); set qualify_parent to
281+
// namespace-qualify it for emission elsewhere.
282+
std::string gen_adapted_type(
258283
const std::string* adapter,
259284
int16_t field_id,
260285
const std::string& standard_type,
261-
const t_structured& parent);
286+
const t_structured& parent,
287+
bool qualify_parent = false);
262288

263289
std::string gen_thrift_type_tag(const t_type&);
264290
// TODO(dokwon): Remove ignored_cpp_type once cpp.type lowering migration is

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,6 +1741,15 @@ class t_mstch_cpp2_generator : public t_whisker_generator {
17411741
assert(parent != nullptr);
17421742
return cpp_context_->resolver().get_storage_type(field, *parent);
17431743
});
1744+
// Storage type for frozen2 codegen (*_layouts.h): namespace-qualifies a
1745+
// field-level @cpp.Adapter's enclosing-struct parameter so it resolves
1746+
// under namespace apache::thrift::frozen.
1747+
def.property("cpp_frozen2_storage_type", [this](const t_field& field) {
1748+
const t_structured* parent = context().get_field_parent(&field);
1749+
assert(parent != nullptr);
1750+
return cpp_context_->resolver().get_qualified_storage_type(
1751+
field, *parent);
1752+
});
17441753
def.property("cpp_standard_type", [this](const t_field& field) {
17451754
return cpp_context_->resolver().get_standard_type(field);
17461755
});

third-party/thrift/src/thrift/compiler/generate/templates/cpp2/module_layouts.h.whisker

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ FROZEN_EXCLUDE_TYPE({{def.cpp_fullname}})
3737
{{#if (not def.union?)}}
3838
FROZEN_TYPE({{def.cpp_fullname}},{{!
3939
}}{{#each def.fields as |field|}}
40-
FROZEN_FIELD{{#partial module_layouts.field_suffix field=field}}({{field.cpp_name}}, {{field.id}}, {{field.cpp_storage_type}}){{!
40+
FROZEN_FIELD{{#partial module_layouts.field_suffix field=field}}({{field.cpp_name}}, {{field.id}}, {{field.cpp_frozen2_storage_type}}){{!
4141
}}{{/each}}
4242
FROZEN_VIEW({{!
4343
}}{{#each def.fields as |field|}}
44-
FROZEN_VIEW_FIELD{{#partial module_layouts.field_suffix field=field}}({{field.cpp_name}}, {{field.cpp_storage_type}}){{!
44+
FROZEN_VIEW_FIELD{{#partial module_layouts.field_suffix field=field}}({{field.cpp_name}}, {{field.cpp_frozen2_storage_type}}){{!
4545
}}{{/each}})
4646
FROZEN_SAVE_INLINE({{!
4747
}}{{#each def.fields as |field|}}
@@ -56,7 +56,7 @@ FROZEN_TYPE({{def.cpp_fullname}},{{!
5656
}}
5757
FROZEN_FIELD(fbthrift_type_, 0, {{def.cpp_fullname}}::Type){{!
5858
}}{{#each def.fields as |field|}}
59-
FROZEN_FIELD{{#partial module_layouts.frozen2_union_field_suffix field=field}}({{field.cpp_name}}, {{field.id}}, {{field.cpp_storage_type}}){{!
59+
FROZEN_FIELD{{#partial module_layouts.frozen2_union_field_suffix field=field}}({{field.cpp_name}}, {{field.id}}, {{field.cpp_frozen2_storage_type}}){{!
6060
}}{{/each}}
6161
struct View : public ViewBase<View, LayoutSelf, {{def.cpp_fullname}}> {
6262
{{def.cpp_fullname}}::Type fbthrift_type_;

third-party/thrift/src/thrift/compiler/test/cpp_name_resolver_test.cc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ class CppNameResolverTest : public ::testing::Test {
8383
return resolver_.get_storage_type(node, struct_);
8484
}
8585

86+
const std::string& get_qualified_storage_type(const t_field& node) {
87+
return resolver_.get_qualified_storage_type(node, struct_);
88+
}
89+
8690
const std::string& get_reference_type(const t_field& node) {
8791
return resolver_.get_reference_type(node);
8892
}
@@ -572,6 +576,40 @@ TEST_F(CppNameResolverTest, adapted_field_type) {
572576
"MyAdapter, 42, ::std::int64_t, ThriftStruct>");
573577
}
574578

579+
TEST_F(CppNameResolverTest, adapted_field_qualified_storage_type) {
580+
// get_qualified_storage_type namespace-qualifies the enclosing-struct
581+
// parameter so the storage type is valid when emitted outside the struct's
582+
// own namespace, while the default (get_storage_type) keeps the readable
583+
// unqualified name.
584+
const auto& i64 = t_primitive_type::t_i64();
585+
auto cpp_ref = gen::cpp_ref_builder(program_);
586+
auto adapter = gen::adapter_builder(program_, "cpp");
587+
588+
auto field = t_field(i64, "n", 42);
589+
field.add_structured_annotation(adapter.make("MyAdapter"));
590+
EXPECT_EQ(
591+
get_storage_type(field),
592+
"::apache::thrift::adapt_detail::adapted_field_t<"
593+
"MyAdapter, 42, ::std::int64_t, ThriftStruct>");
594+
EXPECT_EQ(
595+
get_qualified_storage_type(field),
596+
"::apache::thrift::adapt_detail::adapted_field_t<"
597+
"MyAdapter, 42, ::std::int64_t, ::path::to::ThriftStruct>");
598+
599+
// Reference wrapping is applied around the qualified adapted type too.
600+
auto unique_field = t_field(i64, "n", 42);
601+
unique_field.add_structured_annotation(adapter.make("MyAdapter"));
602+
unique_field.add_structured_annotation(cpp_ref.unique());
603+
EXPECT_EQ(
604+
get_qualified_storage_type(unique_field),
605+
"::std::unique_ptr<::apache::thrift::adapt_detail::adapted_field_t<"
606+
"MyAdapter, 42, ::std::int64_t, ::path::to::ThriftStruct>>");
607+
608+
// A field with no adapter resolves identically to get_storage_type.
609+
auto plain_field = t_field(i64, "n", 42);
610+
EXPECT_EQ(get_qualified_storage_type(plain_field), "::std::int64_t");
611+
}
612+
575613
TEST_F(CppNameResolverTest, adapted_field_storage_type) {
576614
const auto& i64 = t_primitive_type::t_i64();
577615
auto cpp_ref = gen::cpp_ref_builder(program_);
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package "facebook.com/thrift/test/frozen/adapter"
18+
19+
include "thrift/annotation/cpp.thrift"
20+
21+
cpp_include "thrift/lib/cpp2/frozen/test/FrozenAdapterTestUtil.h"
22+
23+
// Field-level @cpp.Adapter storage types name their enclosing struct, so the
24+
// layouts header (emitted under namespace apache::thrift::frozen) can only
25+
// compile these structs if that name is fully qualified.
26+
struct AdaptedNested {
27+
@cpp.Adapter{name = "::apache::thrift::test::PackedIntListAdapter"}
28+
1: binary ids;
29+
}
30+
31+
struct AdaptedFields {
32+
@cpp.Adapter{name = "::apache::thrift::test::PackedIntListAdapter"}
33+
1: binary ids;
34+
35+
@cpp.Adapter{name = "::apache::thrift::test::WidenToI64FieldAdapter"}
36+
2: i32 widened;
37+
38+
@cpp.Adapter{name = "::apache::thrift::test::PackedIntListAdapter"}
39+
3: optional binary optionalIds;
40+
41+
4: AdaptedNested nested;
42+
43+
5: list<AdaptedNested> nestedList;
44+
45+
6: string plain;
46+
}

0 commit comments

Comments
 (0)