Просмотр исходного кода

Fix `where` crash when empty `decl_name_stack` (#4451)

Bug found by fuzzer.

---------

Co-authored-by: Josh L <josh11b@users.noreply.github.com>
josh11b 1 год назад
Родитель
Сommit
c30b1d1124

+ 1 - 1
toolchain/check/handle_where.cpp

@@ -34,7 +34,7 @@ auto HandleParseNode(Context& context, Parse::WhereOperandId node_id) -> bool {
   // the `value_id` on the `BindSymbolicName`.
   auto entity_name_id = context.entity_names().Add(
       {.name_id = SemIR::NameId::PeriodSelf,
-       .parent_scope_id = context.decl_name_stack().PeekParentScopeId(),
+       .parent_scope_id = context.scope_stack().PeekNameScopeId(),
        .bind_index = context.scope_stack().AddCompileTimeBinding()});
   auto inst_id =
       context.AddInst(SemIR::LocIdAndInst::NoLoc<SemIR::BindSymbolicName>(

+ 87 - 0
toolchain/check/testdata/where_expr/constraints.carbon

@@ -158,6 +158,16 @@ fn NotEmptyStruct() {
   EmptyStruct(C);
 }
 
+// --- let.carbon
+
+library "[[@TEST_NAME]]";
+
+interface A {}
+class D {}
+impl D as A {}
+// TODO: This should be a compile-time binding, once that is supported.
+let B: type where .Self impls A = D;
+
 // CHECK:STDOUT: --- state_constraints.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -1456,3 +1466,80 @@ fn NotEmptyStruct() {
 // CHECK:STDOUT:   %Y.patt.loc26_16.2 => constants.%Y
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- let.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %A.type: type = interface_type @A [template]
+// CHECK:STDOUT:   %Self: %A.type = bind_symbolic_name Self, 0 [symbolic]
+// CHECK:STDOUT:   %D: type = class_type @D [template]
+// CHECK:STDOUT:   %.1: type = struct_type {} [template]
+// CHECK:STDOUT:   %.2: <witness> = complete_type_witness %.1 [template]
+// CHECK:STDOUT:   %.3: type = tuple_type () [template]
+// CHECK:STDOUT:   %.4: <witness> = interface_witness () [template]
+// CHECK:STDOUT:   %.Self: type = bind_symbolic_name .Self, 0 [symbolic]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/operators
+// CHECK:STDOUT:     import Core//prelude/types
+// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
+// CHECK:STDOUT:     import Core//prelude/operators/bitwise
+// CHECK:STDOUT:     import Core//prelude/operators/comparison
+// CHECK:STDOUT:     import Core//prelude/types/bool
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .A = %A.decl
+// CHECK:STDOUT:     .D = %D.decl
+// CHECK:STDOUT:     .B = @__global_init.%B
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %A.decl: type = interface_decl @A [template = constants.%A.type] {} {}
+// CHECK:STDOUT:   %D.decl: type = class_decl @D [template = constants.%D] {} {}
+// CHECK:STDOUT:   impl_decl @impl [template] {} {
+// CHECK:STDOUT:     %D.ref: type = name_ref D, file.%D.decl [template = constants.%D]
+// CHECK:STDOUT:     %A.ref: type = name_ref A, file.%A.decl [template = constants.%A.type]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.Self: type = bind_symbolic_name .Self, 0 [symbolic = constants.%.Self]
+// CHECK:STDOUT:   %.Self.ref: type = name_ref .Self, %.Self [symbolic = constants.%.Self]
+// CHECK:STDOUT:   %A.ref: type = name_ref A, %A.decl [template = constants.%A.type]
+// CHECK:STDOUT:   %.loc8: type = where_expr %.Self [template = type] {
+// CHECK:STDOUT:     requirement_impls %.Self.ref, %A.ref
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: interface @A {
+// CHECK:STDOUT:   %Self: %A.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = %Self
+// CHECK:STDOUT:   witness = ()
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: impl @impl: %D.ref as %A.ref {
+// CHECK:STDOUT:   %.loc6: <witness> = interface_witness () [template = constants.%.4]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   witness = %.loc6
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @D {
+// CHECK:STDOUT:   %.loc5: <witness> = complete_type_witness %.1 [template = constants.%.2]
+// CHECK:STDOUT:
+// CHECK:STDOUT: !members:
+// CHECK:STDOUT:   .Self = constants.%D
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @__global_init() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %D.ref: type = name_ref D, file.%D.decl [template = constants.%D]
+// CHECK:STDOUT:   %B: type = bind_name B, %D.ref
+// CHECK:STDOUT:   return
+// CHECK:STDOUT: }
+// CHECK:STDOUT:

+ 141 - 12
toolchain/check/testdata/where_expr/fail_not_facet.carbon

@@ -12,11 +12,39 @@
 
 library "[[@TEST_NAME]]";
 
-// CHECK:STDERR: fail_left_where_not_facet.carbon:[[@LINE+3]]:10: error: left argument of `where` operator must be a facet type [WhereOnNonFacetType]
+// CHECK:STDERR: fail_left_where_not_facet.carbon:[[@LINE+4]]:10: error: left argument of `where` operator must be a facet type [WhereOnNonFacetType]
 // CHECK:STDERR: fn F(T:! i32 where .Self == bool);
 // CHECK:STDERR:          ^~~
+// CHECK:STDERR:
 fn F(T:! i32 where .Self == bool);
 
+// --- fail_left_where_unknown.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_left_where_unknown.carbon:[[@LINE+8]]:10: error: name `NOT_DECLARED` not found [NameNotFound]
+// CHECK:STDERR: fn G(U:! NOT_DECLARED where .Self == bool);
+// CHECK:STDERR:          ^~~~~~~~~~~~
+// CHECK:STDERR:
+// CHECK:STDERR: fail_left_where_unknown.carbon:[[@LINE+4]]:10: error: left argument of `where` operator must be a facet type [WhereOnNonFacetType]
+// CHECK:STDERR: fn G(U:! NOT_DECLARED where .Self == bool);
+// CHECK:STDERR:          ^~~~~~~~~~~~
+// CHECK:STDERR:
+fn G(U:! NOT_DECLARED where .Self == bool);
+
+// --- fail_var.carbon
+
+library "[[@TEST_NAME]]";
+
+// CHECK:STDERR: fail_var.carbon:[[@LINE+7]]:8: error: name `e` not found [NameNotFound]
+// CHECK:STDERR: var v: e where .x = 3;
+// CHECK:STDERR:        ^
+// CHECK:STDERR:
+// CHECK:STDERR: fail_var.carbon:[[@LINE+3]]:8: error: left argument of `where` operator must be a facet type [WhereOnNonFacetType]
+// CHECK:STDERR: var v: e where .x = 3;
+// CHECK:STDERR:        ^
+var v: e where .x = 3;
+
 // CHECK:STDOUT: --- fail_left_where_not_facet.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -56,20 +84,20 @@ fn F(T:! i32 where .Self == bool);
 // CHECK:STDOUT:   }
 // CHECK:STDOUT:   %Core.import = import Core
 // CHECK:STDOUT:   %F.decl: %F.type = fn_decl @F [template = constants.%F] {
-// CHECK:STDOUT:     %T.patt.loc7_6.1: <error> = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc7_6.2 (constants.%T.patt)]
-// CHECK:STDOUT:     %T.param_patt: <error> = value_param_pattern %T.patt.loc7_6.1, runtime_param<invalid> [symbolic = %T.patt.loc7_6.2 (constants.%T.patt)]
+// CHECK:STDOUT:     %T.patt.loc8_6.1: <error> = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc8_6.2 (constants.%T.patt)]
+// CHECK:STDOUT:     %T.param_patt: <error> = value_param_pattern %T.patt.loc8_6.1, runtime_param<invalid> [symbolic = %T.patt.loc8_6.2 (constants.%T.patt)]
 // CHECK:STDOUT:   } {
 // CHECK:STDOUT:     %int.make_type_32: init type = call constants.%Int32() [template = i32]
-// CHECK:STDOUT:     %.loc7_10.1: type = value_of_initializer %int.make_type_32 [template = i32]
-// CHECK:STDOUT:     %.loc7_10.2: type = converted %int.make_type_32, %.loc7_10.1 [template = i32]
+// CHECK:STDOUT:     %.loc8_10.1: type = value_of_initializer %int.make_type_32 [template = i32]
+// CHECK:STDOUT:     %.loc8_10.2: type = converted %int.make_type_32, %.loc8_10.1 [template = i32]
 // CHECK:STDOUT:     %.Self: <error> = bind_symbolic_name .Self, 0 [symbolic = constants.%.Self]
 // CHECK:STDOUT:     %.Self.ref: <error> = name_ref .Self, %.Self [symbolic = constants.%.Self]
 // CHECK:STDOUT:     %bool.make_type: init type = call constants.%Bool() [template = bool]
-// CHECK:STDOUT:     %.loc7_14: type = where_expr %.Self [template = <error>] {
+// CHECK:STDOUT:     %.loc8_14: type = where_expr %.Self [template = <error>] {
 // CHECK:STDOUT:       requirement_equivalent %.Self.ref, %bool.make_type
 // CHECK:STDOUT:     }
 // CHECK:STDOUT:     %T.param: <error> = value_param runtime_param<invalid>
-// CHECK:STDOUT:     %T.loc7_6.1: <error> = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc7_6.2 (constants.%T)]
+// CHECK:STDOUT:     %T.loc8_6.1: <error> = bind_symbolic_name T, 0, %T.param [symbolic = %T.loc8_6.2 (constants.%T)]
 // CHECK:STDOUT:   }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -77,15 +105,116 @@ fn F(T:! i32 where .Self == bool);
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @Bool() -> type = "bool.make_type";
 // CHECK:STDOUT:
-// CHECK:STDOUT: generic fn @F(%T.loc7_6.1: <error>) {
-// CHECK:STDOUT:   %T.loc7_6.2: <error> = bind_symbolic_name T, 0 [symbolic = %T.loc7_6.2 (constants.%T)]
-// CHECK:STDOUT:   %T.patt.loc7_6.2: <error> = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc7_6.2 (constants.%T.patt)]
+// CHECK:STDOUT: generic fn @F(%T.loc8_6.1: <error>) {
+// CHECK:STDOUT:   %T.loc8_6.2: <error> = bind_symbolic_name T, 0 [symbolic = %T.loc8_6.2 (constants.%T)]
+// CHECK:STDOUT:   %T.patt.loc8_6.2: <error> = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc8_6.2 (constants.%T.patt)]
 // CHECK:STDOUT:
 // CHECK:STDOUT:   fn(%T.param_patt: <error>);
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: specific @F(constants.%T) {
-// CHECK:STDOUT:   %T.loc7_6.2 => constants.%T
-// CHECK:STDOUT:   %T.patt.loc7_6.2 => constants.%T
+// CHECK:STDOUT:   %T.loc8_6.2 => constants.%T
+// CHECK:STDOUT:   %T.patt.loc8_6.2 => constants.%T
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_left_where_unknown.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.Self: <error> = bind_symbolic_name .Self, 0 [symbolic]
+// CHECK:STDOUT:   %Bool.type: type = fn_type @Bool [template]
+// CHECK:STDOUT:   %.1: type = tuple_type () [template]
+// CHECK:STDOUT:   %Bool: %Bool.type = struct_value () [template]
+// CHECK:STDOUT:   %U: <error> = bind_symbolic_name U, 0 [symbolic]
+// CHECK:STDOUT:   %U.patt: <error> = symbolic_binding_pattern U, 0 [symbolic]
+// CHECK:STDOUT:   %G.type: type = fn_type @G [template]
+// CHECK:STDOUT:   %G: %G.type = struct_value () [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// CHECK:STDOUT:     .Bool = %import_ref
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/operators
+// CHECK:STDOUT:     import Core//prelude/types
+// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
+// CHECK:STDOUT:     import Core//prelude/operators/bitwise
+// CHECK:STDOUT:     import Core//prelude/operators/comparison
+// CHECK:STDOUT:     import Core//prelude/types/bool
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %import_ref: %Bool.type = import_ref Core//prelude/types/bool, inst+5, loaded [template = constants.%Bool]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .G = %G.decl
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %G.decl: %G.type = fn_decl @G [template = constants.%G] {
+// CHECK:STDOUT:     %U.patt.loc12_6.1: <error> = symbolic_binding_pattern U, 0 [symbolic = %U.patt.loc12_6.2 (constants.%U.patt)]
+// CHECK:STDOUT:     %U.param_patt: <error> = value_param_pattern %U.patt.loc12_6.1, runtime_param<invalid> [symbolic = %U.patt.loc12_6.2 (constants.%U.patt)]
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     %NOT_DECLARED.ref: <error> = name_ref NOT_DECLARED, <error> [template = <error>]
+// CHECK:STDOUT:     %.Self: <error> = bind_symbolic_name .Self, 0 [symbolic = constants.%.Self]
+// CHECK:STDOUT:     %.Self.ref: <error> = name_ref .Self, %.Self [symbolic = constants.%.Self]
+// CHECK:STDOUT:     %bool.make_type: init type = call constants.%Bool() [template = bool]
+// CHECK:STDOUT:     %.loc12: type = where_expr %.Self [template = <error>] {
+// CHECK:STDOUT:       requirement_equivalent %.Self.ref, %bool.make_type
+// CHECK:STDOUT:     }
+// CHECK:STDOUT:     %U.param: <error> = value_param runtime_param<invalid>
+// CHECK:STDOUT:     %U.loc12_6.1: <error> = bind_symbolic_name U, 0, %U.param [symbolic = %U.loc12_6.2 (constants.%U)]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @Bool() -> type = "bool.make_type";
+// CHECK:STDOUT:
+// CHECK:STDOUT: generic fn @G(%U.loc12_6.1: <error>) {
+// CHECK:STDOUT:   %U.loc12_6.2: <error> = bind_symbolic_name U, 0 [symbolic = %U.loc12_6.2 (constants.%U)]
+// CHECK:STDOUT:   %U.patt.loc12_6.2: <error> = symbolic_binding_pattern U, 0 [symbolic = %U.patt.loc12_6.2 (constants.%U.patt)]
+// CHECK:STDOUT:
+// CHECK:STDOUT:   fn(%U.param_patt: <error>);
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: specific @G(constants.%U) {
+// CHECK:STDOUT:   %U.loc12_6.2 => constants.%U
+// CHECK:STDOUT:   %U.patt.loc12_6.2 => constants.%U
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- fail_var.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %.Self: <error> = bind_symbolic_name .Self, 0 [symbolic]
+// CHECK:STDOUT:   %.1: i32 = int_literal 3 [template]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Core: <namespace> = namespace file.%Core.import, [template] {
+// CHECK:STDOUT:     import Core//prelude
+// CHECK:STDOUT:     import Core//prelude/operators
+// CHECK:STDOUT:     import Core//prelude/types
+// CHECK:STDOUT:     import Core//prelude/operators/arithmetic
+// CHECK:STDOUT:     import Core//prelude/operators/as
+// CHECK:STDOUT:     import Core//prelude/operators/bitwise
+// CHECK:STDOUT:     import Core//prelude/operators/comparison
+// CHECK:STDOUT:     import Core//prelude/types/bool
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: file {
+// CHECK:STDOUT:   package: <namespace> = namespace [template] {
+// CHECK:STDOUT:     .Core = imports.%Core
+// CHECK:STDOUT:     .v = %v
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import = import Core
+// CHECK:STDOUT:   %e.ref: <error> = name_ref e, <error> [template = <error>]
+// CHECK:STDOUT:   %.Self: <error> = bind_symbolic_name .Self, 0 [symbolic = constants.%.Self]
+// CHECK:STDOUT:   %.Self.ref: <error> = name_ref .Self, %.Self [symbolic = constants.%.Self]
+// CHECK:STDOUT:   %.loc11_21: i32 = int_literal 3 [template = constants.%.1]
+// CHECK:STDOUT:   %.loc11_10: type = where_expr %.Self [template = <error>] {
+// CHECK:STDOUT:     requirement_rewrite <error>, <error>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %v.var: ref <error> = var v
+// CHECK:STDOUT:   %v: ref <error> = bind_name v, %v.var
 // CHECK:STDOUT: }
 // CHECK:STDOUT: