Skip to content

Commit 43e09e8

Browse files
authored
Type-check require declarations (#6286)
They don't get stored anywhere yet, but this type checks the declarations and diagnoses errors in their form, such as not placing a facet type after `impls` or a type before it.
1 parent 42e2280 commit 43e09e8

File tree

12 files changed

+1478
-107
lines changed

12 files changed

+1478
-107
lines changed

toolchain/check/handle_require.cpp

Lines changed: 173 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,198 @@
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"
12+
#include "toolchain/check/type_completion.h"
713
#include "toolchain/parse/node_ids.h"
14+
#include "toolchain/sem_ir/named_constraint.h"
15+
#include "toolchain/sem_ir/type_iterator.h"
16+
#include "toolchain/sem_ir/typed_insts.h"
817

918
namespace Carbon::Check {
1019

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

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

2173
auto HandleParseNode(Context& context, Parse::RequireTypeImplsId node_id)
2274
-> bool {
23-
return context.TODO(node_id, "require");
75+
auto [self_node_id, self_inst_id] = context.node_stack().PopExprWithNodeId();
76+
auto self_type = ExprAsType(context, self_node_id, self_inst_id);
77+
context.node_stack().Push(node_id, self_type.inst_id);
78+
return true;
79+
}
80+
81+
static auto TypeStructureReferencesSelf(
82+
Context& context, SemIR::TypeInstId inst_id,
83+
const SemIR::IdentifiedFacetType& identified_facet_type) -> bool {
84+
if (inst_id == SemIR::ErrorInst::TypeInstId) {
85+
// Don't generate more diagnostics.
86+
return true;
87+
}
88+
89+
auto find_self = [&](SemIR::TypeIterator& type_iter) -> bool {
90+
while (true) {
91+
auto step = type_iter.Next();
92+
if (step.Is<SemIR::TypeIterator::Step::Done>()) {
93+
break;
94+
}
95+
CARBON_KIND_SWITCH(step.any) {
96+
case CARBON_KIND(SemIR::TypeIterator::Step::Error _): {
97+
// Don't generate more diagnostics.
98+
return true;
99+
}
100+
case CARBON_KIND(SemIR::TypeIterator::Step::SymbolicBinding bind): {
101+
if (context.entity_names().Get(bind.entity_name_id).name_id ==
102+
SemIR::NameId::SelfType) {
103+
return true;
104+
}
105+
break;
106+
}
107+
default:
108+
break;
109+
}
110+
}
111+
return false;
112+
};
113+
114+
{
115+
SemIR::TypeIterator type_iter(&context.sem_ir());
116+
type_iter.Add(context.constant_values().GetConstantTypeInstId(inst_id));
117+
if (find_self(type_iter)) {
118+
return true;
119+
}
120+
}
121+
122+
if (identified_facet_type.required_interfaces().empty()) {
123+
return false;
124+
}
125+
126+
for (auto specific_interface : identified_facet_type.required_interfaces()) {
127+
SemIR::TypeIterator type_iter(&context.sem_ir());
128+
type_iter.Add(specific_interface);
129+
if (!find_self(type_iter)) {
130+
return false;
131+
}
132+
}
133+
134+
return true;
24135
}
25136

26137
auto HandleParseNode(Context& context, Parse::RequireDeclId node_id) -> bool {
27-
return context.TODO(node_id, "require");
138+
auto [constraint_node_id, constraint_inst_id] =
139+
context.node_stack().PopExprWithNodeId();
140+
auto [self_node_id, self_inst_id] =
141+
context.node_stack().PopWithNodeId<Parse::NodeCategory::RequireImpls>();
142+
143+
[[maybe_unused]] auto decl_block_id = context.inst_block_stack().Pop();
144+
145+
// Process modifiers.
146+
auto introducer =
147+
context.decl_introducer_state_stack().Pop<Lex::TokenKind::Require>();
148+
LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Extend);
149+
150+
auto scope_inst_id =
151+
context.node_stack().Pop<Parse::NodeKind::RequireIntroducer>();
152+
153+
auto constraint_constant_value_inst_id =
154+
context.constant_values().GetConstantInstId(constraint_inst_id);
155+
auto constraint_facet_type = context.insts().TryGetAs<SemIR::FacetType>(
156+
constraint_constant_value_inst_id);
157+
if (!constraint_facet_type) {
158+
if (constraint_constant_value_inst_id != SemIR::ErrorInst::InstId) {
159+
CARBON_DIAGNOSTIC(
160+
RequireImplsMissingFacetType, Error,
161+
"`require` declaration constrained by a non-facet type; "
162+
"expected an `interface` or `constraint` name after `impls`");
163+
context.emitter().Emit(constraint_node_id, RequireImplsMissingFacetType);
164+
}
165+
// Can't continue without a constraint to use.
166+
return true;
167+
}
168+
169+
auto identified_facet_type_id =
170+
RequireIdentifiedFacetType(context, *constraint_facet_type);
171+
const auto& identified =
172+
context.identified_facet_types().Get(identified_facet_type_id);
173+
174+
if (!TypeStructureReferencesSelf(context, self_inst_id, identified)) {
175+
CARBON_DIAGNOSTIC(RequireImplsMissingSelf, Error,
176+
"no `Self` reference found in `require` declaration; "
177+
"`Self` must appear in the self-type or as a generic "
178+
"parameter for each `interface` or `constraint`");
179+
context.emitter().Emit(node_id, RequireImplsMissingSelf);
180+
return true;
181+
}
182+
183+
if (scope_inst_id == SemIR::ErrorInst::InstId) {
184+
// `require` is in the wrong scope.
185+
return true;
186+
}
187+
188+
if (identified.required_interfaces().empty()) {
189+
// A `require T impls type` adds no actual constraints.
190+
return true;
191+
}
192+
193+
// TODO: Add the `require` constraint to the InterfaceDecl or ConstraintDecl
194+
// from `scope_inst_id`.
195+
196+
return true;
28197
}
29198

30199
} // 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:
@@ -524,9 +527,6 @@ class NodeStack {
524527
case Parse::NodeKind::ParenExprStart:
525528
case Parse::NodeKind::PatternListComma:
526529
case Parse::NodeKind::Placeholder:
527-
case Parse::NodeKind::RequireIntroducer:
528-
case Parse::NodeKind::RequireDefaultSelfImpls:
529-
case Parse::NodeKind::RequireTypeImpls:
530530
case Parse::NodeKind::RequirementAnd:
531531
case Parse::NodeKind::RequirementEqual:
532532
case Parse::NodeKind::RequirementEqualEqual:

toolchain/check/testdata/facet/require_invalid.carbon

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,24 @@
1010
// TIP: To dump output, run:
1111
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/facet/require_invalid.carbon
1212

13-
// --- fail_todo_require_outside_scope.carbon
13+
// --- fail_require_outside_scope.carbon
1414
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_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:
2222
require impls Y;
2323

24-
// --- fail_todo_require_in_class.carbon
24+
// --- fail_require_in_class.carbon
2525
library "[[@TEST_NAME]]";
2626

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_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)