Skip to content

Commit 19a7fb0

Browse files
jonmeowdanakj
andauthored
Switch handling of errors in impls to not build a type structure (#5881)
Per discussion at #5875 (comment), a different approach to the same solution. A key difference is that whereas #5875 would build a `TypeStructure` containing `ConcreteType{error}`, this instead just returns nothing. This means impls with errors can't be compared in the same way, though I'm not sure how much impact that'll really have (I've added a test here to show a case where it seemed interesting to see what effect it'd have, and it seems to have none). --------- Co-authored-by: Dana Jansens <[email protected]>
1 parent a905f15 commit 19a7fb0

File tree

7 files changed

+89
-14
lines changed

7 files changed

+89
-14
lines changed

toolchain/check/impl_lookup.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -537,17 +537,20 @@ static auto CollectCandidateImplsForQuery(
537537
// Build the type structure used for choosing the best the candidate.
538538
auto type_structure =
539539
BuildTypeStructure(context, impl.self_id, impl.interface);
540+
if (!type_structure) {
541+
continue;
542+
}
540543
// TODO: We can skip the comparison here if the `impl_interface_const_id` is
541544
// not symbolic, since when the interface and specific ids match, and they
542545
// aren't symbolic, the structure will be identical.
543546
if (!query_type_structure.CompareStructure(
544547
TypeStructure::CompareTest::IsEqualToOrMoreSpecificThan,
545-
type_structure)) {
548+
*type_structure)) {
546549
continue;
547550
}
548551

549552
candidate_impls.push_back(
550-
{id, impl.definition_id, std::move(type_structure)});
553+
{id, impl.definition_id, std::move(*type_structure)});
551554
}
552555

553556
auto compare = [](auto& lhs, auto& rhs) -> bool {
@@ -607,6 +610,9 @@ auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
607610
auto query_type_structure = BuildTypeStructure(
608611
context, context.constant_values().GetInstId(query_self_const_id),
609612
query_specific_interface);
613+
if (!query_type_structure) {
614+
return EvalImplLookupResult::MakeNone();
615+
}
610616
bool query_is_concrete =
611617
QueryIsConcrete(context, query_self_const_id, query_specific_interface);
612618

@@ -621,7 +627,7 @@ auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
621627
// not be concrete in this case, so only final impls can produce a concrete
622628
// witness for this query.
623629
auto candidate_impls = CollectCandidateImplsForQuery(
624-
context, self_facet_provides_witness, query_type_structure,
630+
context, self_facet_provides_witness, *query_type_structure,
625631
query_specific_interface);
626632

627633
for (const auto& candidate : candidate_impls) {

toolchain/check/impl_validation.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ struct ImplInfo {
3434
bool is_local;
3535
// If imported, the IR from which the `impl` decl was imported.
3636
SemIR::ImportIRId ir_id;
37-
TypeStructure type_structure;
37+
std::optional<TypeStructure> type_structure;
3838
};
3939

4040
} // namespace
@@ -47,7 +47,7 @@ static auto GetIRId(Context& context, SemIR::InstId owning_inst_id)
4747
return GetCanonicalImportIRInst(context, owning_inst_id).ir_id();
4848
}
4949

50-
auto GetImplInfo(Context& context, SemIR::ImplId impl_id) -> ImplInfo {
50+
static auto GetImplInfo(Context& context, SemIR::ImplId impl_id) -> ImplInfo {
5151
const auto& impl = context.impls().Get(impl_id);
5252
auto ir_id = GetIRId(context, impl.first_owning_decl_id);
5353
return {.impl_id = impl_id,
@@ -271,6 +271,11 @@ static auto ValidateImplsForInterface(Context& context,
271271

272272
auto impls_before = llvm::drop_end(impls, num_impls - split_point);
273273
for (const auto& impl_a : impls_before) {
274+
// Don't diagnose structures that contain errors.
275+
if (!impl_a.type_structure || !impl_b.type_structure) {
276+
continue;
277+
}
278+
274279
// Only enforce rules when at least one of the impls was written in this
275280
// file.
276281
if (!impl_a.is_local && !impl_b.is_local) {
@@ -304,9 +309,9 @@ static auto ValidateImplsForInterface(Context& context,
304309
did_diagnose_unmatchable_non_final_impl_with_final_impl = true;
305310
}
306311
}
307-
} else if (impl_a.type_structure.CompareStructure(
312+
} else if (impl_a.type_structure->CompareStructure(
308313
TypeStructure::CompareTest::HasOverlap,
309-
impl_b.type_structure)) {
314+
*impl_b.type_structure)) {
310315
// =====================================================================
311316
// Rules between two overlapping final impls.
312317
// =====================================================================

toolchain/check/testdata/impl/basic.carbon

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,56 @@ fn F() {
6060
C.(I.Op)();
6161
}
6262

63+
// --- fail_conflicting_bad_impls.carbon
64+
65+
library "[[@TEST_NAME]]";
66+
67+
interface I {}
68+
69+
class C(T:! type) {}
70+
71+
// CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE+4]]:12: error: name `invalid` not found [NameNotFound]
72+
// CHECK:STDERR: final impl invalid as I {}
73+
// CHECK:STDERR: ^~~~~~~
74+
// CHECK:STDERR:
75+
final impl invalid as I {}
76+
// CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE+11]]:12: error: name `invalid` not found [NameNotFound]
77+
// CHECK:STDERR: final impl invalid as I {}
78+
// CHECK:STDERR: ^~~~~~~
79+
// CHECK:STDERR:
80+
// CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE+7]]:1: error: redefinition of `impl <error> as I` [ImplRedefinition]
81+
// CHECK:STDERR: final impl invalid as I {}
82+
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~
83+
// CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE-8]]:1: note: previous definition was here [ImplPreviousDefinition]
84+
// CHECK:STDERR: final impl invalid as I {}
85+
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~
86+
// CHECK:STDERR:
87+
final impl invalid as I {}
88+
89+
// CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE+4]]:12: error: name `alsoinvalid` not found [NameNotFound]
90+
// CHECK:STDERR: final impl alsoinvalid as I {}
91+
// CHECK:STDERR: ^~~~~~~~~~~
92+
// CHECK:STDERR:
93+
final impl alsoinvalid as I {}
94+
95+
// CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE+4]]:14: error: name `invalid` not found [NameNotFound]
96+
// CHECK:STDERR: final impl C(invalid) as I {}
97+
// CHECK:STDERR: ^~~~~~~
98+
// CHECK:STDERR:
99+
final impl C(invalid) as I {}
100+
// CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE+11]]:14: error: name `invalid` not found [NameNotFound]
101+
// CHECK:STDERR: final impl C(invalid) as I {}
102+
// CHECK:STDERR: ^~~~~~~
103+
// CHECK:STDERR:
104+
// CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE+7]]:1: error: redefinition of `impl <error> as I` [ImplRedefinition]
105+
// CHECK:STDERR: final impl C(invalid) as I {}
106+
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
107+
// CHECK:STDERR: fail_conflicting_bad_impls.carbon:[[@LINE-8]]:1: note: previous definition was here [ImplPreviousDefinition]
108+
// CHECK:STDERR: final impl C(invalid) as I {}
109+
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
110+
// CHECK:STDERR:
111+
final impl C(invalid) as I {}
112+
63113
// CHECK:STDOUT: --- basic.carbon
64114
// CHECK:STDOUT:
65115
// CHECK:STDOUT: constants {

toolchain/check/type_structure.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ class TypeStructureBuilder {
155155
explicit TypeStructureBuilder(Context* context) : context_(context) {}
156156

157157
auto Run(SemIR::InstId self_inst_id,
158-
SemIR::SpecificInterface interface_constraint) -> TypeStructure {
158+
SemIR::SpecificInterface interface_constraint)
159+
-> std::optional<TypeStructure> {
159160
structure_.clear();
160161
symbolic_type_indices_.clear();
161162
concrete_types_.clear();
@@ -172,7 +173,7 @@ class TypeStructureBuilder {
172173
}
173174

174175
private:
175-
auto Build(SemIR::TypeIterator type_iter) -> TypeStructure;
176+
auto Build(SemIR::TypeIterator type_iter) -> std::optional<TypeStructure>;
176177

177178
// Append a structural element to the TypeStructure being built.
178179
auto AppendStructuralConcrete(TypeStructure::ConcreteType type) -> void {
@@ -202,7 +203,7 @@ class TypeStructureBuilder {
202203

203204
// Builds the type structure and returns it.
204205
auto TypeStructureBuilder::Build(SemIR::TypeIterator type_iter)
205-
-> TypeStructure {
206+
-> std::optional<TypeStructure> {
206207
while (true) {
207208
using Step = SemIR::TypeIterator::Step;
208209
CARBON_KIND_SWITCH(type_iter.Next().any) {
@@ -217,6 +218,9 @@ auto TypeStructureBuilder::Build(SemIR::TypeIterator type_iter)
217218
AppendStructuralConcreteCloseParen();
218219
break;
219220
}
221+
case CARBON_KIND(Step::Error _): {
222+
return std::nullopt;
223+
}
220224
case CARBON_KIND(Step::ConcreteType concrete): {
221225
AppendStructuralConcrete(concrete.type_id);
222226
break;
@@ -291,7 +295,8 @@ auto TypeStructureBuilder::Build(SemIR::TypeIterator type_iter)
291295
}
292296

293297
auto BuildTypeStructure(Context& context, SemIR::InstId self_inst_id,
294-
SemIR::SpecificInterface interface) -> TypeStructure {
298+
SemIR::SpecificInterface interface)
299+
-> std::optional<TypeStructure> {
295300
TypeStructureBuilder builder(&context);
296301
return builder.Run(self_inst_id, interface);
297302
}

toolchain/check/type_structure.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,12 @@ class TypeStructure : public Printable<TypeStructure> {
180180
//
181181
// Given `impl C as Z {}` the `self_const_id` would be a `C` and the interface
182182
// constraint would be `Z`.
183+
//
184+
// Returns nullopt if an ErrorInst is encountered in the self type or facet
185+
// value.
183186
auto BuildTypeStructure(Context& context, SemIR::InstId self_inst_id,
184-
SemIR::SpecificInterface interface) -> TypeStructure;
187+
SemIR::SpecificInterface interface)
188+
-> std::optional<TypeStructure>;
185189

186190
} // namespace Carbon::Check
187191

toolchain/sem_ir/type_iterator.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ auto TypeIterator::Next() -> Step {
7979

8080
case SemIR::AssociatedEntityType::Kind:
8181
case SemIR::BoolType::Kind:
82-
case SemIR::ErrorInst::Kind:
8382
case SemIR::FacetType::Kind:
8483
case SemIR::FloatType::Kind:
8584
case SemIR::FunctionType::Kind:
@@ -167,6 +166,10 @@ auto TypeIterator::Next() -> Step {
167166
return Step::StructStart{.type_id = type_id};
168167
}
169168
}
169+
170+
case SemIR::ErrorInst::Kind:
171+
return Step::Error();
172+
170173
default:
171174
CARBON_FATAL("Unhandled type instruction {0}", inst_id);
172175
}

toolchain/sem_ir/type_iterator.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,14 +187,16 @@ class TypeIterator::Step {
187187
struct End {};
188188
// Iteration is complete.
189189
struct Done {};
190+
// Iteration found an error.
191+
struct Error {};
190192

191193
// Each step is one of these.
192194
using Any =
193195
std::variant<ConcreteType, SymbolicType, TemplateType, ConcreteValue,
194196
SymbolicValue, StructFieldName, ClassStartOnly,
195197
StructStartOnly, TupleStartOnly, InterfaceStartOnly,
196198
ClassStart, StructStart, TupleStart, InterfaceStart,
197-
IntStart, ArrayStart, PointerStart, End, Done>;
199+
IntStart, ArrayStart, PointerStart, End, Done, Error>;
198200

199201
template <class T>
200202
auto Is() const -> bool {

0 commit comments

Comments
 (0)