diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 5dd8b00fcf4c..94906e100ec2 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -310,12 +310,36 @@ static auto ValidateForEntryPoint(Context& context, } } +static auto IsGenericFunction(Context& context, + SemIR::GenericId function_generic_id, + SemIR::GenericId class_generic_id) -> bool { + if (function_generic_id == SemIR::GenericId::None) { + return false; + } + + if (class_generic_id == SemIR::GenericId::None) { + return true; + } + + const auto& function_generic = context.generics().Get(function_generic_id); + const auto& class_generic = context.generics().Get(class_generic_id); + + auto function_bindings = + context.inst_blocks().Get(function_generic.bindings_id); + auto class_bindings = context.inst_blocks().Get(class_generic.bindings_id); + + // If the function's bindings are the same size as the class's bindings, + // then there are no extra bindings for the function, so it is effectively + // non-generic within the scope of a specific of the class. + return class_bindings.size() != function_bindings.size(); +} + // Requests a vtable be created when processing a virtual function. static auto RequestVtableIfVirtual( Context& context, Parse::AnyFunctionDeclId node_id, SemIR::Function::VirtualModifier virtual_modifier, - const std::optional& parent_scope_inst, SemIR::InstId decl_id) - -> void { + const std::optional& parent_scope_inst, SemIR::InstId decl_id, + SemIR::GenericId generic_id) -> void { // In order to request a vtable, the function must be virtual, and in a class // scope. if (virtual_modifier == SemIR::Function::VirtualModifier::None || @@ -333,6 +357,13 @@ static auto RequestVtableIfVirtual( CARBON_DIAGNOSTIC(ImplWithoutBase, Error, "impl without base class"); context.emitter().Emit(node_id, ImplWithoutBase); } + + if (IsGenericFunction(context, generic_id, class_info.generic_id)) { + CARBON_DIAGNOSTIC(GenericVirtual, Error, "generic virtual function"); + context.emitter().Emit(node_id, GenericVirtual); + return; + } + // TODO: If this is an `impl` function, check there's a matching base // function that's impl or virtual. class_info.is_dynamic = true; @@ -465,8 +496,6 @@ static auto BuildFunctionDecl(Context& context, SemIR::FunctionId::None, context.inst_block_stack().Pop()}; auto decl_id = AddPlaceholderInst(context, node_id, function_decl); - RequestVtableIfVirtual(context, node_id, virtual_modifier, parent_scope_inst, - decl_id); // Build the function entity. This will be merged into an existing function if // there is one, or otherwise added to the function store. @@ -509,6 +538,9 @@ static auto BuildFunctionDecl(Context& context, // TODO: Validate that the redeclaration doesn't set an access modifier. } + RequestVtableIfVirtual(context, node_id, virtual_modifier, parent_scope_inst, + decl_id, function_info.generic_id); + // Write the function ID into the FunctionDecl. ReplaceInstBeforeConstantUse(context, decl_id, function_decl); diff --git a/toolchain/check/testdata/class/virtual_modifiers.carbon b/toolchain/check/testdata/class/virtual_modifiers.carbon index 2c5edf1105fe..6c7a85a977b3 100644 --- a/toolchain/check/testdata/class/virtual_modifiers.carbon +++ b/toolchain/check/testdata/class/virtual_modifiers.carbon @@ -295,6 +295,47 @@ class T2 { impl fn F1[addr self: Self*](); } +// --- fail_generic_virtual.carbon + +library "[[@TEST_NAME]]"; + +base class T1 { + // CHECK:STDERR: fail_generic_virtual.carbon:[[@LINE+4]]:3: error: generic virtual function [GenericVirtual] + // CHECK:STDERR: virtual fn F[self: Self, T:! type](); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + virtual fn F[self: Self, T:! type](); +} + +// --- fail_generic_virtual_in_generic_class.carbon + +library "[[@TEST_NAME]]"; + +base class T1(T:! type) { + // CHECK:STDERR: fail_generic_virtual_in_generic_class.carbon:[[@LINE+4]]:3: error: generic virtual function [GenericVirtual] + // CHECK:STDERR: virtual fn F[self: Self, T:! type](); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + virtual fn F[self: Self, T:! type](); +} + +// --- generic_with_virtual.carbon + +library "[[@TEST_NAME]]"; + +base class T1(T:! type) { + virtual fn F[self: Self](); +} + +// --- with_dependent_arg.carbon + +library "[[@TEST_NAME]]"; + +base class T1(T:! type) { + virtual fn F[self: Self](t: T); +} + + // CHECK:STDOUT: --- modifiers.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -2012,3 +2053,340 @@ class T2 { // CHECK:STDOUT: // CHECK:STDOUT: impl fn @F1.2(%self.param: %ptr.63e); // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_generic_virtual.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %T1: type = class_type @T1 [concrete] +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %T.patt: type = symbolic_binding_pattern T, 0 [symbolic] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = 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 [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .T1 = %T1.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %T1.decl: type = class_decl @T1 [concrete = constants.%T1] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @T1 { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %self.patt: %T1 = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %T1 = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: %T.patt.loc9_28.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc9_28.2 (constants.%T.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %T1 = value_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%T1 [concrete = constants.%T1] +// CHECK:STDOUT: %self: %T1 = bind_name self, %self.param +// CHECK:STDOUT: %T.loc9_28.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc9_28.1 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%T1 +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic virtual fn @F(%T.loc9_28.2: type) { +// CHECK:STDOUT: %T.loc9_28.1: type = bind_symbolic_name T, 0 [symbolic = %T.loc9_28.1 (constants.%T)] +// CHECK:STDOUT: %T.patt.loc9_28.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc9_28.2 (constants.%T.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: virtual fn(%self.param: %T1); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%T) { +// CHECK:STDOUT: %T.loc9_28.1 => constants.%T +// CHECK:STDOUT: %T.patt.loc9_28.2 => constants.%T.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_generic_virtual_in_generic_class.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %T.8b3: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %T.patt.e01: type = symbolic_binding_pattern T, 0 [symbolic] +// CHECK:STDOUT: %T1.type: type = generic_class_type @T1 [concrete] +// CHECK:STDOUT: %T1.generic: %T1.type = struct_value () [concrete] +// CHECK:STDOUT: %T1: type = class_type @T1, @T1(%T.8b3) [symbolic] +// CHECK:STDOUT: %T.336: type = bind_symbolic_name T, 1 [symbolic] +// CHECK:STDOUT: %T.patt.7a9: type = symbolic_binding_pattern T, 1 [symbolic] +// CHECK:STDOUT: %F.type: type = fn_type @F, @T1(%T.8b3) [symbolic] +// CHECK:STDOUT: %F: %F.type = struct_value () [symbolic] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = 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 [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .T1 = %T1.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %T1.decl: %T1.type = class_decl @T1 [concrete = constants.%T1.generic] { +// CHECK:STDOUT: %T.patt.loc4_15.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_15.2 (constants.%T.patt.e01)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %T.loc4_15.1: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_15.2 (constants.%T.8b3)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @T1(%T.loc4_15.1: type) { +// CHECK:STDOUT: %T.loc4_15.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_15.2 (constants.%T.8b3)] +// CHECK:STDOUT: %T.patt.loc4_15.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_15.2 (constants.%T.patt.e01)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %F.type: type = fn_type @F, @T1(%T.loc4_15.2) [symbolic = %F.type (constants.%F.type)] +// CHECK:STDOUT: %F: @T1.%F.type (%F.type) = struct_value () [symbolic = %F (constants.%F)] +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %F.decl: @T1.%F.type (%F.type) = fn_decl @F [symbolic = @T1.%F (constants.%F)] { +// CHECK:STDOUT: %self.patt: @F.%T1 (%T1) = binding_pattern self +// CHECK:STDOUT: %self.param_patt: @F.%T1 (%T1) = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: %T.patt.loc9_28.1: type = symbolic_binding_pattern T, 1 [symbolic = %T.patt.loc9_28.2 (constants.%T.patt.7a9)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: @F.%T1 (%T1) = value_param call_param0 +// CHECK:STDOUT: %.loc9_22.1: type = splice_block %Self.ref [symbolic = %T1 (constants.%T1)] { +// CHECK:STDOUT: %.loc9_22.2: type = specific_constant constants.%T1, @T1(constants.%T.8b3) [symbolic = %T1 (constants.%T1)] +// CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc9_22.2 [symbolic = %T1 (constants.%T1)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %self: @F.%T1 (%T1) = bind_name self, %self.param +// CHECK:STDOUT: %T.loc9_28.2: type = bind_symbolic_name T, 1 [symbolic = %T.loc9_28.1 (constants.%T.336)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%T1 +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic virtual fn @F(@T1.%T.loc4_15.1: type, %T.loc9_28.2: type) { +// CHECK:STDOUT: %T.loc9_22: type = bind_symbolic_name T, 0 [symbolic = %T.loc9_22 (constants.%T.8b3)] +// CHECK:STDOUT: %T1: type = class_type @T1, @T1(%T.loc9_22) [symbolic = %T1 (constants.%T1)] +// CHECK:STDOUT: %T.loc9_28.1: type = bind_symbolic_name T, 1 [symbolic = %T.loc9_28.1 (constants.%T.336)] +// CHECK:STDOUT: %T.patt.loc9_28.2: type = symbolic_binding_pattern T, 1 [symbolic = %T.patt.loc9_28.2 (constants.%T.patt.7a9)] +// CHECK:STDOUT: +// CHECK:STDOUT: virtual fn(%self.param: @F.%T1 (%T1)); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1(constants.%T.8b3) { +// CHECK:STDOUT: %T.loc4_15.2 => constants.%T.8b3 +// CHECK:STDOUT: %T.patt.loc4_15.2 => constants.%T.patt.e01 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%T.8b3, constants.%T.336) { +// CHECK:STDOUT: %T.loc9_22 => constants.%T.8b3 +// CHECK:STDOUT: %T1 => constants.%T1 +// CHECK:STDOUT: %T.loc9_28.1 => constants.%T.336 +// CHECK:STDOUT: %T.patt.loc9_28.2 => constants.%T.patt.7a9 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1(@F.%T.loc9_22) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1(%T.loc4_15.2) {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- generic_with_virtual.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: %T1.type: type = generic_class_type @T1 [concrete] +// CHECK:STDOUT: %T1.generic: %T1.type = struct_value () [concrete] +// CHECK:STDOUT: %T1: type = class_type @T1, @T1(%T) [symbolic] +// CHECK:STDOUT: %F.type: type = fn_type @F, @T1(%T) [symbolic] +// CHECK:STDOUT: %F: %F.type = struct_value () [symbolic] +// CHECK:STDOUT: %ptr: type = ptr_type [concrete] +// CHECK:STDOUT: %.db6: = vtable (%F) [symbolic] +// CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = 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 [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .T1 = %T1.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %T1.decl: %T1.type = class_decl @T1 [concrete = constants.%T1.generic] { +// CHECK:STDOUT: %T.patt.loc4_15.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_15.2 (constants.%T.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %T.loc4_15.1: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_15.2 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @T1(%T.loc4_15.1: type) { +// CHECK:STDOUT: %T.loc4_15.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_15.2 (constants.%T)] +// CHECK:STDOUT: %T.patt.loc4_15.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_15.2 (constants.%T.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %F.type: type = fn_type @F, @T1(%T.loc4_15.2) [symbolic = %F.type (constants.%F.type)] +// CHECK:STDOUT: %F: @T1.%F.type (%F.type) = struct_value () [symbolic = %F (constants.%F)] +// CHECK:STDOUT: %.loc6_1.2: = vtable (%F) [symbolic = %.loc6_1.2 (constants.%.db6)] +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %F.decl: @T1.%F.type (%F.type) = fn_decl @F [symbolic = @T1.%F (constants.%F)] { +// CHECK:STDOUT: %self.patt: @F.%T1 (%T1) = binding_pattern self +// CHECK:STDOUT: %self.param_patt: @F.%T1 (%T1) = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: @F.%T1 (%T1) = value_param call_param0 +// CHECK:STDOUT: %.loc5_22.1: type = splice_block %Self.ref [symbolic = %T1 (constants.%T1)] { +// CHECK:STDOUT: %.loc5_22.2: type = specific_constant constants.%T1, @T1(constants.%T) [symbolic = %T1 (constants.%T1)] +// CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_22.2 [symbolic = %T1 (constants.%T1)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %self: @F.%T1 (%T1) = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc6_1.1: = vtable (%F.decl) [symbolic = %.loc6_1.2 (constants.%.db6)] +// CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr} [concrete = constants.%struct_type.vptr] +// CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%T1 +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic virtual fn @F(@T1.%T.loc4_15.1: type) { +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %T1: type = class_type @T1, @T1(%T) [symbolic = %T1 (constants.%T1)] +// CHECK:STDOUT: +// CHECK:STDOUT: virtual fn(%self.param: @F.%T1 (%T1)); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1(constants.%T) { +// CHECK:STDOUT: %T.loc4_15.2 => constants.%T +// CHECK:STDOUT: %T.patt.loc4_15.2 => constants.%T.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%T) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: %T1 => constants.%T1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1(@F.%T) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1(%T.loc4_15.2) {} +// CHECK:STDOUT: +// CHECK:STDOUT: --- with_dependent_arg.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: %T1.type: type = generic_class_type @T1 [concrete] +// CHECK:STDOUT: %T1.generic: %T1.type = struct_value () [concrete] +// CHECK:STDOUT: %T1: type = class_type @T1, @T1(%T) [symbolic] +// CHECK:STDOUT: %F.type: type = fn_type @F, @T1(%T) [symbolic] +// CHECK:STDOUT: %F: %F.type = struct_value () [symbolic] +// CHECK:STDOUT: %ptr: type = ptr_type [concrete] +// CHECK:STDOUT: %.db6: = vtable (%F) [symbolic] +// CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = 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 [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .T1 = %T1.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %T1.decl: %T1.type = class_decl @T1 [concrete = constants.%T1.generic] { +// CHECK:STDOUT: %T.patt.loc4_15.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_15.2 (constants.%T.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %T.loc4_15.1: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_15.2 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic class @T1(%T.loc4_15.1: type) { +// CHECK:STDOUT: %T.loc4_15.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc4_15.2 (constants.%T)] +// CHECK:STDOUT: %T.patt.loc4_15.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc4_15.2 (constants.%T.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: !definition: +// CHECK:STDOUT: %F.type: type = fn_type @F, @T1(%T.loc4_15.2) [symbolic = %F.type (constants.%F.type)] +// CHECK:STDOUT: %F: @T1.%F.type (%F.type) = struct_value () [symbolic = %F (constants.%F)] +// CHECK:STDOUT: %.loc6_1.2: = vtable (%F) [symbolic = %.loc6_1.2 (constants.%.db6)] +// CHECK:STDOUT: +// CHECK:STDOUT: class { +// CHECK:STDOUT: %F.decl: @T1.%F.type (%F.type) = fn_decl @F [symbolic = @T1.%F (constants.%F)] { +// CHECK:STDOUT: %self.patt: @F.%T1 (%T1) = binding_pattern self +// CHECK:STDOUT: %self.param_patt: @F.%T1 (%T1) = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: %t.patt: @F.%T (%T) = binding_pattern t +// CHECK:STDOUT: %t.param_patt: @F.%T (%T) = value_param_pattern %t.patt, call_param1 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: @F.%T1 (%T1) = value_param call_param0 +// CHECK:STDOUT: %.loc5_22.1: type = splice_block %Self.ref [symbolic = %T1 (constants.%T1)] { +// CHECK:STDOUT: %.loc5_22.2: type = specific_constant constants.%T1, @T1(constants.%T) [symbolic = %T1 (constants.%T1)] +// CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc5_22.2 [symbolic = %T1 (constants.%T1)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %self: @F.%T1 (%T1) = bind_name self, %self.param +// CHECK:STDOUT: %t.param: @F.%T (%T) = value_param call_param1 +// CHECK:STDOUT: %T.ref: type = name_ref T, @T1.%T.loc4_15.1 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %t: @F.%T (%T) = bind_name t, %t.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc6_1.1: = vtable (%F.decl) [symbolic = %.loc6_1.2 (constants.%.db6)] +// CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr} [concrete = constants.%struct_type.vptr] +// CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%T1 +// CHECK:STDOUT: .T = +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic virtual fn @F(@T1.%T.loc4_15.1: type) { +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %T1: type = class_type @T1, @T1(%T) [symbolic = %T1 (constants.%T1)] +// CHECK:STDOUT: +// CHECK:STDOUT: virtual fn(%self.param: @F.%T1 (%T1), %t.param: @F.%T (%T)); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1(constants.%T) { +// CHECK:STDOUT: %T.loc4_15.2 => constants.%T +// CHECK:STDOUT: %T.patt.loc4_15.2 => constants.%T.patt +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @F(constants.%T) { +// CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: %T1 => constants.%T1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1(@F.%T) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @T1(%T.loc4_15.2) {} +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 3866e5fe1641..2220f07c3854 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -265,6 +265,7 @@ CARBON_DIAGNOSTIC_KIND(ClassForwardDeclaredHere) CARBON_DIAGNOSTIC_KIND(ClassSpecificDeclOutsideClass) CARBON_DIAGNOSTIC_KIND(ClassSpecificDeclPrevious) CARBON_DIAGNOSTIC_KIND(ClassIncompleteWithinDefinition) +CARBON_DIAGNOSTIC_KIND(GenericVirtual) CARBON_DIAGNOSTIC_KIND(ImplWithoutBase) CARBON_DIAGNOSTIC_KIND(VirtualWithoutSelf)