Skip to content

Commit a357f6c

Browse files
committed
require-check
1 parent 0730d53 commit a357f6c

File tree

11 files changed

+1459
-98
lines changed

11 files changed

+1459
-98
lines changed

toolchain/check/handle_require.cpp

Lines changed: 220 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,245 @@
22
// Exceptions. See /LICENSE for license information.
33
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
44

5+
#include "toolchain/base/kind_switch.h"
56
#include "toolchain/check/context.h"
7+
#include "toolchain/check/convert.h"
68
#include "toolchain/check/handle.h"
9+
#include "toolchain/check/modifiers.h"
10+
#include "toolchain/check/name_lookup.h"
11+
#include "toolchain/check/subst.h"
712
#include "toolchain/parse/node_ids.h"
13+
#include "toolchain/sem_ir/named_constraint.h"
14+
#include "toolchain/sem_ir/type_iterator.h"
15+
#include "toolchain/sem_ir/typed_insts.h"
816

917
namespace Carbon::Check {
1018

1119
auto HandleParseNode(Context& context, Parse::RequireIntroducerId node_id)
1220
-> bool {
13-
return context.TODO(node_id, "require");
21+
// Create an instruction block to hold the instructions created for the type
22+
// and interface.
23+
context.inst_block_stack().Push();
24+
25+
// Optional modifiers follow.
26+
context.decl_introducer_state_stack().Push<Lex::TokenKind::Require>();
27+
28+
auto scope_id = context.scope_stack().PeekNameScopeId();
29+
const auto& scope = context.name_scopes().Get(scope_id);
30+
auto scope_inst = context.insts().Get(scope.inst_id());
31+
if (!scope_inst.Is<SemIR::InterfaceDecl>() &&
32+
!scope_inst.Is<SemIR::NamedConstraintDecl>()) {
33+
CARBON_DIAGNOSTIC(
34+
RequireInWrongScope, Error,
35+
"`require` can only be used in an interface or constraint");
36+
context.emitter().Emit(node_id, RequireInWrongScope);
37+
context.node_stack().Push(node_id, SemIR::ErrorInst::InstId);
38+
} else {
39+
context.node_stack().Push(node_id, scope.inst_id());
40+
}
41+
return true;
1442
}
1543

1644
auto HandleParseNode(Context& context, Parse::RequireDefaultSelfImplsId node_id)
1745
-> bool {
18-
return context.TODO(node_id, "require");
46+
auto scope_inst_id =
47+
context.node_stack().Peek<Parse::NodeKind::RequireIntroducer>();
48+
if (scope_inst_id == SemIR::ErrorInst::InstId) {
49+
context.node_stack().Push(node_id, SemIR::ErrorInst::TypeInstId);
50+
return true;
51+
}
52+
53+
auto scope_id = context.scope_stack().PeekNameScopeId();
54+
auto lookup_result =
55+
LookupNameInExactScope(context, node_id, SemIR::NameId::SelfType,
56+
scope_id, context.name_scopes().Get(scope_id),
57+
/*is_being_declared=*/false);
58+
CARBON_CHECK(lookup_result.is_found());
59+
60+
auto self_inst_id = lookup_result.target_inst_id();
61+
auto self_type_id = context.insts().Get(self_inst_id).type_id();
62+
CARBON_CHECK(context.types().Is<SemIR::FacetType>(self_type_id));
63+
64+
auto self_facet_as_type = AddTypeInst<SemIR::FacetAccessType>(
65+
context, node_id,
66+
{.type_id = SemIR::TypeType::TypeId,
67+
.facet_value_inst_id = self_inst_id});
68+
context.node_stack().Push(node_id, self_facet_as_type);
69+
return true;
1970
}
2071

2172
auto HandleParseNode(Context& context, Parse::RequireTypeImplsId node_id)
2273
-> bool {
23-
return context.TODO(node_id, "require");
74+
auto [self_node_id, self_inst_id] = context.node_stack().PopExprWithNodeId();
75+
auto self_type = ExprAsType(context, self_node_id, self_inst_id);
76+
context.node_stack().Push(node_id, self_type.inst_id);
77+
return true;
78+
}
79+
80+
static auto ConstraintHasInterface(Context& context,
81+
SemIR::FacetType facet_type) -> bool {
82+
const auto& facet_type_info =
83+
context.facet_types().Get(facet_type.facet_type_id);
84+
85+
return !facet_type_info.extend_constraints.empty() ||
86+
!facet_type_info.self_impls_constraints.empty();
87+
}
88+
89+
static auto TypeOrInterfaceReferencesSelf(Context& context,
90+
SemIR::TypeInstId inst_id,
91+
SemIR::FacetType facet_type) -> bool {
92+
if (inst_id == SemIR::ErrorInst::TypeInstId) {
93+
// Don't generate more diagnostics.
94+
return true;
95+
}
96+
97+
SemIR::TypeIterator type_iter(&context.sem_ir());
98+
type_iter.Add(context.constant_values().GetConstantTypeInstId(inst_id));
99+
100+
const auto& facet_type_info =
101+
context.facet_types().Get(facet_type.facet_type_id);
102+
for (auto extend : facet_type_info.extend_constraints) {
103+
type_iter.Add(extend);
104+
}
105+
for (auto self_impls : facet_type_info.self_impls_constraints) {
106+
type_iter.Add(self_impls);
107+
}
108+
109+
while (true) {
110+
auto step = type_iter.Next();
111+
if (step.Is<SemIR::TypeIterator::Step::Done>()) {
112+
break;
113+
}
114+
CARBON_KIND_SWITCH(step.any) {
115+
case CARBON_KIND(SemIR::TypeIterator::Step::Error _): {
116+
// Don't generate more diagnostics.
117+
return true;
118+
}
119+
case CARBON_KIND(SemIR::TypeIterator::Step::SymbolicBinding bind): {
120+
if (context.entity_names().Get(bind.entity_name_id).name_id ==
121+
SemIR::NameId::SelfType) {
122+
return true;
123+
}
124+
break;
125+
}
126+
default:
127+
break;
128+
}
129+
}
130+
return false;
131+
}
132+
133+
static auto RequirementReferencesSelf(
134+
Context& context, const SemIR::FacetTypeInfo& facet_type_info) -> bool {
135+
class FindSelfCallbacks : public SubstInstCallbacks {
136+
public:
137+
explicit FindSelfCallbacks(Context* context, bool* found)
138+
: SubstInstCallbacks(context), found_(found) {}
139+
auto Subst(SemIR::InstId& inst_id) -> SubstResult override {
140+
if (*found_ || context().constant_values().Get(inst_id).is_concrete()) {
141+
return FullySubstituted;
142+
}
143+
if (auto bind =
144+
context().insts().TryGetAs<SemIR::SymbolicBindingType>(inst_id)) {
145+
const auto& entity_name =
146+
context().entity_names().Get(bind->entity_name_id);
147+
if (entity_name.name_id == SemIR::NameId::SelfType) {
148+
// It would be nice to return a location, but we're working with
149+
// canonical instructions so there's no location available here.
150+
*found_ = true;
151+
return FullySubstituted;
152+
}
153+
}
154+
return SubstOperands;
155+
}
156+
auto Rebuild(SemIR::InstId /*orig_inst_id*/, SemIR::Inst /*new_inst*/)
157+
-> SemIR::InstId override {
158+
CARBON_FATAL();
159+
}
160+
161+
bool* found_;
162+
};
163+
164+
bool found = false;
165+
FindSelfCallbacks callbacks(&context, &found);
166+
for (const auto& rewrite : facet_type_info.rewrite_constraints) {
167+
SubstInst(context, rewrite.lhs_id, callbacks);
168+
if (found) {
169+
return true;
170+
}
171+
SubstInst(context, rewrite.rhs_id, callbacks);
172+
if (found) {
173+
return true;
174+
}
175+
}
176+
177+
return false;
24178
}
25179

26180
auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool {
27-
return context.TODO(node_id, "require");
181+
auto [constraint_node_id, constraint_inst_id] =
182+
context.node_stack().PopExprWithNodeId();
183+
auto [self_node_id, self_inst_id] =
184+
context.node_stack().PopWithNodeId<Parse::NodeCategory::RequireImpls>();
185+
186+
auto constraint_constant_value_inst_id =
187+
context.constant_values().GetConstantInstId(constraint_inst_id);
188+
auto constraint_facet_type = context.insts().TryGetAs<SemIR::FacetType>(
189+
constraint_constant_value_inst_id);
190+
if (constraint_constant_value_inst_id == SemIR::ErrorInst::InstId) {
191+
constraint_inst_id = self_inst_id = SemIR::ErrorInst::TypeInstId;
192+
} else if (!constraint_facet_type) {
193+
CARBON_DIAGNOSTIC(
194+
RequireImplsMissingFacetType, Error,
195+
"`require` declaration constrained by a non-facet type; "
196+
"expected an `interface` or `constraint` name after `impls`");
197+
context.emitter().Emit(constraint_node_id, RequireImplsMissingFacetType);
198+
constraint_inst_id = self_inst_id = SemIR::ErrorInst::TypeInstId;
199+
} else if (!ConstraintHasInterface(context, *constraint_facet_type)) {
200+
CARBON_DIAGNOSTIC(
201+
RequireImplsHasEmptyFacetType, Error,
202+
"`require` declaration constrained by an empty constraint; "
203+
"expected an `interface` or a non-empty `constraint`");
204+
context.emitter().Emit(constraint_node_id, RequireImplsHasEmptyFacetType);
205+
constraint_inst_id = self_inst_id = SemIR::ErrorInst::TypeInstId;
206+
} else if (!TypeOrInterfaceReferencesSelf(context, self_inst_id,
207+
*constraint_facet_type)) {
208+
CARBON_DIAGNOSTIC(RequireImplsMissingSelf, Error,
209+
"no `Self` reference found in `require` declaration; "
210+
"`Self` must appear in the self-type or as a generic "
211+
"parameter for an `interface` or `constraint`");
212+
context.emitter().Emit(node_id, RequireImplsMissingSelf);
213+
constraint_inst_id = self_inst_id = SemIR::ErrorInst::TypeInstId;
214+
} else if (RequirementReferencesSelf(
215+
context, context.facet_types().Get(
216+
constraint_facet_type->facet_type_id))) {
217+
// TODO: Should this be allowed? For now, no, but leads question:
218+
// https://github.com/carbon-language/carbon-lang/issues/6285
219+
CARBON_DIAGNOSTIC(RequireImplsSelfInWhereExpr, Error,
220+
"`require` declaration with `Self` in the `where` "
221+
"expression of the constraint");
222+
context.emitter().Emit(constraint_node_id, RequireImplsSelfInWhereExpr);
223+
constraint_inst_id = self_inst_id = SemIR::ErrorInst::TypeInstId;
224+
}
225+
226+
[[maybe_unused]] auto decl_block_id = context.inst_block_stack().Pop();
227+
228+
// Process modifiers.
229+
auto introducer =
230+
context.decl_introducer_state_stack().Pop<Lex::TokenKind::Require>();
231+
LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Extend);
232+
233+
auto scope_inst_id =
234+
context.node_stack().Pop<Parse::NodeKind::RequireIntroducer>();
235+
if (scope_inst_id == SemIR::ErrorInst::InstId) {
236+
// `require` is in the wrong scope.
237+
return true;
238+
}
239+
240+
// TODO: Add the `require` constraint to the InterfaceDecl or ConstraintDecl
241+
// from `scope_inst_id`.
242+
243+
return true;
28244
}
29245

30246
} // namespace Carbon::Check

toolchain/check/name_lookup.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ auto LookupUnqualifiedName(Context& context, SemIR::LocId loc_id,
161161
DiagnoseNameNotFound(context, loc_id, name_id);
162162
}
163163

164+
// TODO: Should this return MakeNotFound if `required` is false, so that
165+
// `is_found()` would be false?
164166
return {.specific_id = SemIR::SpecificId::None,
165167
.scope_result = SemIR::ScopeLookupResult::MakeError()};
166168
}

toolchain/check/node_stack.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,8 @@ class NodeStack {
397397
Id::KindFor<SemIR::NameId>());
398398
set_id_if_category_is(Parse::NodeCategory::ImplAs,
399399
Id::KindFor<SemIR::TypeInstId>());
400+
set_id_if_category_is(Parse::NodeCategory::RequireImpls,
401+
Id::KindFor<SemIR::TypeInstId>());
400402
set_id_if_category_is(Parse::NodeCategory::Decl |
401403
Parse::NodeCategory::Statement |
402404
Parse::NodeCategory::Modifier,
@@ -412,6 +414,7 @@ class NodeStack {
412414
case Parse::NodeKind::CallExprStart:
413415
case Parse::NodeKind::FieldNameAndType:
414416
case Parse::NodeKind::IfExprThen:
417+
case Parse::NodeKind::RequireIntroducer:
415418
case Parse::NodeKind::ReturnType:
416419
case Parse::NodeKind::ShortCircuitOperandAnd:
417420
case Parse::NodeKind::ShortCircuitOperandOr:
@@ -523,9 +526,6 @@ class NodeStack {
523526
case Parse::NodeKind::ParenExprStart:
524527
case Parse::NodeKind::PatternListComma:
525528
case Parse::NodeKind::Placeholder:
526-
case Parse::NodeKind::RequireIntroducer:
527-
case Parse::NodeKind::RequireDefaultSelfImpls:
528-
case Parse::NodeKind::RequireTypeImpls:
529529
case Parse::NodeKind::RequirementAnd:
530530
case Parse::NodeKind::RequirementEqual:
531531
case Parse::NodeKind::RequirementEqualEqual:

toolchain/check/testdata/facet/require_invalid.carbon

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ library "[[@TEST_NAME]]";
1515

1616
interface Y {}
1717

18-
// CHECK:STDERR: fail_todo_require_outside_scope.carbon:[[@LINE+4]]:1: error: semantics TODO: `require` [SemanticsTodo]
18+
// CHECK:STDERR: fail_todo_require_outside_scope.carbon:[[@LINE+4]]:1: error: `require` can only be used in an interface or constraint [RequireInWrongScope]
1919
// CHECK:STDERR: require impls Y;
2020
// CHECK:STDERR: ^~~~~~~
2121
// CHECK:STDERR:
@@ -27,8 +27,7 @@ library "[[@TEST_NAME]]";
2727
interface Y {}
2828

2929
class C {
30-
// TODO: require is not allowed outside of `interface` or `constraint`.
31-
// CHECK:STDERR: fail_todo_require_in_class.carbon:[[@LINE+4]]:3: error: semantics TODO: `require` [SemanticsTodo]
30+
// CHECK:STDERR: fail_todo_require_in_class.carbon:[[@LINE+4]]:3: error: `require` can only be used in an interface or constraint [RequireInWrongScope]
3231
// CHECK:STDERR: require impls Y;
3332
// CHECK:STDERR: ^~~~~~~
3433
// CHECK:STDERR:
@@ -42,6 +41,7 @@ interface Y {}
4241

4342
fn F() {
4443
// require is not allowed outside of `interface` or `constraint`.
44+
//
4545
// CHECK:STDERR: fail_require_in_fn.carbon:[[@LINE+8]]:3: error: expected expression [ExpectedExpr]
4646
// CHECK:STDERR: require impls Y;
4747
// CHECK:STDERR: ^~~~~~~
@@ -52,3 +52,61 @@ fn F() {
5252
// CHECK:STDERR:
5353
require impls Y;
5454
}
55+
56+
// --- fail_require_in_nested_class.carbon
57+
library "[[@TEST_NAME]]";
58+
59+
interface Y {}
60+
61+
interface Z {
62+
// TODO: Add `default` modifier.
63+
fn F() {
64+
class C {
65+
// CHECK:STDERR: fail_require_in_nested_class.carbon:[[@LINE+4]]:7: error: `require` can only be used in an interface or constraint [RequireInWrongScope]
66+
// CHECK:STDERR: require impls Y;
67+
// CHECK:STDERR: ^~~~~~~
68+
// CHECK:STDERR:
69+
require impls Y;
70+
}
71+
}
72+
}
73+
74+
// --- fail_require_in_nested_fn.carbon
75+
library "[[@TEST_NAME]]";
76+
77+
interface Y {}
78+
79+
interface Z {
80+
// TODO: Add `default` modifier.
81+
fn F() {
82+
// require is not allowed outside of `interface` or `constraint`.
83+
//
84+
// CHECK:STDERR: fail_require_in_nested_fn.carbon:[[@LINE+8]]:5: error: expected expression [ExpectedExpr]
85+
// CHECK:STDERR: require impls Y;
86+
// CHECK:STDERR: ^~~~~~~
87+
// CHECK:STDERR:
88+
// CHECK:STDERR: fail_require_in_nested_fn.carbon:[[@LINE+4]]:5: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo]
89+
// CHECK:STDERR: require impls Y;
90+
// CHECK:STDERR: ^~~~~~~
91+
// CHECK:STDERR:
92+
require impls Y;
93+
}
94+
}
95+
96+
// --- fail_errors_in_require_still_found.carbon
97+
library "[[@TEST_NAME]]";
98+
99+
class C {
100+
// The `require` is diagnosed as being in the wrong place. But we can still
101+
// diagnose issues inside the decl too.
102+
//
103+
// CHECK:STDERR: fail_errors_in_require_still_found.carbon:[[@LINE+8]]:3: error: `require` can only be used in an interface or constraint [RequireInWrongScope]
104+
// CHECK:STDERR: require impls Y;
105+
// CHECK:STDERR: ^~~~~~~
106+
// CHECK:STDERR:
107+
// CHECK:STDERR: fail_errors_in_require_still_found.carbon:[[@LINE+4]]:17: error: name `Y` not found [NameNotFound]
108+
// CHECK:STDERR: require impls Y;
109+
// CHECK:STDERR: ^
110+
// CHECK:STDERR:
111+
require impls Y;
112+
}

0 commit comments

Comments
 (0)