Skip to content

Commit fd64295

Browse files
committed
recurse-on-facets
1 parent 5087965 commit fd64295

File tree

3 files changed

+137
-28
lines changed

3 files changed

+137
-28
lines changed

toolchain/check/testdata/generic/extend_type_completion.carbon

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,25 @@ constraint J(N:! i32) {
103103
// CHECK:STDERR: ^~~~~~~~~~~~
104104
// CHECK:STDERR:
105105
var v: J(-1);
106+
107+
// --- interface_require_impls_doesnt_need_complete_self.carbon
108+
library "[[@TEST_NAME]]";
109+
110+
interface K {}
111+
interface J(N:! i32) {
112+
require array(Self, N) impls K;
113+
}
114+
115+
// J does not extend K so the type of Self impling K is not completed. No error.
116+
var v: J(-1);
117+
118+
// --- constraint_require_impls_doesnt_need_complete_self.carbon
119+
library "[[@TEST_NAME]]";
120+
121+
interface K {}
122+
constraint J(N:! i32) {
123+
require array(Self, N) impls K;
124+
}
125+
126+
// J does not extend K so the type of Self impling K is not completed. No error.
127+
var v: J(-1);

toolchain/check/type_completion.cpp

Lines changed: 113 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -72,45 +72,130 @@ static auto NoteIncompleteNamedConstraint(
7272
}
7373
}
7474

75+
// Makes a copy of a Specific from one generic to apply to another given
76+
// `generic_id`, with a given `Self` argument appended to the end.
77+
static auto MakeCopyOfSpecificAndAppendSelf(
78+
Context& context, SemIR::LocId loc_id, SemIR::SpecificId specific_id,
79+
SemIR::GenericId generic_id, SemIR::InstId self_id) -> SemIR::SpecificId {
80+
auto source_specific_args_id =
81+
context.specifics().GetArgsOrEmpty(specific_id);
82+
auto source_specific_args =
83+
context.inst_blocks().Get(source_specific_args_id);
84+
85+
llvm::SmallVector<SemIR::InstId> arg_ids;
86+
arg_ids.reserve(source_specific_args.size() + 1);
87+
// Start with the enclosing arguments from the source Specific.
88+
llvm::append_range(arg_ids, source_specific_args);
89+
// Add the new `Self` argument.
90+
arg_ids.push_back(self_id);
91+
92+
return MakeSpecific(context, loc_id, generic_id, arg_ids);
93+
}
94+
95+
static auto GetRequireImplsSpecificSelf(Context& context,
96+
const SemIR::RequireImpls& require)
97+
-> SemIR::InstId {
98+
const auto& require_generic = context.generics().Get(require.generic_id);
99+
const auto& require_self_specific =
100+
context.specifics().Get(require_generic.self_specific_id);
101+
auto require_self_specific_args =
102+
context.inst_blocks().Get(require_self_specific.args_id);
103+
// The last argument of a `require` generic is always `Self`, as require can
104+
// not have any parameters of its own, only enclosing parameters.
105+
return require_self_specific_args.back();
106+
}
107+
108+
static auto GetFacetTypeInSpecific(Context& context,
109+
SemIR::SpecificId specific_id,
110+
SemIR::InstId facet_type)
111+
-> SemIR::FacetTypeId {
112+
auto const_facet_type = SemIR::GetConstantValueInSpecific(
113+
context.sem_ir(), specific_id, facet_type);
114+
if (const_facet_type == SemIR::ErrorInst::ConstantId) {
115+
return SemIR::FacetTypeId::None;
116+
}
117+
auto facet_type_in_specific = context.insts().GetAs<SemIR::FacetType>(
118+
context.constant_values().GetInstId(const_facet_type));
119+
return facet_type_in_specific.facet_type_id;
120+
}
121+
75122
static auto RequireCompleteFacetType(Context& context, SemIR::LocId loc_id,
76123
const SemIR::FacetType& facet_type,
77124
MakeDiagnosticBuilderFn diagnoser)
78125
-> bool {
79-
const auto& facet_type_info =
80-
context.facet_types().Get(facet_type.facet_type_id);
126+
llvm::SmallVector<SemIR::FacetTypeId> work = {facet_type.facet_type_id};
127+
while (!work.empty()) {
128+
auto next_facet_type_id = work.pop_back_val();
129+
const auto& facet_type_info = context.facet_types().Get(next_facet_type_id);
81130

82-
for (auto extends : facet_type_info.extend_constraints) {
83-
auto interface_id = extends.interface_id;
84-
const auto& interface = context.interfaces().Get(interface_id);
85-
if (!interface.is_complete()) {
86-
if (diagnoser) {
87-
auto builder = diagnoser();
88-
NoteIncompleteInterface(context, interface_id, builder);
89-
builder.Emit();
131+
struct SpecificForRequires {
132+
SemIR::SpecificId specific_id;
133+
SemIR::RequireImplsBlockId requires_block_id;
134+
};
135+
llvm::SmallVector<SpecificForRequires> specifics;
136+
137+
for (auto extends : facet_type_info.extend_constraints) {
138+
auto interface_id = extends.interface_id;
139+
const auto& interface = context.interfaces().Get(interface_id);
140+
if (!interface.is_complete()) {
141+
if (diagnoser) {
142+
auto builder = diagnoser();
143+
NoteIncompleteInterface(context, interface_id, builder);
144+
builder.Emit();
145+
}
146+
return false;
147+
}
148+
if (interface.generic_id.has_value()) {
149+
specifics.push_back(
150+
{extends.specific_id, interface.require_impls_block_id});
151+
}
152+
if (extends.specific_id.has_value()) {
153+
ResolveSpecificDefinition(context, loc_id, extends.specific_id);
90154
}
91-
return false;
92-
}
93-
94-
if (extends.specific_id.has_value()) {
95-
ResolveSpecificDefinition(context, loc_id, extends.specific_id);
96155
}
97-
}
98156

99-
for (auto extends : facet_type_info.extend_named_constraints) {
100-
auto named_constraint_id = extends.named_constraint_id;
101-
const auto& constraint =
102-
context.named_constraints().Get(named_constraint_id);
103-
if (!constraint.is_complete()) {
104-
if (diagnoser) {
105-
auto builder = diagnoser();
106-
NoteIncompleteNamedConstraint(context, named_constraint_id, builder);
107-
builder.Emit();
157+
for (auto extends : facet_type_info.extend_named_constraints) {
158+
auto named_constraint_id = extends.named_constraint_id;
159+
const auto& constraint =
160+
context.named_constraints().Get(named_constraint_id);
161+
if (!constraint.is_complete()) {
162+
if (diagnoser) {
163+
auto builder = diagnoser();
164+
NoteIncompleteNamedConstraint(context, named_constraint_id, builder);
165+
builder.Emit();
166+
}
167+
return false;
168+
}
169+
if (constraint.generic_id.has_value()) {
170+
specifics.push_back(
171+
{extends.specific_id, constraint.require_impls_block_id});
172+
}
173+
if (extends.specific_id.has_value()) {
174+
ResolveSpecificDefinition(context, loc_id, extends.specific_id);
108175
}
109-
return false;
110176
}
111177

112-
if (extends.specific_id.has_value()) {
113-
ResolveSpecificDefinition(context, loc_id, extends.specific_id);
178+
// Formulate a specific for each `require` declaration, as the specific may
179+
// introduce errors. Then recurse on those specific facet types to find
180+
// other interfaces/constraints that may contain other require declarations.
181+
for (auto [specific_id, requires_block_id] : specifics) {
182+
for (auto require_impls_id :
183+
context.require_impls_blocks().Get(requires_block_id)) {
184+
const auto& require = context.require_impls().Get(require_impls_id);
185+
if (require.extend_self) {
186+
auto require_specific_id = MakeCopyOfSpecificAndAppendSelf(
187+
context, loc_id, specific_id, require.generic_id,
188+
GetRequireImplsSpecificSelf(context, require));
189+
// Construct the specific facet type. If None is returned, it means an
190+
// error was diagnosed.
191+
auto facet_type_id = GetFacetTypeInSpecific(
192+
context, require_specific_id, require.facet_type_inst_id);
193+
if (!facet_type_id.has_value()) {
194+
return false;
195+
}
196+
work.push_back(facet_type_id);
197+
}
198+
}
114199
}
115200
}
116201

toolchain/sem_ir/require_impls.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ struct RequireImpls : Printable<RequireImpls> {
2323
// Evaluates to the `FacetType` that the self-type must implement.
2424
TypeInstId facet_type_inst_id;
2525
// The `FacetTypeInfo` derived from the `facet_type_inst_id` instruction.
26+
// TODO: Remove this, we need to use the inst to get a constant value in the
27+
// appropriate specific.
2628
FacetTypeId facet_type_id;
2729
// If the facet type extends `Self`. When true, the `self_id` will be `Self`.
2830
bool extend_self;

0 commit comments

Comments
 (0)