diff --git a/toolchain/check/handle_binding_pattern.cpp b/toolchain/check/handle_binding_pattern.cpp index 167617e1af70..4ab73fa83b31 100644 --- a/toolchain/check/handle_binding_pattern.cpp +++ b/toolchain/check/handle_binding_pattern.cpp @@ -19,6 +19,17 @@ auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, // Every other kind of pattern binding has a name. auto [name_node, name_id] = context.node_stack().PopNameWithNodeId(); + // Determine whether we're handling an associated constant. These share the + // syntax for a compile-time binding, but don't behave like other compile-time + // bindings. + // TODO: Consider using a different parse node kind to make this easier. + bool is_associated_constant = false; + if (is_generic) { + auto inst_id = context.scope_stack().PeekInstId(); + is_associated_constant = + inst_id.is_valid() && context.insts().Is(inst_id); + } + // Create the appropriate kind of binding for this pattern. auto make_bind_name = [&](SemIR::TypeId type_id, SemIR::InstId value_id) -> SemIR::LocIdAndInst { @@ -29,7 +40,7 @@ auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, .enclosing_scope_id = context.scope_stack().PeekNameScopeId(), // TODO: Don't allocate a compile-time binding index for an associated // constant declaration. - .bind_index = is_generic + .bind_index = is_generic && !is_associated_constant ? context.scope_stack().AddCompileTimeBinding() : SemIR::CompileTimeBindIndex::Invalid}); if (is_generic) { diff --git a/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_binding.carbon b/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_binding.carbon index 3423dda74999..2ffbbefde729 100644 --- a/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_binding.carbon +++ b/toolchain/check/testdata/interface/no_prelude/fail_assoc_const_not_binding.carbon @@ -16,8 +16,8 @@ interface I { // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = interface_type @I [template] // CHECK:STDOUT: %Self: I = bind_symbolic_name Self 0 [symbolic] -// CHECK:STDOUT: %T: type = bind_symbolic_name T 1 [symbolic] -// CHECK:STDOUT: %U: type = bind_symbolic_name U 2 [symbolic] +// CHECK:STDOUT: %T: type = bind_symbolic_name T [symbolic] +// CHECK:STDOUT: %U: type = bind_symbolic_name U [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file {} diff --git a/toolchain/check/testdata/interface/no_prelude/generic_binding_after_assoc_const.carbon b/toolchain/check/testdata/interface/no_prelude/generic_binding_after_assoc_const.carbon new file mode 100644 index 000000000000..ce75bdbcbfc6 --- /dev/null +++ b/toolchain/check/testdata/interface/no_prelude/generic_binding_after_assoc_const.carbon @@ -0,0 +1,61 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +interface I { + fn F(T:! type); + let U:! type; + // This `T` should have index 1, just like the `T` above. + // The intervening `U:! type` should not cause this to have index 2. + fn G(T:! type); +} + +// CHECK:STDOUT: --- generic_binding_after_assoc_const.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = interface_type @I [template] +// CHECK:STDOUT: %Self: I = bind_symbolic_name Self 0 [symbolic] +// CHECK:STDOUT: %T: type = bind_symbolic_name T 1 [symbolic] +// CHECK:STDOUT: %.2: type = assoc_entity_type @I, [template] +// CHECK:STDOUT: %.3: in I> = assoc_entity element0, @I.%F [template] +// CHECK:STDOUT: %.4: type = assoc_entity_type @I, type [template] +// CHECK:STDOUT: %.5: = assoc_entity element1, @I.%U [template] +// CHECK:STDOUT: %.6: in I> = assoc_entity element2, @I.%G [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.decl: type = interface_decl @I [template = constants.%.1] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @I { +// CHECK:STDOUT: %Self: I = bind_symbolic_name Self 0 [symbolic = constants.%Self] +// CHECK:STDOUT: %F: = fn_decl @F [template] { +// CHECK:STDOUT: %T.loc8_8.1: type = param T +// CHECK:STDOUT: %T.loc8_8.2: type = bind_symbolic_name T 1, %T.loc8_8.1 [symbolic = constants.%T] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc8: in I> = assoc_entity element0, %F [template = constants.%.3] +// CHECK:STDOUT: %U: type = assoc_const_decl U [template] +// CHECK:STDOUT: %.loc9: = assoc_entity element1, %U [template = constants.%.5] +// CHECK:STDOUT: %G: = fn_decl @G [template] { +// CHECK:STDOUT: %T.loc12_8.1: type = param T +// CHECK:STDOUT: %T.loc12_8.2: type = bind_symbolic_name T 1, %T.loc12_8.1 [symbolic = constants.%T] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc12: in I> = assoc_entity element2, %G [template = constants.%.6] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .F = %.loc8 +// CHECK:STDOUT: .U = %.loc9 +// CHECK:STDOUT: .G = %.loc12 +// CHECK:STDOUT: witness = (%F, %U, %G) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(@I.%T.loc8_8.2: type); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G(@I.%T.loc12_8.2: type); +// CHECK:STDOUT: