Skip to content

Commit ca3f95f

Browse files
authored
Make named constraint eval to a FacetType with itself in it (#6308)
This requires declared FacetTypes to hold NamedConstraintIds (along with a specific) that are named in an extend or impls requirement. We add support to stringify and formatter to display the named constraints in the facet type, and special case when a facet type contains a single extend named constraint, like we did for a single extend interface. This means that `RequireIndentifiedFacetType` can now fail, if the facet type contains a forward-declared named constraint. Add the appropriate diagnostics for each call to this function, and note the ones that should change to `RequireCompleteFacetType` in the future with TODOs. We also add tests for using facet types that can or can't be identified, or completed, with named constraints in them.
1 parent ed31a6d commit ca3f95f

27 files changed

+1011
-432
lines changed

toolchain/check/eval.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -623,19 +623,36 @@ static auto GetConstantFacetTypeInfo(EvalContext& eval_context,
623623
.other_requirements = orig.other_requirements};
624624

625625
info.extend_constraints.reserve(orig.extend_constraints.size());
626-
for (const auto& interface : orig.extend_constraints) {
626+
for (const auto& extend : orig.extend_constraints) {
627627
info.extend_constraints.push_back(
628-
{.interface_id = interface.interface_id,
628+
{.interface_id = extend.interface_id,
629629
.specific_id =
630-
GetConstantValue(eval_context, interface.specific_id, phase)});
630+
GetConstantValue(eval_context, extend.specific_id, phase)});
631631
}
632632

633633
info.self_impls_constraints.reserve(orig.self_impls_constraints.size());
634-
for (const auto& interface : orig.self_impls_constraints) {
634+
for (const auto& self_impls : orig.self_impls_constraints) {
635635
info.self_impls_constraints.push_back(
636-
{.interface_id = interface.interface_id,
636+
{.interface_id = self_impls.interface_id,
637637
.specific_id =
638-
GetConstantValue(eval_context, interface.specific_id, phase)});
638+
GetConstantValue(eval_context, self_impls.specific_id, phase)});
639+
}
640+
641+
info.extend_named_constraints.reserve(orig.extend_named_constraints.size());
642+
for (const auto& extend : orig.extend_named_constraints) {
643+
info.extend_named_constraints.push_back(
644+
{.named_constraint_id = extend.named_constraint_id,
645+
.specific_id =
646+
GetConstantValue(eval_context, extend.specific_id, phase)});
647+
}
648+
649+
info.self_impls_named_constraints.reserve(
650+
orig.self_impls_named_constraints.size());
651+
for (const auto& self_impls : orig.self_impls_named_constraints) {
652+
info.self_impls_named_constraints.push_back(
653+
{.named_constraint_id = self_impls.named_constraint_id,
654+
.specific_id =
655+
GetConstantValue(eval_context, self_impls.specific_id, phase)});
639656
}
640657

641658
// Rewrite constraints are resolved first before replacing them with their

toolchain/check/eval_inst.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,8 @@ auto EvalConstantInst(Context& context, SemIR::InterfaceDecl inst)
448448
.type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty});
449449
}
450450

451-
// A non-parameterized interface declaration evaluates to a facet type.
451+
// A non-parameterized interface declaration evaluates to a declared facet
452+
// type containing just the interface.
452453
return ConstantEvalResult::NewAnyPhase(FacetTypeFromInterface(
453454
context, inst.interface_id,
454455
context.generics().GetSelfSpecific(interface_info.generic_id)));
@@ -467,7 +468,8 @@ auto EvalConstantInst(Context& context, SemIR::NamedConstraintDecl inst)
467468
.type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty});
468469
}
469470

470-
// A non-parameterized named constraint declaration evaluates to a facet type.
471+
// A non-parameterized named constraint declaration evaluates to a declared
472+
// facet type containing just the named constraint.
471473
return ConstantEvalResult::NewAnyPhase(FacetTypeFromNamedConstraint(
472474
context, inst.named_constraint_id,
473475
context.generics().GetSelfSpecific(named_constraint_info.generic_id)));

toolchain/check/facet_type.cpp

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,18 @@ namespace Carbon::Check {
2626
auto FacetTypeFromInterface(Context& context, SemIR::InterfaceId interface_id,
2727
SemIR::SpecificId specific_id) -> SemIR::FacetType {
2828
auto info = SemIR::FacetTypeInfo{};
29-
3029
info.extend_constraints.push_back({interface_id, specific_id});
31-
// TODO: Add `require impls` to the set of constraints.
32-
3330
info.Canonicalize();
3431
SemIR::FacetTypeId facet_type_id = context.facet_types().Add(info);
3532
return {.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id};
3633
}
3734

38-
auto FacetTypeFromNamedConstraint(
39-
Context& context, SemIR::NamedConstraintId /*named_constraint_id*/,
40-
SemIR::SpecificId /*specific_id*/) -> SemIR::FacetType {
35+
auto FacetTypeFromNamedConstraint(Context& context,
36+
SemIR::NamedConstraintId named_constraint_id,
37+
SemIR::SpecificId specific_id)
38+
-> SemIR::FacetType {
4139
auto info = SemIR::FacetTypeInfo{};
42-
43-
// TODO: Add `require impls` to the set of constraints.
44-
40+
info.extend_named_constraints.push_back({named_constraint_id, specific_id});
4541
info.Canonicalize();
4642
SemIR::FacetTypeId facet_type_id = context.facet_types().Add(info);
4743
return {.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id};

toolchain/check/handle_require.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,19 @@ auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool {
167167
}
168168

169169
auto identified_facet_type_id =
170-
RequireIdentifiedFacetType(context, *constraint_facet_type);
170+
RequireIdentifiedFacetType(context, *constraint_facet_type, [&] {
171+
CARBON_DIAGNOSTIC(
172+
RequireImplsUnidentifiedFacetType, Error,
173+
"facet type {0} cannot be identified in `require` declaration",
174+
InstIdAsType);
175+
return context.emitter().Build(constraint_node_id,
176+
RequireImplsUnidentifiedFacetType,
177+
constraint_inst_id);
178+
});
179+
if (!identified_facet_type_id.has_value()) {
180+
// The constraint can't be used.
181+
return true;
182+
}
171183
const auto& identified =
172184
context.identified_facet_types().Get(identified_facet_type_id);
173185

toolchain/check/impl.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,16 @@ auto CheckConstraintIsInterface(Context& context, SemIR::InstId impl_decl_id,
296296
return SemIR::SpecificInterface::None;
297297
}
298298

299-
auto identified_id = RequireIdentifiedFacetType(context, *facet_type);
299+
auto identified_id = RequireIdentifiedFacetType(context, *facet_type, [&] {
300+
CARBON_DIAGNOSTIC(ImplOfUnidentifiedFacetType, Error,
301+
"facet type {0} cannot be identified in `impl as`",
302+
InstIdAsType);
303+
return context.emitter().Build(impl_decl_id, ImplOfUnidentifiedFacetType,
304+
constraint_id);
305+
});
306+
if (!identified_id.has_value()) {
307+
return SemIR::SpecificInterface::None;
308+
}
300309
const auto& identified = context.identified_facet_types().Get(identified_id);
301310
if (!identified.is_valid_impl_as_target()) {
302311
CARBON_DIAGNOSTIC(ImplOfNotOneInterface, Error,

toolchain/check/impl_lookup.cpp

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -184,31 +184,39 @@ static auto FindAndDiagnoseImplLookupCycle(
184184
}
185185

186186
struct InterfacesFromConstantId {
187-
llvm::SmallVector<SemIR::SpecificInterface> interfaces;
187+
llvm::ArrayRef<SemIR::SpecificInterface> interfaces;
188188
SemIR::BuiltinConstraintMask builtin_constraint_mask;
189189
bool other_requirements;
190190
};
191191

192192
// Gets the set of `SpecificInterface`s that are required by a facet type
193193
// (as a constant value), and any special requirements.
194194
static auto GetInterfacesFromConstantId(
195-
Context& context, SemIR::ConstantId query_facet_type_const_id)
196-
-> InterfacesFromConstantId {
195+
Context& context, SemIR::LocId loc_id,
196+
SemIR::ConstantId query_facet_type_const_id)
197+
-> std::optional<InterfacesFromConstantId> {
197198
auto facet_type_inst_id =
198199
context.constant_values().GetInstId(query_facet_type_const_id);
199200
auto facet_type_inst =
200201
context.insts().GetAs<SemIR::FacetType>(facet_type_inst_id);
201202
const auto& facet_type_info =
202203
context.facet_types().Get(facet_type_inst.facet_type_id);
203-
auto identified_id = RequireIdentifiedFacetType(context, facet_type_inst);
204-
auto interfaces_array_ref =
205-
context.identified_facet_types().Get(identified_id).required_interfaces();
206-
// Returns a copy to avoid use-after-free when the identified_facet_types
207-
// store resizes.
208-
return {
209-
.interfaces = {interfaces_array_ref.begin(), interfaces_array_ref.end()},
210-
.builtin_constraint_mask = facet_type_info.builtin_constraint_mask,
211-
.other_requirements = facet_type_info.other_requirements};
204+
// TODO: Get the complete facet type here.
205+
auto identified_id =
206+
RequireIdentifiedFacetType(context, facet_type_inst, [&] {
207+
CARBON_DIAGNOSTIC(ImplLookupInIncompleteFacetType, Error,
208+
"facet type {0} is incomplete", InstIdAsType);
209+
return context.emitter().Build(loc_id, ImplLookupInIncompleteFacetType,
210+
facet_type_inst_id);
211+
});
212+
if (!identified_id.has_value()) {
213+
return std::nullopt;
214+
}
215+
return {{.interfaces = context.identified_facet_types()
216+
.Get(identified_id)
217+
.required_interfaces(),
218+
.builtin_constraint_mask = facet_type_info.builtin_constraint_mask,
219+
.other_requirements = facet_type_info.other_requirements}};
212220
}
213221

214222
static auto GetWitnessIdForImpl(Context& context, SemIR::LocId loc_id,
@@ -313,8 +321,18 @@ static auto LookupImplWitnessInSelfFacetValue(
313321
}
314322

315323
// The position of the interface in `required_interfaces()` is also the
316-
// position of the witness for that interface in `FacetValue`.
317-
auto identified_id = RequireIdentifiedFacetType(context, *facet_type);
324+
// position of the witness for that interface in `FacetValue`. The
325+
// `FacetValue` witnesses are the output of an impl lookup, which finds and
326+
// returns witnesses in the same order.
327+
//
328+
// TODO: Get the complete facet type here.
329+
auto identified_id =
330+
RequireIdentifiedFacetType(context, *facet_type, nullptr);
331+
// This should not be possible as FacetValue is constructed by a conversion
332+
// to a facet type, which performs impl lookup for that facet type, and
333+
// lookup only succeeds for complete facet types.
334+
CARBON_CHECK(identified_id.has_value(),
335+
"FacetValue was constructed with an incomplete facet type");
318336
auto facet_type_required_interfaces =
319337
llvm::enumerate(context.identified_facet_types()
320338
.Get(identified_id)
@@ -589,8 +607,13 @@ auto LookupImplWitness(Context& context, SemIR::LocId loc_id,
589607
context.constant_values().GetInstId(query_facet_type_const_id)));
590608
}
591609

610+
auto interfaces_from_constant_id =
611+
GetInterfacesFromConstantId(context, loc_id, query_facet_type_const_id);
612+
if (!interfaces_from_constant_id) {
613+
return SemIR::InstBlockIdOrError::MakeError();
614+
}
592615
auto [interfaces, builtin_constraint_mask, other_requirements] =
593-
GetInterfacesFromConstantId(context, query_facet_type_const_id);
616+
*interfaces_from_constant_id;
594617
if (other_requirements) {
595618
// TODO: Remove this when other requirements go away.
596619
return SemIR::InstBlockId::None;

toolchain/check/testdata/facet/fail_incomplete.carbon

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
// TIP: To dump output, run:
1111
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/fail_incomplete.carbon
1212

13+
// --- fail_incomplete_interface.carbon
14+
library "[[@TEST_NAME]]";
15+
1316
interface A;
1417
interface B {}
1518
class C {}
@@ -18,31 +21,85 @@ fn G[T:! A](t: T) {}
1821
fn H[T:! A & B](t: T) {}
1922

2023
fn F() {
21-
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+4]]:17: error: cannot convert type `C` into type implementing `A` [ConversionFailureTypeToFacet]
22-
// CHECK:STDERR: ({} as C) as (C as A);
23-
// CHECK:STDERR: ^~~~~~
24+
// CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+4]]:3: error: cannot convert type `C` into type implementing `A` [ConversionFailureTypeToFacet]
25+
// CHECK:STDERR: C as A;
26+
// CHECK:STDERR: ^~~~~~
27+
// CHECK:STDERR:
28+
C as A;
29+
30+
// CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+4]]:3: error: cannot convert type `C` into type implementing `A & B` [ConversionFailureTypeToFacet]
31+
// CHECK:STDERR: C as (A & B);
32+
// CHECK:STDERR: ^~~~~~~~~~~~
33+
// CHECK:STDERR:
34+
C as (A & B);
35+
36+
// CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+7]]:3: error: cannot convert type `C` into type implementing `A` [ConversionFailureTypeToFacet]
37+
// CHECK:STDERR: G({} as C);
38+
// CHECK:STDERR: ^~~~~~~~~~
39+
// CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE-19]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
40+
// CHECK:STDERR: fn G[T:! A](t: T) {}
41+
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~
42+
// CHECK:STDERR:
43+
G({} as C);
44+
45+
// CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE+7]]:3: error: cannot convert type `C` into type implementing `A & B` [ConversionFailureTypeToFacet]
46+
// CHECK:STDERR: H({} as C);
47+
// CHECK:STDERR: ^~~~~~~~~~
48+
// CHECK:STDERR: fail_incomplete_interface.carbon:[[@LINE-27]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
49+
// CHECK:STDERR: fn H[T:! A & B](t: T) {}
50+
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~
51+
// CHECK:STDERR:
52+
H({} as C);
53+
}
54+
55+
// --- fail_incomplete_constraint.carbon
56+
library "[[@TEST_NAME]]";
57+
58+
constraint A;
59+
interface B {}
60+
class C {}
61+
62+
fn G[T:! A](t: T) {}
63+
fn H[T:! A & B](t: T) {}
64+
65+
fn F() {
66+
// CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+7]]:3: error: facet type `A` is incomplete [ImplLookupInIncompleteFacetType]
67+
// CHECK:STDERR: C as A;
68+
// CHECK:STDERR: ^~~~~~
69+
// CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-11]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere]
70+
// CHECK:STDERR: constraint A;
71+
// CHECK:STDERR: ^~~~~~~~~~~~~
2472
// CHECK:STDERR:
25-
({} as C) as (C as A);
73+
C as A;
2674

27-
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+4]]:17: error: cannot convert type `C` into type implementing `A & B` [ConversionFailureTypeToFacet]
28-
// CHECK:STDERR: ({} as C) as (C as (A & B));
29-
// CHECK:STDERR: ^~~~~~~~~~~~
75+
// CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+7]]:3: error: facet type `B & A` is incomplete [ImplLookupInIncompleteFacetType]
76+
// CHECK:STDERR: C as (A & B);
77+
// CHECK:STDERR: ^~~~~~~~~~~~
78+
// CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-20]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere]
79+
// CHECK:STDERR: constraint A;
80+
// CHECK:STDERR: ^~~~~~~~~~~~~
3081
// CHECK:STDERR:
31-
({} as C) as (C as (A & B));
82+
C as (A & B);
3283

33-
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+7]]:3: error: cannot convert type `C` into type implementing `A` [ConversionFailureTypeToFacet]
84+
// CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+10]]:3: error: facet type `A` is incomplete [ImplLookupInIncompleteFacetType]
3485
// CHECK:STDERR: G({} as C);
3586
// CHECK:STDERR: ^~~~~~~~~~
36-
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-19]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
87+
// CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-29]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere]
88+
// CHECK:STDERR: constraint A;
89+
// CHECK:STDERR: ^~~~~~~~~~~~~
90+
// CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-28]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
3791
// CHECK:STDERR: fn G[T:! A](t: T) {}
3892
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~
3993
// CHECK:STDERR:
4094
G({} as C);
4195

42-
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE+7]]:3: error: cannot convert type `C` into type implementing `A & B` [ConversionFailureTypeToFacet]
96+
// CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE+10]]:3: error: facet type `B & A` is incomplete [ImplLookupInIncompleteFacetType]
4397
// CHECK:STDERR: H({} as C);
4498
// CHECK:STDERR: ^~~~~~~~~~
45-
// CHECK:STDERR: fail_incomplete.carbon:[[@LINE-27]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
99+
// CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-41]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere]
100+
// CHECK:STDERR: constraint A;
101+
// CHECK:STDERR: ^~~~~~~~~~~~~
102+
// CHECK:STDERR: fail_incomplete_constraint.carbon:[[@LINE-39]]:1: note: while deducing parameters of generic declared here [DeductionGenericHere]
46103
// CHECK:STDERR: fn H[T:! A & B](t: T) {}
47104
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~
48105
// CHECK:STDERR:

0 commit comments

Comments
 (0)