Skip to content

Commit bad9bed

Browse files
authored
Diagnose using named constraint's name inside its definition (#6906)
Using a named constraint inside itself is problematic: - If there were not require decls written above, it identifies as an empty set. This makes `Z(Self)` essentially disappear in the identified facet type, which produces "no use of Self" diagnostics while the user can see a use of Self in the code. - It won't include require decls that are written after, and so `require T impls Z` won't actually enforce that `T` impls all of `Z`. Previously this was an error because using the named constraint would require it to be identified, and it's not identified until it is complete. But this will change in proposal #6902. So that proposal also includes changes to preserve diagnostics for incorrect use of a named constraint before it's complete, which is implemented here. Discussed in open discussion [on 2026-03-12](https://docs.google.com/document/d/1mjllGO3ZCL4qGt9uJHUtcxKoHAGEY7Y999ie4EtBWB8/edit?tab=t.0#heading=h.1dvbbrp5a6t3). The new tests exposed a bug where we're not copying named constraints in a facet type on the RHS of `where .Self impls` into the facet type on the left, which is now fixed. The `fail_require_impls_incomplete_self_in_period_self_impls.carbon` test would not diagnose its error without this fix.
1 parent 6706162 commit bad9bed

File tree

4 files changed

+96
-93
lines changed

4 files changed

+96
-93
lines changed

toolchain/check/eval.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,6 +2494,10 @@ auto TryEvalTypedInst<SemIR::WhereExpr>(EvalContext& eval_context,
24942494
more_info.extend_constraints);
24952495
llvm::append_range(info.self_impls_constraints,
24962496
more_info.self_impls_constraints);
2497+
llvm::append_range(info.self_impls_named_constraints,
2498+
more_info.extend_named_constraints);
2499+
llvm::append_range(info.self_impls_named_constraints,
2500+
more_info.self_impls_named_constraints);
24972501
// Other requirements are copied in.
24982502
llvm::append_range(info.rewrite_constraints,
24992503
more_info.rewrite_constraints);

toolchain/check/handle_require.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "toolchain/parse/node_ids.h"
1818
#include "toolchain/sem_ir/ids.h"
1919
#include "toolchain/sem_ir/named_constraint.h"
20+
#include "toolchain/sem_ir/specific_named_constraint.h"
2021
#include "toolchain/sem_ir/type_iterator.h"
2122
#include "toolchain/sem_ir/typed_insts.h"
2223

@@ -209,6 +210,36 @@ static auto ValidateRequire(Context& context, SemIR::LocId loc_id,
209210
return std::nullopt;
210211
}
211212

213+
if (auto named_constraint =
214+
context.insts().TryGetAs<SemIR::NamedConstraintWithSelfDecl>(
215+
scope_inst_id)) {
216+
const auto& constraint_facet_type_info =
217+
context.facet_types().Get(constraint_facet_type->facet_type_id);
218+
// TODO: Handle other impls named constraints for the
219+
// RequireImplsReferenceCycle diagnostic.
220+
if (constraint_facet_type_info.other_requirements) {
221+
context.TODO(constraint_inst_id,
222+
"facet type has constraints that we don't handle yet");
223+
return std::nullopt;
224+
}
225+
auto named_constraints = llvm::concat<const SemIR::SpecificNamedConstraint>(
226+
constraint_facet_type_info.extend_named_constraints,
227+
constraint_facet_type_info.self_impls_named_constraints);
228+
for (auto c : named_constraints) {
229+
if (c.named_constraint_id == named_constraint->named_constraint_id) {
230+
const auto& named_constraint =
231+
context.named_constraints().Get(c.named_constraint_id);
232+
CARBON_DIAGNOSTIC(RequireImplsReferenceCycle, Error,
233+
"facet type in `require` declaration refers to the "
234+
"named constraint `{0}` from within its definition",
235+
SemIR::NameId);
236+
context.emitter().Emit(constraint_inst_id, RequireImplsReferenceCycle,
237+
named_constraint.name_id);
238+
return std::nullopt;
239+
}
240+
}
241+
}
242+
212243
auto identified_facet_type_id = RequireIdentifiedFacetType(
213244
context, SemIR::LocId(constraint_inst_id), self_constant_value_id,
214245
*constraint_facet_type, [&](auto& builder) {

toolchain/check/testdata/named_constraint/require.carbon

Lines changed: 60 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -175,34 +175,82 @@ constraint Z {
175175
// --- fail_require_impls_incomplete_self.carbon
176176
library "[[@TEST_NAME]]";
177177

178-
//@dump-sem-ir-begin
179178
constraint Z {
180-
// CHECK:STDERR: fail_require_impls_incomplete_self.carbon:[[@LINE+7]]:17: error: facet type `Z` cannot be identified in `require` declaration [RequireImplsUnidentifiedFacetType]
179+
// CHECK:STDERR: fail_require_impls_incomplete_self.carbon:[[@LINE+4]]:17: error: facet type in `require` declaration refers to the named constraint `Z` from within its definition [RequireImplsReferenceCycle]
181180
// CHECK:STDERR: require impls Z;
182181
// CHECK:STDERR: ^
183-
// CHECK:STDERR: fail_require_impls_incomplete_self.carbon:[[@LINE-4]]:1: note: constraint is currently being defined [NamedConstraintIncompleteWithinDefinition]
184-
// CHECK:STDERR: constraint Z {
185-
// CHECK:STDERR: ^~~~~~~~~~~~~~
186182
// CHECK:STDERR:
187183
require impls Z;
188184
}
189-
//@dump-sem-ir-end
185+
186+
// --- fail_require_impls_incomplete_self_forward_declared.carbon
187+
library "[[@TEST_NAME]]";
188+
189+
constraint Z;
190+
191+
constraint Z {
192+
// CHECK:STDERR: fail_require_impls_incomplete_self_forward_declared.carbon:[[@LINE+4]]:17: error: facet type in `require` declaration refers to the named constraint `Z` from within its definition [RequireImplsReferenceCycle]
193+
// CHECK:STDERR: require impls Z;
194+
// CHECK:STDERR: ^
195+
// CHECK:STDERR:
196+
require impls Z;
197+
}
198+
199+
// --- fail_require_impls_incomplete_self_in_period_self_impls.carbon
200+
library "[[@TEST_NAME]]";
201+
202+
constraint Z {
203+
// CHECK:STDERR: fail_require_impls_incomplete_self_in_period_self_impls.carbon:[[@LINE+4]]:17: error: facet type in `require` declaration refers to the named constraint `Z` from within its definition [RequireImplsReferenceCycle]
204+
// CHECK:STDERR: require impls type where .Self impls Z;
205+
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~
206+
// CHECK:STDERR:
207+
require impls type where .Self impls Z;
208+
}
209+
210+
// --- fail_require_impls_incomplete_self_in_class_impls.carbon
211+
library "[[@TEST_NAME]]";
212+
213+
class C;
214+
constraint Z {
215+
// TODO: This should be a RequireImplsReferenceCycle error since `Z` is being
216+
// used inside `Z`.
217+
// CHECK:STDERR: fail_require_impls_incomplete_self_in_class_impls.carbon:[[@LINE+4]]:17: error: semantics TODO: `facet type has constraints that we don't handle yet` [SemanticsTodo]
218+
// CHECK:STDERR: require impls type where C impls Z;
219+
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~
220+
// CHECK:STDERR:
221+
require impls type where C impls Z;
222+
}
223+
224+
// --- fail_require_impls_incomplete_indirect.carbon
225+
library "[[@TEST_NAME]]";
226+
227+
constraint Z;
228+
229+
constraint Y {
230+
// CHECK:STDERR: fail_require_impls_incomplete_indirect.carbon:[[@LINE+7]]:17: error: facet type `Z` cannot be identified in `require` declaration [RequireImplsUnidentifiedFacetType]
231+
// CHECK:STDERR: require impls Z;
232+
// CHECK:STDERR: ^
233+
// CHECK:STDERR: fail_require_impls_incomplete_indirect.carbon:[[@LINE-6]]:1: note: constraint was forward declared here [NamedConstraintForwardDeclaredHere]
234+
// CHECK:STDERR: constraint Z;
235+
// CHECK:STDERR: ^~~~~~~~~~~~~
236+
// CHECK:STDERR:
237+
require impls Z;
238+
}
239+
240+
constraint Z {
241+
require impls Y;
242+
}
190243

191244
// --- fail_require_impls_incomplete_self_specific.carbon
192245
library "[[@TEST_NAME]]";
193246

194-
//@dump-sem-ir-begin
195247
constraint Z(T:! type) {
196-
// CHECK:STDERR: fail_require_impls_incomplete_self_specific.carbon:[[@LINE+7]]:19: error: facet type `Z(Self)` cannot be identified in `require` declaration [RequireImplsUnidentifiedFacetType]
248+
// CHECK:STDERR: fail_require_impls_incomplete_self_specific.carbon:[[@LINE+4]]:19: error: facet type in `require` declaration refers to the named constraint `Z` from within its definition [RequireImplsReferenceCycle]
197249
// CHECK:STDERR: require T impls Z(Self);
198250
// CHECK:STDERR: ^~~~~~~
199-
// CHECK:STDERR: fail_require_impls_incomplete_self_specific.carbon:[[@LINE-4]]:1: note: constraint is currently being defined [NamedConstraintIncompleteWithinDefinition]
200-
// CHECK:STDERR: constraint Z(T:! type) {
201-
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~
202251
// CHECK:STDERR:
203252
require T impls Z(Self);
204253
}
205-
//@dump-sem-ir-end
206254

207255
// --- fail_require_impls_without_self.carbon
208256
library "[[@TEST_NAME]]";
@@ -1064,87 +1112,6 @@ fn F() {
10641112
// CHECK:STDOUT:
10651113
// CHECK:STDOUT: specific @Z.WithSelf(constants.%Self) {}
10661114
// CHECK:STDOUT:
1067-
// CHECK:STDOUT: --- fail_require_impls_incomplete_self.carbon
1068-
// CHECK:STDOUT:
1069-
// CHECK:STDOUT: constants {
1070-
// CHECK:STDOUT: %Z.type: type = facet_type <@Z> [concrete]
1071-
// CHECK:STDOUT: %Self: %Z.type = symbolic_binding Self, 0 [symbolic]
1072-
// CHECK:STDOUT: }
1073-
// CHECK:STDOUT:
1074-
// CHECK:STDOUT: file {
1075-
// CHECK:STDOUT: %Z.decl: type = constraint_decl @Z [concrete = constants.%Z.type] {} {}
1076-
// CHECK:STDOUT: }
1077-
// CHECK:STDOUT:
1078-
// CHECK:STDOUT: constraint @Z {
1079-
// CHECK:STDOUT: %Self: %Z.type = symbolic_binding Self, 0 [symbolic = constants.%Self]
1080-
// CHECK:STDOUT: %Z.WithSelf.decl = constraint_with_self_decl @Z [concrete]
1081-
// CHECK:STDOUT:
1082-
// CHECK:STDOUT: !members:
1083-
// CHECK:STDOUT: .Self = %Self
1084-
// CHECK:STDOUT: .Z = <poisoned>
1085-
// CHECK:STDOUT: .Z = <poisoned>
1086-
// CHECK:STDOUT:
1087-
// CHECK:STDOUT: !requires:
1088-
// CHECK:STDOUT: }
1089-
// CHECK:STDOUT:
1090-
// CHECK:STDOUT: specific @Z.WithSelf(constants.%Self) {}
1091-
// CHECK:STDOUT:
1092-
// CHECK:STDOUT: --- fail_require_impls_incomplete_self_specific.carbon
1093-
// CHECK:STDOUT:
1094-
// CHECK:STDOUT: constants {
1095-
// CHECK:STDOUT: %T: type = symbolic_binding T, 0 [symbolic]
1096-
// CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete]
1097-
// CHECK:STDOUT: %Z.type.7a8: type = generic_named_constaint_type @Z [concrete]
1098-
// CHECK:STDOUT: %empty_struct: %Z.type.7a8 = struct_value () [concrete]
1099-
// CHECK:STDOUT: %Z.type.d682d6.1: type = facet_type <@Z, @Z(%T)> [symbolic]
1100-
// CHECK:STDOUT: %Self: %Z.type.d682d6.1 = symbolic_binding Self, 1 [symbolic]
1101-
// CHECK:STDOUT: %Self.binding.as_type: type = symbolic_binding_type Self, 1, %Self [symbolic]
1102-
// CHECK:STDOUT: }
1103-
// CHECK:STDOUT:
1104-
// CHECK:STDOUT: file {
1105-
// CHECK:STDOUT: %Z.decl: %Z.type.7a8 = constraint_decl @Z [concrete = constants.%empty_struct] {
1106-
// CHECK:STDOUT: %T.patt: %pattern_type = symbolic_binding_pattern T, 0 [concrete]
1107-
// CHECK:STDOUT: } {
1108-
// CHECK:STDOUT: %.loc4_18.1: type = splice_block %.loc4_18.2 [concrete = type] {
1109-
// CHECK:STDOUT: <elided>
1110-
// CHECK:STDOUT: %.loc4_18.2: type = type_literal type [concrete = type]
1111-
// CHECK:STDOUT: }
1112-
// CHECK:STDOUT: %T.loc4_14.2: type = symbolic_binding T, 0 [symbolic = %T.loc4_14.1 (constants.%T)]
1113-
// CHECK:STDOUT: }
1114-
// CHECK:STDOUT: }
1115-
// CHECK:STDOUT:
1116-
// CHECK:STDOUT: generic constraint @Z(%T.loc4_14.2: type) {
1117-
// CHECK:STDOUT: %T.loc4_14.1: type = symbolic_binding T, 0 [symbolic = %T.loc4_14.1 (constants.%T)]
1118-
// CHECK:STDOUT:
1119-
// CHECK:STDOUT: !definition:
1120-
// CHECK:STDOUT: %Z.type: type = facet_type <@Z, @Z(%T.loc4_14.1)> [symbolic = %Z.type (constants.%Z.type.d682d6.1)]
1121-
// CHECK:STDOUT: %Self.loc4_24.2: @Z.%Z.type (%Z.type.d682d6.1) = symbolic_binding Self, 1 [symbolic = %Self.loc4_24.2 (constants.%Self)]
1122-
// CHECK:STDOUT:
1123-
// CHECK:STDOUT: constraint {
1124-
// CHECK:STDOUT: %Self.loc4_24.1: @Z.%Z.type (%Z.type.d682d6.1) = symbolic_binding Self, 1 [symbolic = %Self.loc4_24.2 (constants.%Self)]
1125-
// CHECK:STDOUT: %Z.WithSelf.decl = constraint_with_self_decl @Z [concrete]
1126-
// CHECK:STDOUT:
1127-
// CHECK:STDOUT: !members:
1128-
// CHECK:STDOUT: .Self = %Self.loc4_24.1
1129-
// CHECK:STDOUT: .T = <poisoned>
1130-
// CHECK:STDOUT: .Z = <poisoned>
1131-
// CHECK:STDOUT: .T = <poisoned>
1132-
// CHECK:STDOUT: .Z = <poisoned>
1133-
// CHECK:STDOUT:
1134-
// CHECK:STDOUT: !requires:
1135-
// CHECK:STDOUT: }
1136-
// CHECK:STDOUT: }
1137-
// CHECK:STDOUT:
1138-
// CHECK:STDOUT: specific @Z(constants.%T) {
1139-
// CHECK:STDOUT: %T.loc4_14.1 => constants.%T
1140-
// CHECK:STDOUT: }
1141-
// CHECK:STDOUT:
1142-
// CHECK:STDOUT: specific @Z.WithSelf(constants.%T, constants.%Self) {}
1143-
// CHECK:STDOUT:
1144-
// CHECK:STDOUT: specific @Z(constants.%Self.binding.as_type) {
1145-
// CHECK:STDOUT: %T.loc4_14.1 => constants.%Self.binding.as_type
1146-
// CHECK:STDOUT: }
1147-
// CHECK:STDOUT:
11481115
// CHECK:STDOUT: --- fail_require_impls_without_self.carbon
11491116
// CHECK:STDOUT:
11501117
// CHECK:STDOUT: constants {

toolchain/diagnostics/kind.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ CARBON_DIAGNOSTIC_KIND(RequireImplsMissingFacetType)
381381
CARBON_DIAGNOSTIC_KIND(RequireImplsMissingSelf)
382382
CARBON_DIAGNOSTIC_KIND(RequireImplsMissingSelfEmptyFacetType)
383383
CARBON_DIAGNOSTIC_KIND(RequireImplsIncompleteFacetType)
384+
CARBON_DIAGNOSTIC_KIND(RequireImplsReferenceCycle)
384385
CARBON_DIAGNOSTIC_KIND(RequireImplsUnidentifiedFacetType)
385386
CARBON_DIAGNOSTIC_KIND(RequireImplsNotImplemented)
386387
CARBON_DIAGNOSTIC_KIND(RequireInWrongScope)

0 commit comments

Comments
 (0)