Files
carbon-lang/toolchain/check/testdata/class/generic_method.carbon
T
Dana Jansens 6dbcc78e6c Rewrite symbolic constants in generic redeclarations (#5154)
When a generic function declaration was encountered for the second or
more time, we would FinishGenericRedecl() for the function decl, but
this just popped the generic region stack and moved on.

The issue with that is when the stack entry is gone, we lose the
symbolic constants from that declaration, and are unable to rewrite them
to point to the actual generic. This left us with a function declaration
with abstract symbolic values that were not useful, and in a function
call we use the declaration attached to the definition, which would be a
declaration with broken symbolic values. Then the function would be
uncallable since deduce would be unable to determine argument types
without the generic bindings.

This resolves the issue for functions, as well as ensuring the correct
generic id from a previous declaration is used for other generic entity
types that have redeclarations.

When a function declaration is qualified, such as defining a class
method outside the class body, we need only the function declaration to
contribute to its generic region stack. The code was collecting constant
values from all qualifier segments together incorrectly.

So when we PushNameQualifierScope(), we also drop the current generic
region stack and rewrite its constant values by calling
FinishGenericRedecl(), and open a new stack entry for the next part of
the qualified declaration.

If a generic declaration somehow has more dependent instruction than a
previous declaration, it would add new instructions to its eval block
with indices beyond the elements in the actual declaration eval block,
since we only store the block from the first declaration found. To avoid
this we plumb through that we are in a redeclaration, and terminate with
an ICE instead of adding new instructions to crash on later.

Fixes #5136.
2025-03-21 22:26:08 +00:00

155 lines
9.2 KiB
Plaintext

// 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
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/generic_method.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/generic_method.carbon
class Class(T:! type) {
var a: T;
fn F[self: Self](n: T);
}
fn Class(T:! type).F[self: Self](n: T) {}
// CHECK:STDOUT: --- generic_method.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic]
// CHECK:STDOUT: %T.patt: type = symbolic_binding_pattern T, 0 [symbolic]
// CHECK:STDOUT: %Class.type: type = generic_class_type @Class [concrete]
// CHECK:STDOUT: %Class.generic: %Class.type = struct_value () [concrete]
// CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T) [symbolic]
// CHECK:STDOUT: %require_complete.4ae: <witness> = require_complete_type %T [symbolic]
// CHECK:STDOUT: %Class.elem: type = unbound_element_type %Class, %T [symbolic]
// CHECK:STDOUT: %F.type: type = fn_type @F, @Class(%T) [symbolic]
// CHECK:STDOUT: %F: %F.type = struct_value () [symbolic]
// CHECK:STDOUT: %struct_type.a: type = struct_type {.a: %T} [symbolic]
// CHECK:STDOUT: %complete_type: <witness> = complete_type_witness %struct_type.a [symbolic]
// CHECK:STDOUT: %require_complete.4f8: <witness> = require_complete_type %Class [symbolic]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Core: <namespace> = namespace file.%Core.import, [concrete] {
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = namespace [concrete] {
// CHECK:STDOUT: .Core = imports.%Core
// CHECK:STDOUT: .Class = %Class.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: %Core.import = import Core
// CHECK:STDOUT: %Class.decl: %Class.type = class_decl @Class [concrete = constants.%Class.generic] {
// CHECK:STDOUT: %T.patt.loc11_13.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc11_13.2 (constants.%T.patt)]
// CHECK:STDOUT: } {
// CHECK:STDOUT: %T.loc11_13.1: type = bind_symbolic_name T, 0 [symbolic = %T.loc11_13.2 (constants.%T)]
// CHECK:STDOUT: }
// CHECK:STDOUT: %F.decl: @Class.%F.type (%F.type) = fn_decl @F [symbolic = constants.%F] {
// CHECK:STDOUT: %self.patt: @F.%Class (%Class) = binding_pattern self
// CHECK:STDOUT: %self.param_patt: @F.%Class (%Class) = value_param_pattern %self.patt, call_param0
// CHECK:STDOUT: %n.patt: @F.%T.loc13 (%T) = binding_pattern n
// CHECK:STDOUT: %n.param_patt: @F.%T.loc13 (%T) = value_param_pattern %n.patt, call_param1
// CHECK:STDOUT: } {
// CHECK:STDOUT: %T.loc16: type = bind_symbolic_name T, 0 [symbolic = @Class.%T.loc11_13.2 (constants.%T)]
// CHECK:STDOUT: %self.param.loc16: @F.%Class (%Class) = value_param call_param0
// CHECK:STDOUT: %.loc16_28.1: type = splice_block %Self.ref.loc16 [symbolic = %Class (constants.%Class)] {
// CHECK:STDOUT: %.loc16_28.2: type = specific_constant constants.%Class, @Class(constants.%T) [symbolic = %Class (constants.%Class)]
// CHECK:STDOUT: %Self.ref.loc16: type = name_ref Self, %.loc16_28.2 [symbolic = %Class (constants.%Class)]
// CHECK:STDOUT: }
// CHECK:STDOUT: %self.loc16: @F.%Class (%Class) = bind_name self, %self.param.loc16
// CHECK:STDOUT: %n.param.loc16: @F.%T.loc13 (%T) = value_param call_param1
// CHECK:STDOUT: %T.ref.loc16: type = name_ref T, %T.loc16 [symbolic = %T.loc13 (constants.%T)]
// CHECK:STDOUT: %n.loc16: @F.%T.loc13 (%T) = bind_name n, %n.param.loc16
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generic class @Class(%T.loc11_13.1: type) {
// CHECK:STDOUT: %T.loc11_13.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc11_13.2 (constants.%T)]
// CHECK:STDOUT: %T.patt.loc11_13.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc11_13.2 (constants.%T.patt)]
// CHECK:STDOUT:
// CHECK:STDOUT: !definition:
// CHECK:STDOUT: %require_complete: <witness> = require_complete_type @Class.%T.loc11_13.2 (%T) [symbolic = %require_complete (constants.%require_complete.4ae)]
// CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T.loc11_13.2) [symbolic = %Class (constants.%Class)]
// CHECK:STDOUT: %Class.elem: type = unbound_element_type @Class.%Class (%Class), @Class.%T.loc11_13.2 (%T) [symbolic = %Class.elem (constants.%Class.elem)]
// CHECK:STDOUT: %F.type: type = fn_type @F, @Class(%T.loc11_13.2) [symbolic = %F.type (constants.%F.type)]
// CHECK:STDOUT: %F: @Class.%F.type (%F.type) = struct_value () [symbolic = %F (constants.%F)]
// CHECK:STDOUT: %struct_type.a: type = struct_type {.a: @Class.%T.loc11_13.2 (%T)} [symbolic = %struct_type.a (constants.%struct_type.a)]
// CHECK:STDOUT: %complete_type.loc14_1.2: <witness> = complete_type_witness @Class.%struct_type.a (%struct_type.a) [symbolic = %complete_type.loc14_1.2 (constants.%complete_type)]
// CHECK:STDOUT:
// CHECK:STDOUT: class {
// CHECK:STDOUT: %.loc12_8: @Class.%Class.elem (%Class.elem) = field_decl a, element0 [concrete]
// CHECK:STDOUT: name_binding_decl {
// CHECK:STDOUT: %.loc12_3: @Class.%Class.elem (%Class.elem) = var_pattern %.loc12_8
// CHECK:STDOUT: }
// CHECK:STDOUT: %.var: ref @Class.%Class.elem (%Class.elem) = var <none>
// CHECK:STDOUT: %F.decl: @Class.%F.type (%F.type) = fn_decl @F [symbolic = @Class.%F (constants.%F)] {
// CHECK:STDOUT: %self.patt: @F.%Class (%Class) = binding_pattern self
// CHECK:STDOUT: %self.param_patt: @F.%Class (%Class) = value_param_pattern %self.patt, call_param0
// CHECK:STDOUT: %n.patt: @F.%T.loc13 (%T) = binding_pattern n
// CHECK:STDOUT: %n.param_patt: @F.%T.loc13 (%T) = value_param_pattern %n.patt, call_param1
// CHECK:STDOUT: } {
// CHECK:STDOUT: %self.param.loc13: @F.%Class (%Class) = value_param call_param0
// CHECK:STDOUT: %.loc13_14.1: type = splice_block %Self.ref.loc13 [symbolic = %Class (constants.%Class)] {
// CHECK:STDOUT: %.loc13_14.2: type = specific_constant constants.%Class, @Class(constants.%T) [symbolic = %Class (constants.%Class)]
// CHECK:STDOUT: %Self.ref.loc13: type = name_ref Self, %.loc13_14.2 [symbolic = %Class (constants.%Class)]
// CHECK:STDOUT: }
// CHECK:STDOUT: %self.loc13: @F.%Class (%Class) = bind_name self, %self.param.loc13
// CHECK:STDOUT: %n.param.loc13: @F.%T.loc13 (%T) = value_param call_param1
// CHECK:STDOUT: %T.ref.loc13: type = name_ref T, @Class.%T.loc11_13.1 [symbolic = %T.loc13 (constants.%T)]
// CHECK:STDOUT: %n.loc13: @F.%T.loc13 (%T) = bind_name n, %n.param.loc13
// CHECK:STDOUT: }
// CHECK:STDOUT: %complete_type.loc14_1.1: <witness> = complete_type_witness %struct_type.a [symbolic = %complete_type.loc14_1.2 (constants.%complete_type)]
// CHECK:STDOUT: complete_type_witness = %complete_type.loc14_1.1
// CHECK:STDOUT:
// CHECK:STDOUT: !members:
// CHECK:STDOUT: .Self = constants.%Class
// CHECK:STDOUT: .T = <poisoned>
// CHECK:STDOUT: .a = %.loc12_8
// CHECK:STDOUT: .F = %F.decl
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: generic fn @F(@Class.%T.loc11_13.1: type) {
// CHECK:STDOUT: %T.loc13: type = bind_symbolic_name T, 0 [symbolic = %T.loc13 (constants.%T)]
// CHECK:STDOUT: %Class: type = class_type @Class, @Class(%T.loc13) [symbolic = %Class (constants.%Class)]
// CHECK:STDOUT:
// CHECK:STDOUT: !definition:
// CHECK:STDOUT: %require_complete.loc16_26: <witness> = require_complete_type @F.%Class (%Class) [symbolic = %require_complete.loc16_26 (constants.%require_complete.4f8)]
// CHECK:STDOUT: %require_complete.loc16_35: <witness> = require_complete_type @F.%T.loc13 (%T) [symbolic = %require_complete.loc16_35 (constants.%require_complete.4ae)]
// CHECK:STDOUT:
// CHECK:STDOUT: fn[%self.param_patt: @F.%Class (%Class)](%n.param_patt: @F.%T.loc13 (%T)) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: specific @Class(constants.%T) {
// CHECK:STDOUT: %T.loc11_13.2 => constants.%T
// CHECK:STDOUT: %T.patt.loc11_13.2 => constants.%T
// CHECK:STDOUT:
// CHECK:STDOUT: !definition:
// CHECK:STDOUT: %require_complete => constants.%require_complete.4ae
// CHECK:STDOUT: %Class => constants.%Class
// CHECK:STDOUT: %Class.elem => constants.%Class.elem
// CHECK:STDOUT: %F.type => constants.%F.type
// CHECK:STDOUT: %F => constants.%F
// CHECK:STDOUT: %struct_type.a => constants.%struct_type.a
// CHECK:STDOUT: %complete_type.loc14_1.2 => constants.%complete_type
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: specific @F(constants.%T) {
// CHECK:STDOUT: %T.loc13 => constants.%T
// CHECK:STDOUT: %Class => constants.%Class
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: specific @Class(@F.%T.loc13) {}
// CHECK:STDOUT:
// CHECK:STDOUT: specific @Class(%T.loc11_13.2) {}
// CHECK:STDOUT: