Skip to content

Commit 22580a4

Browse files
authored
Initial support for empty named constraints (#6245)
Type check named constraint decls and definitions. We don't correctly error if you put a `fn` inside them. There is no support for `require` or `alias` yet, so there's nothing useful you can do with them yet. We have attempted to share code between `interface` and `constraint` as they are quite similar. First by splitting out some of handle_interface.cpp to a separate file. Second by sharing some code paths when you want a facet type from either one, as they both turn into a facet type.
1 parent a340808 commit 22580a4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1770
-141
lines changed

toolchain/check/call.cpp

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -116,24 +116,42 @@ static auto PerformCallToGenericClass(Context& context, SemIR::LocId loc_id,
116116
.specific_id = *callee_specific_id});
117117
}
118118

119-
// Performs a call where the callee is the name of a generic interface, such as
120-
// `AddWith(i32)`.
121-
static auto PerformCallToGenericInterface(
122-
Context& context, SemIR::LocId loc_id, SemIR::InterfaceId interface_id,
119+
static auto EntityFromInterfaceOrNamedConstraint(
120+
Context& context, SemIR::InterfaceId interface_id)
121+
-> const SemIR::EntityWithParamsBase& {
122+
return context.interfaces().Get(interface_id);
123+
}
124+
125+
static auto EntityFromInterfaceOrNamedConstraint(
126+
Context& context, SemIR::NamedConstraintId named_constraint_id)
127+
-> const SemIR::EntityWithParamsBase& {
128+
return context.named_constraints().Get(named_constraint_id);
129+
}
130+
131+
// Performs a call where the callee is the name of a generic interface or named
132+
// constraint, such as `AddWith(i32)`.
133+
template <typename IdT>
134+
requires SameAsOneOf<IdT, SemIR::InterfaceId, SemIR::NamedConstraintId>
135+
static auto PerformCallToGenericInterfaceOrNamedConstaint(
136+
Context& context, SemIR::LocId loc_id, IdT id,
123137
SemIR::SpecificId enclosing_specific_id,
124138
llvm::ArrayRef<SemIR::InstId> arg_ids) -> SemIR::InstId {
125-
const auto& interface = context.interfaces().Get(interface_id);
139+
const auto& entity = EntityFromInterfaceOrNamedConstraint(context, id);
126140
auto callee_specific_id =
127-
ResolveCalleeInCall(context, loc_id, interface,
128-
EntityKind::GenericInterface, enclosing_specific_id,
141+
ResolveCalleeInCall(context, loc_id, entity, EntityKind::GenericInterface,
142+
enclosing_specific_id,
129143
/*self_type_id=*/SemIR::InstId::None,
130144
/*self_id=*/SemIR::InstId::None, arg_ids);
131145
if (!callee_specific_id) {
132146
return SemIR::ErrorInst::InstId;
133147
}
134-
return GetOrAddInst(
135-
context, loc_id,
136-
FacetTypeFromInterface(context, interface_id, *callee_specific_id));
148+
std::optional<SemIR::FacetType> facet_type;
149+
if constexpr (std::same_as<IdT, SemIR::InterfaceId>) {
150+
facet_type = FacetTypeFromInterface(context, id, *callee_specific_id);
151+
} else {
152+
facet_type = FacetTypeFromNamedConstraint(context, id, *callee_specific_id);
153+
}
154+
return GetOrAddInst(context, loc_id, *facet_type);
137155
}
138156

139157
// Builds an appropriate specific function for the callee, also handling
@@ -307,10 +325,15 @@ static auto PerformCallToNonFunction(Context& context, SemIR::LocId loc_id,
307325
arg_ids);
308326
}
309327
case CARBON_KIND(SemIR::GenericInterfaceType generic_interface): {
310-
return PerformCallToGenericInterface(
328+
return PerformCallToGenericInterfaceOrNamedConstaint(
311329
context, loc_id, generic_interface.interface_id,
312330
generic_interface.enclosing_specific_id, arg_ids);
313331
}
332+
case CARBON_KIND(SemIR::GenericNamedConstraintType generic_constraint): {
333+
return PerformCallToGenericInterfaceOrNamedConstaint(
334+
context, loc_id, generic_constraint.named_constraint_id,
335+
generic_constraint.enclosing_specific_id, arg_ids);
336+
}
314337
default: {
315338
CARBON_DIAGNOSTIC(CallToNonCallable, Error,
316339
"value of type {0} is not callable", TypeOfInstId);

toolchain/check/context.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,9 @@ class Context {
265265
auto classes() -> SemIR::ClassStore& { return sem_ir().classes(); }
266266
auto vtables() -> SemIR::VtableStore& { return sem_ir().vtables(); }
267267
auto interfaces() -> SemIR::InterfaceStore& { return sem_ir().interfaces(); }
268+
auto named_constraints() -> SemIR::NamedConstraintStore& {
269+
return sem_ir().named_constraints();
270+
}
268271
auto associated_constants() -> SemIR::AssociatedConstantStore& {
269272
return sem_ir().associated_constants();
270273
}

toolchain/check/eval_inst.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,25 @@ auto EvalConstantInst(Context& context, SemIR::InterfaceDecl inst)
451451
context.generics().GetSelfSpecific(interface_info.generic_id)));
452452
}
453453

454+
auto EvalConstantInst(Context& context, SemIR::NamedConstraintDecl inst)
455+
-> ConstantEvalResult {
456+
const auto& named_constraint_info =
457+
context.named_constraints().Get(inst.named_constraint_id);
458+
459+
// If the named constraint has generic parameters, we don't produce a named
460+
// constraint type, but a callable whose return value is a named constraint
461+
// type.
462+
if (named_constraint_info.has_parameters()) {
463+
return ConstantEvalResult::NewSamePhase(SemIR::StructValue{
464+
.type_id = inst.type_id, .elements_id = SemIR::InstBlockId::Empty});
465+
}
466+
467+
// A non-parameterized named constraint declaration evaluates to a facet type.
468+
return ConstantEvalResult::NewAnyPhase(FacetTypeFromNamedConstraint(
469+
context, inst.named_constraint_id,
470+
context.generics().GetSelfSpecific(named_constraint_info.generic_id)));
471+
}
472+
454473
auto EvalConstantInst(Context& context, SemIR::NameRef inst)
455474
-> ConstantEvalResult {
456475
// A name reference evaluates to the value the name resolves to.

toolchain/check/facet_type.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "llvm/ADT/ArrayRef.h"
1010
#include "llvm/ADT/STLExtras.h"
11+
#include "toolchain/base/kind_switch.h"
1112
#include "toolchain/check/convert.h"
1213
#include "toolchain/check/diagnostic_helpers.h"
1314
#include "toolchain/check/generic.h"
@@ -24,8 +25,23 @@ namespace Carbon::Check {
2425

2526
auto FacetTypeFromInterface(Context& context, SemIR::InterfaceId interface_id,
2627
SemIR::SpecificId specific_id) -> SemIR::FacetType {
27-
auto info =
28-
SemIR::FacetTypeInfo{.extend_constraints = {{interface_id, specific_id}}};
28+
auto info = SemIR::FacetTypeInfo{};
29+
30+
info.extend_constraints.push_back({interface_id, specific_id});
31+
// TODO: Add `require impls` to the set of constraints.
32+
33+
info.Canonicalize();
34+
SemIR::FacetTypeId facet_type_id = context.facet_types().Add(info);
35+
return {.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id};
36+
}
37+
38+
auto FacetTypeFromNamedConstraint(
39+
Context& context, SemIR::NamedConstraintId /*named_constraint_id*/,
40+
SemIR::SpecificId /*specific_id*/) -> SemIR::FacetType {
41+
auto info = SemIR::FacetTypeInfo{};
42+
43+
// TODO: Add `require impls` to the set of constraints.
44+
2945
info.Canonicalize();
3046
SemIR::FacetTypeId facet_type_id = context.facet_types().Add(info);
3147
return {.type_id = SemIR::TypeType::TypeId, .facet_type_id = facet_type_id};

toolchain/check/facet_type.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,27 @@
88
#include <compare>
99

1010
#include "toolchain/check/context.h"
11+
#include "toolchain/sem_ir/entity_with_params_base.h"
1112
#include "toolchain/sem_ir/ids.h"
1213

1314
namespace Carbon::Check {
1415

15-
// Create a FacetType typed instruction object consisting of a single
16-
// interface. The `specific_id` specifies arguments in the case the interface is
17-
// generic.
16+
// Create a FacetType typed instruction object consisting of a interface. The
17+
// `specific_id` specifies arguments in the case the interface is generic.
18+
//
19+
// The resulting FacetType may contain multiple interfaces if the named
20+
// interface contains `require` declarations.
1821
auto FacetTypeFromInterface(Context& context, SemIR::InterfaceId interface_id,
1922
SemIR::SpecificId specific_id) -> SemIR::FacetType;
2023

24+
// Create a FacetType typed instruction object consisting of a named constraint.
25+
// The `specific_id` specifies arguments in the case the named constraint is
26+
// generic.
27+
auto FacetTypeFromNamedConstraint(Context& context,
28+
SemIR::NamedConstraintId named_constraint_id,
29+
SemIR::SpecificId specific_id)
30+
-> SemIR::FacetType;
31+
2132
// Given an ImplWitnessAccessSubstituted, returns the InstId of the
2233
// ImplWitnessAccess. Otherwise, returns the input `inst_id` unchanged.
2334
//

toolchain/check/generic.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ auto ResolveSpecificDefinition(Context& context, SemIR::LocId loc_id,
730730
}
731731

732732
auto DiagnoseIfGenericMissingExplicitParameters(
733-
Context& context, SemIR::EntityWithParamsBase& entity_base) -> void {
733+
Context& context, const SemIR::EntityWithParamsBase& entity_base) -> void {
734734
if (!entity_base.implicit_param_patterns_id.has_value() ||
735735
entity_base.param_patterns_id.has_value()) {
736736
return;

toolchain/check/generic.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ auto ResolveSpecificDefinition(Context& context, SemIR::LocId loc_id,
123123
// Diagnoses if an entity has implicit parameters, indicating it's generic, but
124124
// is missing explicit parameters.
125125
auto DiagnoseIfGenericMissingExplicitParameters(
126-
Context& context, SemIR::EntityWithParamsBase& entity_base) -> void;
126+
Context& context, const SemIR::EntityWithParamsBase& entity_base) -> void;
127127

128128
} // namespace Carbon::Check
129129

toolchain/check/handle_interface.cpp

Lines changed: 38 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@
1010
#include "toolchain/check/generic.h"
1111
#include "toolchain/check/handle.h"
1212
#include "toolchain/check/inst.h"
13+
#include "toolchain/check/interface.h"
1314
#include "toolchain/check/merge.h"
1415
#include "toolchain/check/modifiers.h"
1516
#include "toolchain/check/name_component.h"
1617
#include "toolchain/check/name_lookup.h"
1718
#include "toolchain/check/type.h"
19+
#include "toolchain/sem_ir/entity_with_params_base.h"
20+
#include "toolchain/sem_ir/ids.h"
21+
#include "toolchain/sem_ir/interface.h"
1822
#include "toolchain/sem_ir/typed_insts.h"
1923

2024
namespace Carbon::Check {
@@ -26,11 +30,12 @@ auto HandleParseNode(Context& context, Parse::InterfaceIntroducerId node_id)
2630
// Create an instruction block to hold the instructions created as part of the
2731
// interface signature, such as generic parameters.
2832
context.inst_block_stack().Push();
29-
// Push the bracketing node.
30-
context.node_stack().Push(node_id);
3133
// Optional modifiers and the name follow.
3234
context.decl_introducer_state_stack().Push<Lex::TokenKind::Interface>();
3335
context.decl_name_stack().PushScopeAndStartName();
36+
37+
// Push the bracketing node.
38+
context.node_stack().Push(node_id);
3439
return true;
3540
}
3641

@@ -56,87 +61,44 @@ static auto BuildInterfaceDecl(Context& context,
5661
// Add the interface declaration.
5762
auto interface_decl = SemIR::InterfaceDecl{
5863
SemIR::TypeType::TypeId, SemIR::InterfaceId::None, decl_block_id};
59-
auto interface_decl_id = AddPlaceholderInst(context, node_id, interface_decl);
64+
auto decl_inst_id = AddPlaceholderInst(context, node_id, interface_decl);
6065

6166
SemIR::Interface interface_info = {name_context.MakeEntityWithParamsBase(
62-
name, interface_decl_id, /*is_extern=*/false,
63-
SemIR::LibraryNameId::None)};
67+
name, decl_inst_id, /*is_extern=*/false, SemIR::LibraryNameId::None)};
6468

6569
DiagnoseIfGenericMissingExplicitParameters(context, interface_info);
6670

6771
// Check whether this is a redeclaration.
6872
SemIR::ScopeLookupResult lookup_result =
6973
context.decl_name_stack().LookupOrAddName(
70-
name_context, interface_decl_id,
71-
introducer.modifier_set.GetAccessKind());
72-
if (lookup_result.is_poisoned()) {
73-
// This is a declaration of a poisoned name.
74-
DiagnosePoisonedName(context, name_context.name_id_for_new_inst(),
75-
lookup_result.poisoning_loc_id(), name_context.loc_id);
76-
} else if (lookup_result.is_found()) {
77-
SemIR::InstId existing_id = lookup_result.target_inst_id();
78-
if (auto existing_interface_decl =
79-
context.insts().Get(existing_id).TryAs<SemIR::InterfaceDecl>()) {
80-
auto existing_interface =
81-
context.interfaces().Get(existing_interface_decl->interface_id);
82-
if (CheckRedeclParamsMatch(
83-
context,
84-
DeclParams(SemIR::LocId(interface_decl_id),
85-
name.first_param_node_id, name.last_param_node_id,
86-
name.implicit_param_patterns_id,
87-
name.param_patterns_id),
88-
DeclParams(existing_interface))) {
89-
// TODO: This should be refactored a little, particularly for
90-
// prev_import_ir_id. See similar logic for classes and functions, which
91-
// might also be refactored to merge.
92-
DiagnoseIfInvalidRedecl(
93-
context, Lex::TokenKind::Interface, existing_interface.name_id,
94-
RedeclInfo(interface_info, node_id, is_definition),
95-
RedeclInfo(existing_interface,
96-
SemIR::LocId(existing_interface.latest_decl_id()),
97-
existing_interface.has_definition_started()),
98-
/*prev_import_ir_id=*/SemIR::ImportIRId::None);
99-
100-
// Can't merge interface definitions due to the generic requirements.
101-
if (!is_definition || !existing_interface.has_definition_started()) {
102-
// This is a redeclaration of an existing interface.
103-
interface_decl.interface_id = existing_interface_decl->interface_id;
104-
interface_decl.type_id = existing_interface_decl->type_id;
105-
// TODO: If the new declaration is a definition, keep its parameter
106-
// and implicit parameter lists rather than the ones from the
107-
// previous declaration.
108-
}
109-
}
110-
} else {
111-
// This is a redeclaration of something other than a interface.
112-
DiagnoseDuplicateName(context, name_context.name_id, name_context.loc_id,
113-
SemIR::LocId(existing_id));
114-
}
115-
}
74+
name_context, decl_inst_id, introducer.modifier_set.GetAccessKind());
75+
if (auto existing_decl = TryGetExistingDecl(context, name, lookup_result,
76+
interface_info, is_definition)) {
77+
auto existing_interface_decl = existing_decl->As<SemIR::InterfaceDecl>();
78+
interface_decl.interface_id = existing_interface_decl.interface_id;
79+
interface_decl.type_id = existing_interface_decl.type_id;
80+
// TODO: If the new declaration is a definition, keep its parameter
81+
// and implicit parameter lists rather than the ones from the
82+
// previous declaration.
11683

117-
// Create a new interface if this isn't a valid redeclaration.
118-
if (!interface_decl.interface_id.has_value()) {
119-
// TODO: If this is an invalid redeclaration of a non-interface entity or
120-
// there was an error in the qualifier, we will have lost track of the
121-
// interface name here. We should keep track of it even if the name is
122-
// invalid.
123-
interface_info.generic_id = BuildGenericDecl(context, interface_decl_id);
84+
auto prev_decl_generic_id =
85+
context.interfaces().Get(interface_decl.interface_id).generic_id;
86+
FinishGenericRedecl(context, prev_decl_generic_id);
87+
} else {
88+
// Create a new interface if this isn't a valid redeclaration.
89+
interface_info.generic_id = BuildGenericDecl(context, decl_inst_id);
12490
interface_decl.interface_id = context.interfaces().Add(interface_info);
12591
if (interface_info.has_parameters()) {
12692
interface_decl.type_id =
12793
GetGenericInterfaceType(context, interface_decl.interface_id,
12894
context.scope_stack().PeekSpecificId());
12995
}
130-
} else {
131-
auto prev_decl_generic_id =
132-
context.interfaces().Get(interface_decl.interface_id).generic_id;
133-
FinishGenericRedecl(context, prev_decl_generic_id);
13496
}
13597

13698
// Write the interface ID into the InterfaceDecl.
137-
ReplaceInstBeforeConstantUse(context, interface_decl_id, interface_decl);
99+
ReplaceInstBeforeConstantUse(context, decl_inst_id, interface_decl);
138100

139-
return {interface_decl.interface_id, interface_decl_id};
101+
return {interface_decl.interface_id, decl_inst_id};
140102
}
141103

142104
auto HandleParseNode(Context& context, Parse::InterfaceDeclId node_id) -> bool {
@@ -147,16 +109,16 @@ auto HandleParseNode(Context& context, Parse::InterfaceDeclId node_id) -> bool {
147109

148110
auto HandleParseNode(Context& context,
149111
Parse::InterfaceDefinitionStartId node_id) -> bool {
150-
auto [interface_id, interface_decl_id] =
112+
auto [interface_id, decl_inst_id] =
151113
BuildInterfaceDecl(context, node_id, /*is_definition=*/true);
152114
auto& interface_info = context.interfaces().Get(interface_id);
153115

154116
// Track that this declaration is the definition.
155117
CARBON_CHECK(!interface_info.has_definition_started(),
156118
"Can't merge with defined interfaces.");
157-
interface_info.definition_id = interface_decl_id;
119+
interface_info.definition_id = decl_inst_id;
158120
interface_info.scope_id = context.name_scopes().Add(
159-
interface_decl_id, SemIR::NameId::None, interface_info.parent_scope_id);
121+
decl_inst_id, SemIR::NameId::None, interface_info.parent_scope_id);
160122
context.name_scopes()
161123
.Get(interface_info.scope_id)
162124
.set_is_interface_definition();
@@ -167,35 +129,21 @@ auto HandleParseNode(Context& context,
167129
StartGenericDefinition(context, interface_info.generic_id);
168130

169131
context.inst_block_stack().Push();
170-
context.node_stack().Push(node_id, interface_id);
171132

172133
// We use the arg stack to build the witness table type.
173134
context.args_type_info_stack().Push();
174135

175-
// Declare and introduce `Self`.
136+
// Declare and introduce `Self`. We model `Self` as a symbolic binding whose
137+
// type is the interface, excluding any other interfaces mentioned by
138+
// `require` declarations.
176139
SemIR::TypeId self_type_id =
177140
GetInterfaceType(context, interface_id, self_specific_id);
178-
179-
// We model `Self` as a symbolic binding whose type is the interface.
180-
// Because there is no equivalent non-symbolic value, we use `None` as
181-
// the `value_id` on the `BindSymbolicName`.
182-
auto entity_name_id = context.entity_names().AddSymbolicBindingName(
183-
SemIR::NameId::SelfType, interface_info.scope_id,
184-
context.scope_stack().AddCompileTimeBinding(),
185-
/*is_template=*/false);
186-
interface_info.self_param_id =
187-
AddInst(context, SemIR::LocIdAndInst::NoLoc<SemIR::BindSymbolicName>(
188-
{.type_id = self_type_id,
189-
.entity_name_id = entity_name_id,
190-
.value_id = SemIR::InstId::None}));
191-
context.scope_stack().PushCompileTimeBinding(interface_info.self_param_id);
192-
context.name_scopes().AddRequiredName(interface_info.scope_id,
193-
SemIR::NameId::SelfType,
194-
interface_info.self_param_id);
141+
interface_info.self_param_id = AddSelfGenericParameter(
142+
context, self_type_id, interface_info.scope_id, /*is_template=*/false);
195143

196144
// Enter the interface scope.
197-
context.scope_stack().PushForEntity(
198-
interface_decl_id, interface_info.scope_id, self_specific_id);
145+
context.scope_stack().PushForEntity(decl_inst_id, interface_info.scope_id,
146+
self_specific_id);
199147

200148
// TODO: Handle the case where there's control flow in the interface body. For
201149
// example:
@@ -207,6 +155,8 @@ auto HandleParseNode(Context& context,
207155
// We may need to track a list of instruction blocks here, as we do for a
208156
// function.
209157
interface_info.body_block_id = context.inst_block_stack().PeekOrAdd();
158+
159+
context.node_stack().Push(node_id, interface_id);
210160
return true;
211161
}
212162

0 commit comments

Comments
 (0)