From f97f1a3e11f0717ded8a0f9e5148f1316ba706c7 Mon Sep 17 00:00:00 2001 From: David Blaikie Date: Thu, 27 Feb 2025 09:01:29 -0800 Subject: [PATCH] Add error for virtual member function without self (#5005) This tripped over a lowering crash when a member function with self was declared-but-not-defined, so that's why some test cases were updated to have (empty) function definitions. I'll follow-up with/look into a fix for the self-declared-but-not-defined cases separately. --------- Co-authored-by: Jon Ross-Perkins Co-authored-by: Carbon Infra Bot --- toolchain/check/function.cpp | 4 +- toolchain/check/function.h | 24 +- toolchain/check/handle_class.cpp | 3 +- toolchain/check/handle_function.cpp | 5 + toolchain/check/handle_impl.cpp | 3 +- toolchain/check/impl.cpp | 4 +- toolchain/check/merge.cpp | 78 +-- toolchain/check/merge.h | 17 +- .../class/adapter/fail_adapt_with_base.carbon | 17 +- .../testdata/class/fail_modifiers.carbon | 53 +- .../testdata/class/virtual_modifiers.carbon | 559 +++++++++++++++--- toolchain/diagnostics/diagnostic_kind.def | 1 + toolchain/lower/testdata/class/virtual.carbon | 75 ++- 13 files changed, 649 insertions(+), 194 deletions(-) diff --git a/toolchain/check/function.cpp b/toolchain/check/function.cpp index 901e4e8fd947..2d6468301d7d 100644 --- a/toolchain/check/function.cpp +++ b/toolchain/check/function.cpp @@ -14,7 +14,7 @@ auto CheckFunctionTypeMatches(Context& context, const SemIR::Function& new_function, const SemIR::Function& prev_function, SemIR::SpecificId prev_specific_id, - bool check_syntax) -> bool { + bool check_syntax, bool check_self) -> bool { // TODO: When check_syntax is false, the functions should be allowed to have // different signatures as long as we can synthesize a suitable thunk. i.e., // when there's an implicit conversion from the original parameter types to @@ -23,7 +23,7 @@ auto CheckFunctionTypeMatches(Context& context, // Also, build that thunk. if (!CheckRedeclParamsMatch(context, DeclParams(new_function), DeclParams(prev_function), prev_specific_id, - check_syntax)) { + /*diagnose=*/true, check_syntax, check_self)) { return false; } diff --git a/toolchain/check/function.h b/toolchain/check/function.h index 346c3cb6749b..fe697675b8bc 100644 --- a/toolchain/check/function.h +++ b/toolchain/check/function.h @@ -29,11 +29,25 @@ struct SuspendedFunction { // Checks that `new_function` has the same parameter types and return type as // `prev_function`, or if `prev_function_id` is specified, a specific version of // `prev_function`. Prints a suitable diagnostic and returns false if not. -auto CheckFunctionTypeMatches( - Context& context, const SemIR::Function& new_function, - const SemIR::Function& prev_function, - SemIR::SpecificId prev_specific_id = SemIR::SpecificId::None, - bool check_syntax = true) -> bool; +// +// `check_syntax` is false if the redeclaration can be called via a thunk with +// implicit conversions from the original declaration. +// `check_self` is false if the self declaration does not have to match (for +// instance in impls of virtual functions). +auto CheckFunctionTypeMatches(Context& context, + const SemIR::Function& new_function, + const SemIR::Function& prev_function, + SemIR::SpecificId prev_specific_id, + bool check_syntax, bool check_self) -> bool; + +inline auto CheckFunctionTypeMatches(Context& context, + const SemIR::Function& new_function, + const SemIR::Function& prev_function) + -> bool { + return CheckFunctionTypeMatches(context, new_function, prev_function, + SemIR::SpecificId::None, + /*check_syntax=*/true, /*check_self=*/true); +} // Checks that the return type of the specified function is complete, issuing an // error if not. This computes the return slot usage for the function if diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index 7716ecb7eba9..486329c72a2a 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -736,7 +736,8 @@ static auto CheckCompleteClassType(Context& context, Parse::NodeId node_id, // `SpecificId::None`. CheckFunctionTypeMatches(context, override_fn, fn, SemIR::SpecificId::None, - /*check_syntax=*/false); + /*check_syntax=*/false, + /*check_self=*/false); fn_decl_id = override_fn_decl_id; } } diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index a8caf9da9e56..3cec9eedec7f 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -278,6 +278,11 @@ static auto BuildFunctionDecl(Context& context, self_param_id = *i; } + if (virtual_modifier != SemIR::Function::VirtualModifier::None && + !self_param_id.has_value()) { + CARBON_DIAGNOSTIC(VirtualWithoutSelf, Error, "virtual class function"); + context.emitter().Build(node_id, VirtualWithoutSelf).Emit(); + } // Build the function entity. This will be merged into an existing function if // there is one, or otherwise added to the function store. auto function_info = diff --git a/toolchain/check/handle_impl.cpp b/toolchain/check/handle_impl.cpp index 8fecb950ca75..606b27260140 100644 --- a/toolchain/check/handle_impl.cpp +++ b/toolchain/check/handle_impl.cpp @@ -254,7 +254,8 @@ static auto MergeImplRedecl(Context& context, SemIR::Impl& new_impl, // `impl`. Keep looking for a prior declaration without issuing a diagnostic. if (!CheckRedeclParamsMatch(context, DeclParams(new_impl), DeclParams(prev_impl), SemIR::SpecificId::None, - /*check_syntax=*/true, /*diagnose=*/false)) { + /*diagnose=*/false, /*check_syntax=*/true, + /*check_self=*/true)) { // NOLINTNEXTLINE(readability-simplify-boolean-expr) return false; } diff --git a/toolchain/check/impl.cpp b/toolchain/check/impl.cpp index aec86c4058b9..c257231b740b 100644 --- a/toolchain/check/impl.cpp +++ b/toolchain/check/impl.cpp @@ -72,8 +72,8 @@ static auto CheckAssociatedFunctionImplementation( if (!CheckFunctionTypeMatches( context, context.functions().Get(impl_function_decl->function_id), context.functions().Get(interface_function_type.function_id), - interface_function_specific_id, - /*check_syntax=*/false)) { + interface_function_specific_id, /*check_syntax=*/false, + /*check_self=*/true)) { return SemIR::ErrorInst::SingletonInstId; } return impl_decl_id; diff --git a/toolchain/check/merge.cpp b/toolchain/check/merge.cpp index 82481ad3464a..04cde311d1e4 100644 --- a/toolchain/check/merge.cpp +++ b/toolchain/check/merge.cpp @@ -201,8 +201,8 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param, int32_t param_index, SemIR::InstId new_param_pattern_id, SemIR::InstId prev_param_pattern_id, - SemIR::SpecificId prev_specific_id, bool diagnose) - -> bool { + SemIR::SpecificId prev_specific_id, bool diagnose, + bool check_self) -> bool { // TODO: Consider differentiating between type and name mistakes. For now, // taking the simpler approach because I also think we may want to refactor // params. @@ -231,25 +231,6 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param, return false; } - auto prev_param_type_id = SemIR::GetTypeInSpecific( - context.sem_ir(), prev_specific_id, prev_param_pattern.type_id()); - if (!context.types().AreEqualAcrossDeclarations(new_param_pattern.type_id(), - prev_param_type_id)) { - if (!diagnose) { - return false; - } - CARBON_DIAGNOSTIC(RedeclParamDiffersType, Error, - "type {3} of {0:implicit |}parameter {1} in " - "redeclaration differs from previous parameter type {2}", - BoolAsSelect, int32_t, SemIR::TypeId, SemIR::TypeId); - context.emitter() - .Build(new_param_pattern_id, RedeclParamDiffersType, is_implicit_param, - param_index + 1, prev_param_type_id, new_param_pattern.type_id()) - .Note(prev_param_pattern_id, RedeclParamPrevious, is_implicit_param) - .Emit(); - return false; - } - if (new_param_pattern.Is()) { new_param_pattern = context.insts().Get( new_param_pattern.As().inner_id); @@ -270,11 +251,40 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param, return false; } - auto new_entity_name = context.entity_names().Get( - new_param_pattern.As().entity_name_id); - auto prev_entity_name = context.entity_names().Get( - prev_param_pattern.As().entity_name_id); - if (new_entity_name.name_id != prev_entity_name.name_id) { + auto new_name_id = + context.entity_names() + .Get(new_param_pattern.As().entity_name_id) + .name_id; + auto prev_name_id = + context.entity_names() + .Get(prev_param_pattern.As().entity_name_id) + .name_id; + + if (!check_self && new_name_id == SemIR::NameId::SelfValue && + prev_name_id == SemIR::NameId::SelfValue) { + return true; + } + + auto prev_param_type_id = SemIR::GetTypeInSpecific( + context.sem_ir(), prev_specific_id, prev_param_pattern.type_id()); + if (!context.types().AreEqualAcrossDeclarations(new_param_pattern.type_id(), + prev_param_type_id)) { + if (!diagnose) { + return false; + } + CARBON_DIAGNOSTIC(RedeclParamDiffersType, Error, + "type {3} of {0:implicit |}parameter {1} in " + "redeclaration differs from previous parameter type {2}", + BoolAsSelect, int32_t, SemIR::TypeId, SemIR::TypeId); + context.emitter() + .Build(new_param_pattern_id, RedeclParamDiffersType, is_implicit_param, + param_index + 1, prev_param_type_id, new_param_pattern.type_id()) + .Note(prev_param_pattern_id, RedeclParamPrevious, is_implicit_param) + .Emit(); + return false; + } + + if (new_name_id != prev_name_id) { emit_diagnostic(); return false; } @@ -288,8 +298,8 @@ static auto CheckRedeclParams(Context& context, SemIRLoc new_decl_loc, SemIRLoc prev_decl_loc, SemIR::InstBlockId prev_param_patterns_id, bool is_implicit_param, - SemIR::SpecificId prev_specific_id, bool diagnose) - -> bool { + SemIR::SpecificId prev_specific_id, bool diagnose, + bool check_self) -> bool { // This will often occur for empty params. if (new_param_patterns_id == prev_param_patterns_id) { return true; @@ -347,7 +357,7 @@ static auto CheckRedeclParams(Context& context, SemIRLoc new_decl_loc, llvm::enumerate(new_param_pattern_ids, prev_param_pattern_ids)) { if (!CheckRedeclParam(context, is_implicit_param, index, new_param_pattern_id, prev_param_pattern_id, - prev_specific_id, diagnose)) { + prev_specific_id, diagnose, check_self)) { return false; } } @@ -458,8 +468,8 @@ static auto CheckRedeclParamSyntax(Context& context, auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity, const DeclParams& prev_entity, - SemIR::SpecificId prev_specific_id, - bool check_syntax, bool diagnose) -> bool { + SemIR::SpecificId prev_specific_id, bool diagnose, + bool check_syntax, bool check_self) -> bool { if (EntityHasParamError(context, new_entity) || EntityHasParamError(context, prev_entity)) { return false; @@ -467,13 +477,15 @@ auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity, if (!CheckRedeclParams( context, new_entity.loc, new_entity.implicit_param_patterns_id, prev_entity.loc, prev_entity.implicit_param_patterns_id, - /*is_implicit_param=*/true, prev_specific_id, diagnose)) { + /*is_implicit_param=*/true, prev_specific_id, diagnose, check_self)) { return false; } + // Don't forward `check_self` here because it's extra cost, and `self` is only + // allowed in implicit params. if (!CheckRedeclParams(context, new_entity.loc, new_entity.param_patterns_id, prev_entity.loc, prev_entity.param_patterns_id, /*is_implicit_param=*/false, prev_specific_id, - diagnose)) { + diagnose, /*check_self=*/true)) { return false; } if (check_syntax && diff --git a/toolchain/check/merge.h b/toolchain/check/merge.h index 677eafe1aceb..fe659c7a8fc8 100644 --- a/toolchain/check/merge.h +++ b/toolchain/check/merge.h @@ -96,11 +96,18 @@ struct DeclParams { // Checks that the parameters in a redeclaration of an entity match the // parameters in the prior declaration. If not, produces a diagnostic if // `diagnose` is true, and returns false. -auto CheckRedeclParamsMatch( - Context& context, const DeclParams& new_entity, - const DeclParams& prev_entity, - SemIR::SpecificId prev_specific_id = SemIR::SpecificId::None, - bool check_syntax = true, bool diagnose = true) -> bool; +auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity, + const DeclParams& prev_entity, + SemIR::SpecificId prev_specific_id, bool diagnose, + bool check_syntax, bool check_self) -> bool; + +inline auto CheckRedeclParamsMatch(Context& context, + const DeclParams& new_entity, + const DeclParams& prev_entity) -> bool { + return CheckRedeclParamsMatch(context, new_entity, prev_entity, + SemIR::SpecificId::None, /*diagnose=*/true, + /*check_syntax=*/true, /*check_self=*/true); +} } // namespace Carbon::Check diff --git a/toolchain/check/testdata/class/adapter/fail_adapt_with_base.carbon b/toolchain/check/testdata/class/adapter/fail_adapt_with_base.carbon index 5352d64562d5..356f2acd9e59 100644 --- a/toolchain/check/testdata/class/adapter/fail_adapt_with_base.carbon +++ b/toolchain/check/testdata/class/adapter/fail_adapt_with_base.carbon @@ -9,13 +9,13 @@ // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/adapter/fail_adapt_with_base.carbon class Simple {}; base class AdaptWithVirtual { - virtual fn F(); + virtual fn F[self: Self](); // CHECK:STDERR: fail_adapt_with_base.carbon:[[@LINE+7]]:3: error: adapter with virtual function [AdaptWithVirtual] // CHECK:STDERR: adapt Simple; // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fail_adapt_with_base.carbon:[[@LINE-4]]:3: note: first virtual function declaration is here [AdaptWithVirtualHere] - // CHECK:STDERR: virtual fn F(); - // CHECK:STDERR: ^~~~~~~~~~~~~~~ + // CHECK:STDERR: virtual fn F[self: Self](); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: adapt Simple; } @@ -58,7 +58,14 @@ base class AdaptWithVirtual { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AdaptWithVirtual { -// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %self.patt: %AdaptWithVirtual = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %AdaptWithVirtual = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %AdaptWithVirtual = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%AdaptWithVirtual [concrete = constants.%AdaptWithVirtual] +// CHECK:STDOUT: %self: %AdaptWithVirtual = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %Simple.ref: type = name_ref Simple, file.%Simple.decl [concrete = constants.%Simple] // CHECK:STDOUT: adapt_decl %Simple.ref [concrete] // CHECK:STDOUT: complete_type_witness = @@ -69,5 +76,5 @@ base class AdaptWithVirtual { // CHECK:STDOUT: .Simple = // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: virtual fn @F(); +// CHECK:STDOUT: virtual fn @F[%self.param_patt: %AdaptWithVirtual](); // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_modifiers.carbon b/toolchain/check/testdata/class/fail_modifiers.carbon index 63c56a07c643..68c6589b4b43 100644 --- a/toolchain/check/testdata/class/fail_modifiers.carbon +++ b/toolchain/check/testdata/class/fail_modifiers.carbon @@ -83,18 +83,18 @@ abstract protected class WrongOrder { } abstract base class AbstractAndBase {} abstract class AbstractWithDefinition { - // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:19: error: definition of `abstract` function [DefinedAbstractFunction] - // CHECK:STDERR: abstract fn F() {} - // CHECK:STDERR: ^ + // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:31: error: definition of `abstract` function [DefinedAbstractFunction] + // CHECK:STDERR: abstract fn F[self: Self]() {} + // CHECK:STDERR: ^ // CHECK:STDERR: - abstract fn F() {} - abstract fn G(); + abstract fn F[self: Self]() {} + abstract fn G[self: Self](); } -// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:31: error: definition of `abstract` function [DefinedAbstractFunction] -// CHECK:STDERR: fn AbstractWithDefinition.G() { -// CHECK:STDERR: ^ +// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:43: error: definition of `abstract` function [DefinedAbstractFunction] +// CHECK:STDERR: fn AbstractWithDefinition.G[self: Self]() { +// CHECK:STDERR: ^ // CHECK:STDERR: -fn AbstractWithDefinition.G() { +fn AbstractWithDefinition.G[self: Self]() { } // CHECK:STDOUT: --- fail_modifiers.carbon @@ -115,9 +115,9 @@ fn AbstractWithDefinition.G() { // CHECK:STDOUT: %F: %F.type = struct_value () [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] -// CHECK:STDOUT: %ptr: type = ptr_type [concrete] +// CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] // CHECK:STDOUT: %.9a5: = vtable (%F, %G) [concrete] -// CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr} [concrete] +// CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] // CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: @@ -151,7 +151,14 @@ fn AbstractWithDefinition.G() { // CHECK:STDOUT: %WrongOrder.decl: type = class_decl @WrongOrder [concrete = constants.%WrongOrder] {} {} // CHECK:STDOUT: %AbstractAndBase.decl: type = class_decl @AbstractAndBase [concrete = constants.%AbstractAndBase] {} {} // CHECK:STDOUT: %AbstractWithDefinition.decl: type = class_decl @AbstractWithDefinition [concrete = constants.%AbstractWithDefinition] {} {} -// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { +// CHECK:STDOUT: %self.patt: %AbstractWithDefinition = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %AbstractWithDefinition = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param.loc97: %AbstractWithDefinition = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref.loc97: type = name_ref Self, constants.%AbstractWithDefinition [concrete = constants.%AbstractWithDefinition] +// CHECK:STDOUT: %self.loc97: %AbstractWithDefinition = bind_name self, %self.param.loc97 +// CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @DuplicatePrivate; @@ -195,8 +202,22 @@ fn AbstractWithDefinition.G() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AbstractWithDefinition { -// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} -// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %self.patt: %AbstractWithDefinition = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %AbstractWithDefinition = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %AbstractWithDefinition = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%AbstractWithDefinition [concrete = constants.%AbstractWithDefinition] +// CHECK:STDOUT: %self: %AbstractWithDefinition = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { +// CHECK:STDOUT: %self.patt: %AbstractWithDefinition = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %AbstractWithDefinition = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param.loc91: %AbstractWithDefinition = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref.loc91: type = name_ref Self, constants.%AbstractWithDefinition [concrete = constants.%AbstractWithDefinition] +// CHECK:STDOUT: %self.loc91: %AbstractWithDefinition = bind_name self, %self.param.loc91 +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc92: = vtable (%F.decl, %G.decl) [concrete = constants.%.9a5] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -207,12 +228,12 @@ fn AbstractWithDefinition.G() { // CHECK:STDOUT: .G = %G.decl // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: abstract fn @F() { +// CHECK:STDOUT: abstract fn @F[%self.param_patt: %AbstractWithDefinition]() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: abstract fn @G() { +// CHECK:STDOUT: abstract fn @G[%self.param_patt: %AbstractWithDefinition]() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/check/testdata/class/virtual_modifiers.carbon b/toolchain/check/testdata/class/virtual_modifiers.carbon index a5183db06bf1..65d7e28d7987 100644 --- a/toolchain/check/testdata/class/virtual_modifiers.carbon +++ b/toolchain/check/testdata/class/virtual_modifiers.carbon @@ -13,13 +13,13 @@ package Modifiers; base class Base { - virtual fn H(); + virtual fn H[self: Self](); } abstract class Abstract { - abstract fn J(); + abstract fn J[self: Self](); - virtual fn K(); + virtual fn K[self: Self](); } // --- override_import.carbon @@ -30,7 +30,7 @@ import Modifiers; class Derived { extend base: Modifiers.Base; - impl fn H(); + impl fn H[self: Self](); } // --- todo_fail_later_base.carbon @@ -40,7 +40,7 @@ library "[[@TEST_NAME]]"; import Modifiers; base class Derived { - virtual fn F(); + virtual fn F[self: Self](); extend base: Modifiers.Base; } @@ -59,12 +59,12 @@ fn F() { library "[[@TEST_NAME]]"; abstract class A1 { - virtual fn F(); + virtual fn F[self: Self](); } abstract class A2 { extend base: A1; - impl fn F(); + impl fn F[self: Self](); } // --- impl_base.carbon @@ -72,17 +72,17 @@ abstract class A2 { library "[[@TEST_NAME]]"; base class B1 { - virtual fn F(); + virtual fn F[self: Self](); } base class B2 { extend base: B1; - impl fn F(); + impl fn F[self: Self](); } class C { extend base: B2; - impl fn F(); + impl fn F[self: Self](); } // --- fail_modifiers.carbon @@ -91,10 +91,10 @@ library "[[@TEST_NAME]]"; class C { // CHECK:STDERR: fail_modifiers.carbon:[[@LINE+4]]:3: error: impl without base class [ImplWithoutBase] - // CHECK:STDERR: impl fn F(); - // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: impl fn F[self: Self](); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: - impl fn F(); + impl fn F[self: Self](); } // --- init_members.carbon @@ -105,7 +105,7 @@ base class Base { var m1: i32; var m2: i32; - virtual fn F(); + virtual fn F[self: Self](); } fn F() { @@ -127,7 +127,7 @@ base class Base { class Derived { extend base: Base; - impl fn F(); + impl fn F[self: Self](); } // --- abstract_impl.carbon @@ -135,7 +135,7 @@ class Derived { library "[[@TEST_NAME]]"; abstract class AbstractBase { - abstract fn F(); + abstract fn F[self: Self](); } abstract class AbstractIntermediate { @@ -144,7 +144,7 @@ abstract class AbstractIntermediate { class Derived { extend base: AbstractIntermediate; - impl fn F(); + impl fn F[self: Self](); } // --- virtual_impl.carbon @@ -152,7 +152,7 @@ class Derived { library "[[@TEST_NAME]]"; base class VirtualBase { - virtual fn F(); + virtual fn F[self: Self](); } base class VirtualIntermediate { @@ -161,7 +161,7 @@ base class VirtualIntermediate { class Derived { extend base: VirtualIntermediate; - impl fn F(); + impl fn F[self: Self](); } // --- fail_impl_mismatch.carbon @@ -169,19 +169,19 @@ class Derived { library "[[@TEST_NAME]]"; base class Base { - virtual fn F(); + virtual fn F[self: Self](); } class Derived { extend base: Base; // CHECK:STDERR: fail_impl_mismatch.carbon:[[@LINE+7]]:3: error: redeclaration differs because of parameter count of 1 [RedeclParamCountDiffers] - // CHECK:STDERR: impl fn F(v: i32); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: impl fn F[self: Self](v: i32); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_impl_mismatch.carbon:[[@LINE-8]]:3: note: previously declared with parameter count of 0 [RedeclParamCountPrevious] - // CHECK:STDERR: virtual fn F(); - // CHECK:STDERR: ^~~~~~~~~~~~~~~ + // CHECK:STDERR: virtual fn F[self: Self](); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: - impl fn F(v: i32); + impl fn F[self: Self](v: i32); } // --- fail_todo_impl_conversion.carbon @@ -201,19 +201,19 @@ impl T2 as Core.ImplicitAs(T1) { } base class Base { - virtual fn F() -> T1; + virtual fn F[self: Self]() -> T1; } class Derived { extend base: Base; // CHECK:STDERR: fail_todo_impl_conversion.carbon:[[@LINE+7]]:3: error: function redeclaration differs because return type is `T2` [FunctionRedeclReturnTypeDiffers] - // CHECK:STDERR: impl fn F() -> T2; - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: impl fn F[self: Self]() -> T2; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: fail_todo_impl_conversion.carbon:[[@LINE-8]]:3: note: previously declared with return type `T1` [FunctionRedeclReturnTypePrevious] - // CHECK:STDERR: virtual fn F() -> T1; - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: virtual fn F[self: Self]() -> T1; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: - impl fn F() -> T2; + impl fn F[self: Self]() -> T2; } // --- fail_todo_impl_generic_base.carbon @@ -224,19 +224,65 @@ class T1 { } base class Base(T:! type) { - virtual fn F(t: T); + virtual fn F[self: Self](t: T); } class Derived { extend base: Base(T1); - // CHECK:STDERR: fail_todo_impl_generic_base.carbon:[[@LINE+7]]:13: error: type `T1` of parameter 1 in redeclaration differs from previous parameter type `T` [RedeclParamDiffersType] - // CHECK:STDERR: impl fn F(t: T1); - // CHECK:STDERR: ^~~~~ - // CHECK:STDERR: fail_todo_impl_generic_base.carbon:[[@LINE-8]]:16: note: previous declaration's corresponding parameter here [RedeclParamPrevious] - // CHECK:STDERR: virtual fn F(t: T); - // CHECK:STDERR: ^~~~ + // CHECK:STDERR: fail_todo_impl_generic_base.carbon:[[@LINE+7]]:25: error: type `T1` of parameter 1 in redeclaration differs from previous parameter type `T` [RedeclParamDiffersType] + // CHECK:STDERR: impl fn F[self: Self](t: T1); + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: fail_todo_impl_generic_base.carbon:[[@LINE-8]]:28: note: previous declaration's corresponding parameter here [RedeclParamPrevious] + // CHECK:STDERR: virtual fn F[self: Self](t: T); + // CHECK:STDERR: ^~~~ // CHECK:STDERR: - impl fn F(t: T1); + impl fn F[self: Self](t: T1); +} + +// --- fail_virtual_without_self.carbon + +library "[[@TEST_NAME]]"; + +abstract class T1 { + // CHECK:STDERR: fail_virtual_without_self.carbon:[[@LINE+4]]:3: error: virtual class function [VirtualWithoutSelf] + // CHECK:STDERR: virtual fn F(); + // CHECK:STDERR: ^~~~~~~~~~~~~~~ + // CHECK:STDERR: + virtual fn F(); + // CHECK:STDERR: fail_virtual_without_self.carbon:[[@LINE+4]]:3: error: virtual class function [VirtualWithoutSelf] + // CHECK:STDERR: abstract fn G(); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: + abstract fn G(); +} + +class T2 { + extend base: T1; + // CHECK:STDERR: fail_virtual_without_self.carbon:[[@LINE+4]]:3: error: virtual class function [VirtualWithoutSelf] + // CHECK:STDERR: impl fn F(); + // CHECK:STDERR: ^~~~~~~~~~~~ + // CHECK:STDERR: + impl fn F(); +} + +// --- fail_addr_self_mismatch.carbon + +library "[[@TEST_NAME]]"; + +base class T1 { + virtual fn F1[self: Self*](); +} + +class T2 { + extend base: T1; + // CHECK:STDERR: fail_addr_self_mismatch.carbon:[[@LINE+7]]:14: error: redeclaration differs at implicit parameter 1 [RedeclParamDiffers] + // CHECK:STDERR: impl fn F1[addr self: Self*](); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_addr_self_mismatch.carbon:[[@LINE-8]]:17: note: previous declaration's corresponding implicit parameter here [RedeclParamPrevious] + // CHECK:STDERR: virtual fn F1[self: Self*](); + // CHECK:STDERR: ^~~~~~~~~~~ + // CHECK:STDERR: + impl fn F1[addr self: Self*](); } // CHECK:STDOUT: --- modifiers.carbon @@ -276,7 +322,14 @@ class Derived { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { -// CHECK:STDOUT: %H.decl: %H.type = fn_decl @H [concrete = constants.%H] {} {} +// CHECK:STDOUT: %H.decl: %H.type = fn_decl @H [concrete = constants.%H] { +// CHECK:STDOUT: %self.patt: %Base = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Base = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %Base = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Base [concrete = constants.%Base] +// CHECK:STDOUT: %self: %Base = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc6: = vtable (%H.decl) [concrete = constants.%.c3d] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -287,8 +340,22 @@ class Derived { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Abstract { -// CHECK:STDOUT: %J.decl: %J.type = fn_decl @J [concrete = constants.%J] {} {} -// CHECK:STDOUT: %K.decl: %K.type = fn_decl @K [concrete = constants.%K] {} {} +// CHECK:STDOUT: %J.decl: %J.type = fn_decl @J [concrete = constants.%J] { +// CHECK:STDOUT: %self.patt: %Abstract = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Abstract = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %Abstract = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Abstract [concrete = constants.%Abstract] +// CHECK:STDOUT: %self: %Abstract = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %K.decl: %K.type = fn_decl @K [concrete = constants.%K] { +// CHECK:STDOUT: %self.patt: %Abstract = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Abstract = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %Abstract = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Abstract [concrete = constants.%Abstract] +// CHECK:STDOUT: %self: %Abstract = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc12: = vtable (%J.decl, %K.decl) [concrete = constants.%.2b2] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -299,11 +366,11 @@ class Derived { // CHECK:STDOUT: .K = %K.decl // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: virtual fn @H(); +// CHECK:STDOUT: virtual fn @H[%self.param_patt: %Base](); // CHECK:STDOUT: -// CHECK:STDOUT: abstract fn @J(); +// CHECK:STDOUT: abstract fn @J[%self.param_patt: %Abstract](); // CHECK:STDOUT: -// CHECK:STDOUT: virtual fn @K(); +// CHECK:STDOUT: virtual fn @K[%self.param_patt: %Abstract](); // CHECK:STDOUT: // CHECK:STDOUT: --- override_import.carbon // CHECK:STDOUT: @@ -333,7 +400,7 @@ class Derived { // CHECK:STDOUT: %Modifiers.Base: type = import_ref Modifiers//default, Base, loaded [concrete = constants.%Base] // CHECK:STDOUT: %Modifiers.import_ref.05e: = import_ref Modifiers//default, loc6_1, loaded [concrete = constants.%complete_type.513] // CHECK:STDOUT: %Modifiers.import_ref.1f3 = import_ref Modifiers//default, inst16 [no loc], unloaded -// CHECK:STDOUT: %Modifiers.import_ref.2cc = import_ref Modifiers//default, loc5_17, unloaded +// CHECK:STDOUT: %Modifiers.import_ref.2cc = import_ref Modifiers//default, loc5_29, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -351,7 +418,14 @@ class Derived { // CHECK:STDOUT: %Modifiers.ref: = name_ref Modifiers, imports.%Modifiers [concrete = imports.%Modifiers] // CHECK:STDOUT: %Base.ref: type = name_ref Base, imports.%Modifiers.Base [concrete = constants.%Base] // CHECK:STDOUT: %.loc7: %Derived.elem = base_decl %Base.ref, element0 [concrete] -// CHECK:STDOUT: %H.decl: %H.type.dba = fn_decl @H.1 [concrete = constants.%H.bce] {} {} +// CHECK:STDOUT: %H.decl: %H.type.dba = fn_decl @H.1 [concrete = constants.%H.bce] { +// CHECK:STDOUT: %self.patt: %Derived = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Derived = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %Derived = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] +// CHECK:STDOUT: %self: %Derived = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc9: = vtable (%H.decl) [concrete = constants.%.dce] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.base [concrete = constants.%complete_type.0e2] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -372,9 +446,9 @@ class Derived { // CHECK:STDOUT: .H = imports.%Modifiers.import_ref.2cc // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl fn @H.1(); +// CHECK:STDOUT: impl fn @H.1[%self.param_patt: %Derived](); // CHECK:STDOUT: -// CHECK:STDOUT: fn @H.2() [from "modifiers.carbon"]; +// CHECK:STDOUT: fn @H.2[%self.param_patt: %Base]() [from "modifiers.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: --- todo_fail_later_base.carbon // CHECK:STDOUT: @@ -406,7 +480,7 @@ class Derived { // CHECK:STDOUT: %Modifiers.Base: type = import_ref Modifiers//default, Base, loaded [concrete = constants.%Base] // CHECK:STDOUT: %Modifiers.import_ref.05e: = import_ref Modifiers//default, loc6_1, loaded [concrete = constants.%complete_type.513] // CHECK:STDOUT: %Modifiers.import_ref.1f3 = import_ref Modifiers//default, inst16 [no loc], unloaded -// CHECK:STDOUT: %Modifiers.import_ref.2cc = import_ref Modifiers//default, loc5_17, unloaded +// CHECK:STDOUT: %Modifiers.import_ref.2cc = import_ref Modifiers//default, loc5_29, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -421,7 +495,14 @@ class Derived { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Derived { -// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %self.patt: %Derived = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Derived = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %Derived = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] +// CHECK:STDOUT: %self: %Derived = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %Modifiers.ref: = name_ref Modifiers, imports.%Modifiers [concrete = imports.%Modifiers] // CHECK:STDOUT: %Base.ref: type = name_ref Base, imports.%Modifiers.Base [concrete = constants.%Base] // CHECK:STDOUT: %.loc8: %Derived.elem = base_decl %Base.ref, element0 [concrete] @@ -445,9 +526,9 @@ class Derived { // CHECK:STDOUT: .H = imports.%Modifiers.import_ref.2cc // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: virtual fn @F(); +// CHECK:STDOUT: virtual fn @F[%self.param_patt: %Derived](); // CHECK:STDOUT: -// CHECK:STDOUT: fn @H() [from "modifiers.carbon"]; +// CHECK:STDOUT: fn @H[%self.param_patt: %Base]() [from "modifiers.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: --- init.carbon // CHECK:STDOUT: @@ -473,7 +554,7 @@ class Derived { // CHECK:STDOUT: %Modifiers.Base: type = import_ref Modifiers//default, Base, loaded [concrete = constants.%Base] // CHECK:STDOUT: %Modifiers.import_ref.05e: = import_ref Modifiers//default, loc6_1, loaded [concrete = constants.%complete_type] // CHECK:STDOUT: %Modifiers.import_ref.1f3 = import_ref Modifiers//default, inst16 [no loc], unloaded -// CHECK:STDOUT: %Modifiers.import_ref.2cc = import_ref Modifiers//default, loc5_17, unloaded +// CHECK:STDOUT: %Modifiers.import_ref.2cc = import_ref Modifiers//default, loc5_29, unloaded // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -555,7 +636,14 @@ class Derived { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @A1 { -// CHECK:STDOUT: %F.decl: %F.type.13a = fn_decl @F.1 [concrete = constants.%F.df5] {} {} +// CHECK:STDOUT: %F.decl: %F.type.13a = fn_decl @F.1 [concrete = constants.%F.df5] { +// CHECK:STDOUT: %self.patt: %A1 = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %A1 = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %A1 = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%A1 [concrete = constants.%A1] +// CHECK:STDOUT: %self: %A1 = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc6: = vtable (%F.decl) [concrete = constants.%.593] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -568,7 +656,14 @@ class Derived { // CHECK:STDOUT: class @A2 { // CHECK:STDOUT: %A1.ref: type = name_ref A1, file.%A1.decl [concrete = constants.%A1] // CHECK:STDOUT: %.loc9: %A2.elem = base_decl %A1.ref, element0 [concrete] -// CHECK:STDOUT: %F.decl: %F.type.4ae = fn_decl @F.2 [concrete = constants.%F.1d5] {} {} +// CHECK:STDOUT: %F.decl: %F.type.4ae = fn_decl @F.2 [concrete = constants.%F.1d5] { +// CHECK:STDOUT: %self.patt: %A2 = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %A2 = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %A2 = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%A2 [concrete = constants.%A2] +// CHECK:STDOUT: %self: %A2 = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc11: = vtable (%F.decl) [concrete = constants.%.943] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.base [concrete = constants.%complete_type.a6f] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -581,9 +676,9 @@ class Derived { // CHECK:STDOUT: extend %A1.ref // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: virtual fn @F.1(); +// CHECK:STDOUT: virtual fn @F.1[%self.param_patt: %A1](); // CHECK:STDOUT: -// CHECK:STDOUT: impl fn @F.2(); +// CHECK:STDOUT: impl fn @F.2[%self.param_patt: %A2](); // CHECK:STDOUT: // CHECK:STDOUT: --- impl_base.carbon // CHECK:STDOUT: @@ -632,7 +727,14 @@ class Derived { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @B1 { -// CHECK:STDOUT: %F.decl: %F.type.e4c = fn_decl @F.1 [concrete = constants.%F.8f5] {} {} +// CHECK:STDOUT: %F.decl: %F.type.e4c = fn_decl @F.1 [concrete = constants.%F.8f5] { +// CHECK:STDOUT: %self.patt: %B1 = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %B1 = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %B1 = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%B1 [concrete = constants.%B1] +// CHECK:STDOUT: %self: %B1 = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc6: = vtable (%F.decl) [concrete = constants.%.bc5] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -645,7 +747,14 @@ class Derived { // CHECK:STDOUT: class @B2 { // CHECK:STDOUT: %B1.ref: type = name_ref B1, file.%B1.decl [concrete = constants.%B1] // CHECK:STDOUT: %.loc9: %B2.elem = base_decl %B1.ref, element0 [concrete] -// CHECK:STDOUT: %F.decl: %F.type.b26 = fn_decl @F.2 [concrete = constants.%F.d48] {} {} +// CHECK:STDOUT: %F.decl: %F.type.b26 = fn_decl @F.2 [concrete = constants.%F.d48] { +// CHECK:STDOUT: %self.patt: %B2 = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %B2 = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %B2 = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%B2 [concrete = constants.%B2] +// CHECK:STDOUT: %self: %B2 = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc11: = vtable (%F.decl) [concrete = constants.%.579] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.base.508 [concrete = constants.%complete_type.5ac] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -661,7 +770,14 @@ class Derived { // CHECK:STDOUT: class @C { // CHECK:STDOUT: %B2.ref: type = name_ref B2, file.%B2.decl [concrete = constants.%B2] // CHECK:STDOUT: %.loc14: %C.elem = base_decl %B2.ref, element0 [concrete] -// CHECK:STDOUT: %F.decl: %F.type.c29 = fn_decl @F.3 [concrete = constants.%F.437] {} {} +// CHECK:STDOUT: %F.decl: %F.type.c29 = fn_decl @F.3 [concrete = constants.%F.437] { +// CHECK:STDOUT: %self.patt: %C = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %C = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %self: %C = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc16: = vtable (%F.decl) [concrete = constants.%.5f6] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.base.421 [concrete = constants.%complete_type.066] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -674,11 +790,11 @@ class Derived { // CHECK:STDOUT: extend %B2.ref // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: virtual fn @F.1(); +// CHECK:STDOUT: virtual fn @F.1[%self.param_patt: %B1](); // CHECK:STDOUT: -// CHECK:STDOUT: impl fn @F.2(); +// CHECK:STDOUT: impl fn @F.2[%self.param_patt: %B2](); // CHECK:STDOUT: -// CHECK:STDOUT: impl fn @F.3(); +// CHECK:STDOUT: impl fn @F.3[%self.param_patt: %C](); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_modifiers.carbon // CHECK:STDOUT: @@ -709,7 +825,14 @@ class Derived { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { -// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %self.patt: %C = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %C = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %self: %C = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc10: = vtable () [concrete = constants.%.f2b] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -719,7 +842,7 @@ class Derived { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl fn @F(); +// CHECK:STDOUT: impl fn @F[%self.param_patt: %C](); // CHECK:STDOUT: // CHECK:STDOUT: --- init_members.carbon // CHECK:STDOUT: @@ -790,7 +913,14 @@ class Derived { // CHECK:STDOUT: %.loc6_3: %Base.elem = var_pattern %.loc6_9 // CHECK:STDOUT: } // CHECK:STDOUT: %.var.loc6: ref %Base.elem = var -// CHECK:STDOUT: %F.decl: %F.type.7c6 = fn_decl @F.1 [concrete = constants.%F.d17] {} {} +// CHECK:STDOUT: %F.decl: %F.type.7c6 = fn_decl @F.1 [concrete = constants.%F.d17] { +// CHECK:STDOUT: %self.patt: %Base = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Base = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %Base = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Base [concrete = constants.%Base] +// CHECK:STDOUT: %self: %Base = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc9: = vtable (%F.decl) [concrete = constants.%.5ee] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr.m1.m2 [concrete = constants.%complete_type.cf7] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -802,7 +932,7 @@ class Derived { // CHECK:STDOUT: .F = %F.decl // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: virtual fn @F.1(); +// CHECK:STDOUT: virtual fn @F.1[%self.param_patt: %Base](); // CHECK:STDOUT: // CHECK:STDOUT: fn @F.2() { // CHECK:STDOUT: !entry: @@ -933,7 +1063,14 @@ class Derived { // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc8: %Derived.elem = base_decl %Base.ref, element1 [concrete] -// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %self.patt: %Derived = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Derived = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %Derived = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] +// CHECK:STDOUT: %self: %Derived = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc10: = vtable () [concrete = constants.%.f2b] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr.base [concrete = constants.%complete_type.336] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -946,7 +1083,7 @@ class Derived { // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl fn @F(); +// CHECK:STDOUT: impl fn @F[%self.param_patt: %Derived](); // CHECK:STDOUT: // CHECK:STDOUT: --- abstract_impl.carbon // CHECK:STDOUT: @@ -992,7 +1129,14 @@ class Derived { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @AbstractBase { -// CHECK:STDOUT: %F.decl: %F.type.85b = fn_decl @F.1 [concrete = constants.%F.6e9] {} {} +// CHECK:STDOUT: %F.decl: %F.type.85b = fn_decl @F.1 [concrete = constants.%F.6e9] { +// CHECK:STDOUT: %self.patt: %AbstractBase = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %AbstractBase = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %AbstractBase = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%AbstractBase [concrete = constants.%AbstractBase] +// CHECK:STDOUT: %self: %AbstractBase = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc6: = vtable (%F.decl) [concrete = constants.%.6ec] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -1019,7 +1163,14 @@ class Derived { // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %AbstractIntermediate.ref: type = name_ref AbstractIntermediate, file.%AbstractIntermediate.decl [concrete = constants.%AbstractIntermediate] // CHECK:STDOUT: %.loc13: %Derived.elem = base_decl %AbstractIntermediate.ref, element0 [concrete] -// CHECK:STDOUT: %F.decl: %F.type.5da = fn_decl @F.2 [concrete = constants.%F.fa3] {} {} +// CHECK:STDOUT: %F.decl: %F.type.5da = fn_decl @F.2 [concrete = constants.%F.fa3] { +// CHECK:STDOUT: %self.patt: %Derived = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Derived = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %Derived = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] +// CHECK:STDOUT: %self: %Derived = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc15: = vtable (%F.decl) [concrete = constants.%.88d] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.base.da5 [concrete = constants.%complete_type.f8c] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -1032,9 +1183,9 @@ class Derived { // CHECK:STDOUT: extend %AbstractIntermediate.ref // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: abstract fn @F.1(); +// CHECK:STDOUT: abstract fn @F.1[%self.param_patt: %AbstractBase](); // CHECK:STDOUT: -// CHECK:STDOUT: impl fn @F.2(); +// CHECK:STDOUT: impl fn @F.2[%self.param_patt: %Derived](); // CHECK:STDOUT: // CHECK:STDOUT: --- virtual_impl.carbon // CHECK:STDOUT: @@ -1080,7 +1231,14 @@ class Derived { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @VirtualBase { -// CHECK:STDOUT: %F.decl: %F.type.e62 = fn_decl @F.1 [concrete = constants.%F.3e7] {} {} +// CHECK:STDOUT: %F.decl: %F.type.e62 = fn_decl @F.1 [concrete = constants.%F.3e7] { +// CHECK:STDOUT: %self.patt: %VirtualBase = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %VirtualBase = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %VirtualBase = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%VirtualBase [concrete = constants.%VirtualBase] +// CHECK:STDOUT: %self: %VirtualBase = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc6: = vtable (%F.decl) [concrete = constants.%.def] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -1107,7 +1265,14 @@ class Derived { // CHECK:STDOUT: class @Derived { // CHECK:STDOUT: %VirtualIntermediate.ref: type = name_ref VirtualIntermediate, file.%VirtualIntermediate.decl [concrete = constants.%VirtualIntermediate] // CHECK:STDOUT: %.loc13: %Derived.elem = base_decl %VirtualIntermediate.ref, element0 [concrete] -// CHECK:STDOUT: %F.decl: %F.type.5da = fn_decl @F.2 [concrete = constants.%F.fa3] {} {} +// CHECK:STDOUT: %F.decl: %F.type.5da = fn_decl @F.2 [concrete = constants.%F.fa3] { +// CHECK:STDOUT: %self.patt: %Derived = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Derived = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %Derived = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] +// CHECK:STDOUT: %self: %Derived = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc15: = vtable (%F.decl) [concrete = constants.%.88d] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.base.43c [concrete = constants.%complete_type.fa6] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -1120,9 +1285,9 @@ class Derived { // CHECK:STDOUT: extend %VirtualIntermediate.ref // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: virtual fn @F.1(); +// CHECK:STDOUT: virtual fn @F.1[%self.param_patt: %VirtualBase](); // CHECK:STDOUT: -// CHECK:STDOUT: impl fn @F.2(); +// CHECK:STDOUT: impl fn @F.2[%self.param_patt: %Derived](); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_impl_mismatch.carbon // CHECK:STDOUT: @@ -1165,7 +1330,14 @@ class Derived { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @Base { -// CHECK:STDOUT: %F.decl: %F.type.7c6 = fn_decl @F.1 [concrete = constants.%F.d17] {} {} +// CHECK:STDOUT: %F.decl: %F.type.7c6 = fn_decl @F.1 [concrete = constants.%F.d17] { +// CHECK:STDOUT: %self.patt: %Base = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Base = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %Base = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Base [concrete = constants.%Base] +// CHECK:STDOUT: %self: %Base = bind_name self, %self.param +// CHECK:STDOUT: } // CHECK:STDOUT: %.loc6: = vtable (%F.decl) [concrete = constants.%.5ee] // CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type.513] // CHECK:STDOUT: complete_type_witness = %complete_type @@ -1179,10 +1351,15 @@ class Derived { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc9: %Derived.elem = base_decl %Base.ref, element0 [concrete] // CHECK:STDOUT: %F.decl: %F.type.5da = fn_decl @F.2 [concrete = constants.%F.fa3] { +// CHECK:STDOUT: %self.patt: %Derived = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Derived = value_param_pattern %self.patt, runtime_param0 // CHECK:STDOUT: %v.patt: %i32 = binding_pattern v -// CHECK:STDOUT: %v.param_patt: %i32 = value_param_pattern %v.patt, runtime_param0 +// CHECK:STDOUT: %v.param_patt: %i32 = value_param_pattern %v.patt, runtime_param1 // CHECK:STDOUT: } { -// CHECK:STDOUT: %v.param: %i32 = value_param runtime_param0 +// CHECK:STDOUT: %self.param: %Derived = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] +// CHECK:STDOUT: %self: %Derived = bind_name self, %self.param +// CHECK:STDOUT: %v.param: %i32 = value_param runtime_param1 // CHECK:STDOUT: %.loc17: type = splice_block %i32 [concrete = constants.%i32] { // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] @@ -1201,9 +1378,9 @@ class Derived { // CHECK:STDOUT: extend %Base.ref // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: virtual fn @F.1(); +// CHECK:STDOUT: virtual fn @F.1[%self.param_patt: %Base](); // CHECK:STDOUT: -// CHECK:STDOUT: impl fn @F.2(%v.param_patt: %i32); +// CHECK:STDOUT: impl fn @F.2[%self.param_patt: %Derived](%v.param_patt: %i32); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_impl_conversion.carbon // CHECK:STDOUT: @@ -1306,11 +1483,16 @@ class Derived { // CHECK:STDOUT: // CHECK:STDOUT: class @Base { // CHECK:STDOUT: %F.decl: %F.type.7c6 = fn_decl @F.1 [concrete = constants.%F.d17] { +// CHECK:STDOUT: %self.patt: %Base = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Base = value_param_pattern %self.patt, runtime_param0 // CHECK:STDOUT: %return.patt: %T1 = return_slot_pattern -// CHECK:STDOUT: %return.param_patt: %T1 = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: %return.param_patt: %T1 = out_param_pattern %return.patt, runtime_param1 // CHECK:STDOUT: } { // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] -// CHECK:STDOUT: %return.param: ref %T1 = out_param runtime_param0 +// CHECK:STDOUT: %self.param: %Base = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Base [concrete = constants.%Base] +// CHECK:STDOUT: %self: %Base = bind_name self, %self.param +// CHECK:STDOUT: %return.param: ref %T1 = out_param runtime_param1 // CHECK:STDOUT: %return: ref %T1 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %.loc18: = vtable (%F.decl) [concrete = constants.%.5ee] @@ -1328,11 +1510,16 @@ class Derived { // CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [concrete = constants.%Base] // CHECK:STDOUT: %.loc21: %Derived.elem = base_decl %Base.ref, element0 [concrete] // CHECK:STDOUT: %F.decl: %F.type.5da = fn_decl @F.2 [concrete = constants.%F.fa3] { +// CHECK:STDOUT: %self.patt: %Derived = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Derived = value_param_pattern %self.patt, runtime_param0 // CHECK:STDOUT: %return.patt: %T2 = return_slot_pattern -// CHECK:STDOUT: %return.param_patt: %T2 = out_param_pattern %return.patt, runtime_param0 +// CHECK:STDOUT: %return.param_patt: %T2 = out_param_pattern %return.patt, runtime_param1 // CHECK:STDOUT: } { // CHECK:STDOUT: %T2.ref: type = name_ref T2, file.%T2.decl [concrete = constants.%T2] -// CHECK:STDOUT: %return.param: ref %T2 = out_param runtime_param0 +// CHECK:STDOUT: %self.param: %Derived = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] +// CHECK:STDOUT: %self: %Derived = bind_name self, %self.param +// CHECK:STDOUT: %return.param: ref %T2 = out_param runtime_param1 // CHECK:STDOUT: %return: ref %T2 = return_slot %return.param // CHECK:STDOUT: } // CHECK:STDOUT: %.loc30: = vtable (%F.decl) [concrete = constants.%.88d] @@ -1356,9 +1543,9 @@ class Derived { // CHECK:STDOUT: return %.loc12_14 to %return // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: virtual fn @F.1() -> %T1; +// CHECK:STDOUT: virtual fn @F.1[%self.param_patt: %Base]() -> %T1; // CHECK:STDOUT: -// CHECK:STDOUT: impl fn @F.2() -> %T2; +// CHECK:STDOUT: impl fn @F.2[%self.param_patt: %Derived]() -> %T2; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_todo_impl_generic_base.carbon // CHECK:STDOUT: @@ -1435,10 +1622,18 @@ class Derived { // CHECK:STDOUT: // CHECK:STDOUT: class { // CHECK:STDOUT: %F.decl: @Base.%F.type (%F.type.f17) = fn_decl @F.1 [symbolic = @Base.%F (constants.%F.e26)] { +// CHECK:STDOUT: %self.patt: @F.1.%Base (%Base.370) = binding_pattern self +// CHECK:STDOUT: %self.param_patt: @F.1.%Base (%Base.370) = value_param_pattern %self.patt, runtime_param0 // CHECK:STDOUT: %t.patt: @F.1.%T (%T) = binding_pattern t -// CHECK:STDOUT: %t.param_patt: @F.1.%T (%T) = value_param_pattern %t.patt, runtime_param0 +// CHECK:STDOUT: %t.param_patt: @F.1.%T (%T) = value_param_pattern %t.patt, runtime_param1 // CHECK:STDOUT: } { -// CHECK:STDOUT: %t.param: @F.1.%T (%T) = value_param runtime_param0 +// CHECK:STDOUT: %self.param: @F.1.%Base (%Base.370) = value_param runtime_param0 +// CHECK:STDOUT: %.loc8_22.1: type = splice_block %Self.ref [symbolic = %Base (constants.%Base.370)] { +// CHECK:STDOUT: %.loc8_22.2: type = specific_constant constants.%Base.370, @Base(constants.%T) [symbolic = %Base (constants.%Base.370)] +// CHECK:STDOUT: %Self.ref: type = name_ref Self, %.loc8_22.2 [symbolic = %Base (constants.%Base.370)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %self: @F.1.%Base (%Base.370) = bind_name self, %self.param +// CHECK:STDOUT: %t.param: @F.1.%T (%T) = value_param runtime_param1 // CHECK:STDOUT: %T.ref: type = name_ref T, @Base.%T.loc7_17.1 [symbolic = %T (constants.%T)] // CHECK:STDOUT: %t: @F.1.%T (%T) = bind_name t, %t.param // CHECK:STDOUT: } @@ -1460,10 +1655,15 @@ class Derived { // CHECK:STDOUT: %Base: type = class_type @Base, @Base(constants.%T1) [concrete = constants.%Base.ea5] // CHECK:STDOUT: %.loc12: %Derived.elem = base_decl %Base, element0 [concrete] // CHECK:STDOUT: %F.decl: %F.type.5da = fn_decl @F.2 [concrete = constants.%F.fa3] { +// CHECK:STDOUT: %self.patt: %Derived = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %Derived = value_param_pattern %self.patt, runtime_param0 // CHECK:STDOUT: %t.patt: %T1 = binding_pattern t -// CHECK:STDOUT: %t.param_patt: %T1 = value_param_pattern %t.patt, runtime_param0 +// CHECK:STDOUT: %t.param_patt: %T1 = value_param_pattern %t.patt, runtime_param1 // CHECK:STDOUT: } { -// CHECK:STDOUT: %t.param: %T1 = value_param runtime_param0 +// CHECK:STDOUT: %self.param: %Derived = value_param runtime_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%Derived [concrete = constants.%Derived] +// CHECK:STDOUT: %self: %Derived = bind_name self, %self.param +// CHECK:STDOUT: %t.param: %T1 = value_param runtime_param1 // CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] // CHECK:STDOUT: %t: %T1 = bind_name t, %t.param // CHECK:STDOUT: } @@ -1482,11 +1682,12 @@ class Derived { // CHECK:STDOUT: // CHECK:STDOUT: generic virtual fn @F.1(@Base.%T.loc7_17.1: type) { // CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic = %T (constants.%T)] +// CHECK:STDOUT: %Base: type = class_type @Base, @Base(%T) [symbolic = %Base (constants.%Base.370)] // CHECK:STDOUT: -// CHECK:STDOUT: virtual fn(%t.param_patt: @F.1.%T (%T)); +// CHECK:STDOUT: virtual fn[%self.param_patt: @F.1.%Base (%Base.370)](%t.param_patt: @F.1.%T (%T)); // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: impl fn @F.2(%t.param_patt: %T1); +// CHECK:STDOUT: impl fn @F.2[%self.param_patt: %Derived](%t.param_patt: %T1); // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T) { // CHECK:STDOUT: %T.loc7_17.2 => constants.%T @@ -1495,8 +1696,11 @@ class Derived { // CHECK:STDOUT: // CHECK:STDOUT: specific @F.1(constants.%T) { // CHECK:STDOUT: %T => constants.%T +// CHECK:STDOUT: %Base => constants.%Base.370 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: specific @Base(@F.1.%T) {} +// CHECK:STDOUT: // CHECK:STDOUT: specific @Base(%T.loc7_17.2) {} // CHECK:STDOUT: // CHECK:STDOUT: specific @Base(constants.%T1) { @@ -1509,3 +1713,168 @@ class Derived { // CHECK:STDOUT: %.loc9_1.2 => constants.%.611 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_virtual_without_self.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %T1: type = class_type @T1 [concrete] +// CHECK:STDOUT: %F.type.ba7: type = fn_type @F.1 [concrete] +// CHECK:STDOUT: %F.1a5: %F.type.ba7 = struct_value () [concrete] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] +// CHECK:STDOUT: %.e62: = vtable (%F.1a5, %G) [concrete] +// CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] +// CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] +// CHECK:STDOUT: %T2: type = class_type @T2 [concrete] +// CHECK:STDOUT: %T2.elem: type = unbound_element_type %T2, %T1 [concrete] +// CHECK:STDOUT: %F.type.834: type = fn_type @F.2 [concrete] +// CHECK:STDOUT: %F.a48: %F.type.834 = struct_value () [concrete] +// CHECK:STDOUT: %.025: = vtable (%F.a48, %G) [concrete] +// CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %T1} [concrete] +// CHECK:STDOUT: %complete_type.e14: = complete_type_witness %struct_type.base [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: .T2 = %T2.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %T1.decl: type = class_decl @T1 [concrete = constants.%T1] {} {} +// CHECK:STDOUT: %T2.decl: type = class_decl @T2 [concrete = constants.%T2] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @T1 { +// CHECK:STDOUT: %F.decl: %F.type.ba7 = fn_decl @F.1 [concrete = constants.%F.1a5] {} {} +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {} {} +// CHECK:STDOUT: %.loc15: = vtable (%F.decl, %G.decl) [concrete = constants.%.e62] +// CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type.513] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%T1 +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: .G = %G.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @T2 { +// CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] +// CHECK:STDOUT: %.loc18: %T2.elem = base_decl %T1.ref, element0 [concrete] +// CHECK:STDOUT: %F.decl: %F.type.834 = fn_decl @F.2 [concrete = constants.%F.a48] {} {} +// CHECK:STDOUT: %.loc24: = vtable (%F.decl, constants.%G) [concrete = constants.%.025] +// CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.base [concrete = constants.%complete_type.e14] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%T2 +// CHECK:STDOUT: .T1 = +// CHECK:STDOUT: .base = %.loc18 +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: extend %T1.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: virtual fn @F.1(); +// CHECK:STDOUT: +// CHECK:STDOUT: abstract fn @G(); +// CHECK:STDOUT: +// CHECK:STDOUT: impl fn @F.2(); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_addr_self_mismatch.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %T1: type = class_type @T1 [concrete] +// CHECK:STDOUT: %ptr.87b: type = ptr_type %T1 [concrete] +// CHECK:STDOUT: %F1.type.b96: type = fn_type @F1.1 [concrete] +// CHECK:STDOUT: %F1.765: %F1.type.b96 = struct_value () [concrete] +// CHECK:STDOUT: %ptr.454: type = ptr_type [concrete] +// CHECK:STDOUT: %.278: = vtable (%F1.765) [concrete] +// CHECK:STDOUT: %struct_type.vptr: type = struct_type {.: %ptr.454} [concrete] +// CHECK:STDOUT: %complete_type.513: = complete_type_witness %struct_type.vptr [concrete] +// CHECK:STDOUT: %T2: type = class_type @T2 [concrete] +// CHECK:STDOUT: %T2.elem: type = unbound_element_type %T2, %T1 [concrete] +// CHECK:STDOUT: %ptr.63e: type = ptr_type %T2 [concrete] +// CHECK:STDOUT: %F1.type.b0d: type = fn_type @F1.2 [concrete] +// CHECK:STDOUT: %F1.0ce: %F1.type.b0d = struct_value () [concrete] +// CHECK:STDOUT: %.20a: = vtable (%F1.0ce) [concrete] +// CHECK:STDOUT: %struct_type.base: type = struct_type {.base: %T1} [concrete] +// CHECK:STDOUT: %complete_type.e14: = complete_type_witness %struct_type.base [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: .T2 = %T2.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %T1.decl: type = class_decl @T1 [concrete = constants.%T1] {} {} +// CHECK:STDOUT: %T2.decl: type = class_decl @T2 [concrete = constants.%T2] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @T1 { +// CHECK:STDOUT: %F1.decl: %F1.type.b96 = fn_decl @F1.1 [concrete = constants.%F1.765] { +// CHECK:STDOUT: %self.patt: %ptr.87b = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %ptr.87b = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %ptr.87b = value_param runtime_param0 +// CHECK:STDOUT: %.loc5: type = splice_block %ptr [concrete = constants.%ptr.87b] { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%T1 [concrete = constants.%T1] +// CHECK:STDOUT: %ptr: type = ptr_type %T1 [concrete = constants.%ptr.87b] +// CHECK:STDOUT: } +// CHECK:STDOUT: %self: %ptr.87b = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc6: = vtable (%F1.decl) [concrete = constants.%.278] +// CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.vptr [concrete = constants.%complete_type.513] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%T1 +// CHECK:STDOUT: .F1 = %F1.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @T2 { +// CHECK:STDOUT: %T1.ref: type = name_ref T1, file.%T1.decl [concrete = constants.%T1] +// CHECK:STDOUT: %.loc9: %T2.elem = base_decl %T1.ref, element0 [concrete] +// CHECK:STDOUT: %F1.decl: %F1.type.b0d = fn_decl @F1.2 [concrete = constants.%F1.0ce] { +// CHECK:STDOUT: %self.patt: %ptr.63e = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %ptr.63e = value_param_pattern %self.patt, runtime_param0 +// CHECK:STDOUT: %.loc17_14: auto = addr_pattern %self.param_patt +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %ptr.63e = value_param runtime_param0 +// CHECK:STDOUT: %.loc17_29: type = splice_block %ptr [concrete = constants.%ptr.63e] { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%T2 [concrete = constants.%T2] +// CHECK:STDOUT: %ptr: type = ptr_type %T2 [concrete = constants.%ptr.63e] +// CHECK:STDOUT: } +// CHECK:STDOUT: %self: %ptr.63e = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc18: = vtable (%F1.decl) [concrete = constants.%.20a] +// CHECK:STDOUT: %complete_type: = complete_type_witness %struct_type.base [concrete = constants.%complete_type.e14] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%T2 +// CHECK:STDOUT: .T1 = +// CHECK:STDOUT: .base = %.loc9 +// CHECK:STDOUT: .F1 = %F1.decl +// CHECK:STDOUT: extend %T1.ref +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: virtual fn @F1.1[%self.param_patt: %ptr.87b](); +// CHECK:STDOUT: +// CHECK:STDOUT: impl fn @F1.2[addr %self.param_patt: %ptr.63e](); +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 84644d46c1fb..b895f9dcf11a 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -256,6 +256,7 @@ CARBON_DIAGNOSTIC_KIND(ClassSpecificDeclPrevious) CARBON_DIAGNOSTIC_KIND(ClassIncompleteWithinDefinition) CARBON_DIAGNOSTIC_KIND(ExpectedSymbolicBindingInFieldDecl) CARBON_DIAGNOSTIC_KIND(ImplWithoutBase) +CARBON_DIAGNOSTIC_KIND(VirtualWithoutSelf) // Deduction. CARBON_DIAGNOSTIC_KIND(DeductionIncomplete) diff --git a/toolchain/lower/testdata/class/virtual.carbon b/toolchain/lower/testdata/class/virtual.carbon index b2c00f14e905..978f0645fdd2 100644 --- a/toolchain/lower/testdata/class/virtual.carbon +++ b/toolchain/lower/testdata/class/virtual.carbon @@ -17,12 +17,12 @@ base class Base { base class Intermediate { extend base: Base; - virtual fn Fn(); + virtual fn Fn[self: Self]() { } } class Derived { extend base: Intermediate; - impl fn Fn(); + impl fn Fn[self: Self]() { } } // --- create.carbon @@ -47,7 +47,7 @@ package MemberInit; base class Base { var m: i32; - virtual fn Fn(); + virtual fn Fn[self: Self]() { } } fn Fn() { @@ -62,9 +62,15 @@ fn Fn() { // CHECK:STDOUT: ; ModuleID = 'classes.carbon' // CHECK:STDOUT: source_filename = "classes.carbon" // CHECK:STDOUT: -// CHECK:STDOUT: declare void @_CFn.Intermediate.Classes() +// CHECK:STDOUT: define void @_CFn.Intermediate.Classes(ptr %self) !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !7 +// CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: declare void @_CFn.Derived.Classes() +// CHECK:STDOUT: define void @_CFn.Derived.Classes(ptr %self) !dbg !8 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !9 +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: !llvm.module.flags = !{!0, !1} // CHECK:STDOUT: !llvm.dbg.cu = !{!2} @@ -73,6 +79,12 @@ fn Fn() { // CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} // CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) // CHECK:STDOUT: !3 = !DIFile(filename: "classes.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Fn", linkageName: "_CFn.Intermediate.Classes", scope: null, file: !3, line: 9, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{} +// CHECK:STDOUT: !7 = !DILocation(line: 9, column: 3, scope: !4) +// CHECK:STDOUT: !8 = distinct !DISubprogram(name: "Fn", linkageName: "_CFn.Derived.Classes", scope: null, file: !3, line: 14, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !9 = !DILocation(line: 14, column: 3, scope: !8) // CHECK:STDOUT: ; ModuleID = 'create.carbon' // CHECK:STDOUT: source_filename = "create.carbon" // CHECK:STDOUT: @@ -89,11 +101,11 @@ fn Fn() { // CHECK:STDOUT: // CHECK:STDOUT: define void @_CUse.Create(ptr %v) !dbg !9 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: call void @_CFn.Intermediate.Classes(), !dbg !10 +// CHECK:STDOUT: call void @_CFn.Intermediate.Classes(ptr %v), !dbg !10 // CHECK:STDOUT: ret void, !dbg !11 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: declare void @_CFn.Intermediate.Classes() +// CHECK:STDOUT: declare void @_CFn.Intermediate.Classes(ptr) // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(i64 immarg, ptr captures(none)) #0 @@ -121,23 +133,26 @@ fn Fn() { // CHECK:STDOUT: ; ModuleID = 'member_init.carbon' // CHECK:STDOUT: source_filename = "member_init.carbon" // CHECK:STDOUT: -// CHECK:STDOUT: declare void @_CFn.Base.MemberInit() -// CHECK:STDOUT: -// CHECK:STDOUT: define void @_CFn.MemberInit() !dbg !4 { +// CHECK:STDOUT: define void @_CFn.Base.MemberInit(ptr %self) !dbg !4 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %i.var = alloca i32, align 4, !dbg !7 -// CHECK:STDOUT: %v.var = alloca { ptr, i32 }, align 8, !dbg !7 -// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 4, ptr %i.var), !dbg !7 -// CHECK:STDOUT: store i32 3, ptr %i.var, align 4, !dbg !7 -// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 16, ptr %v.var), !dbg !7 -// CHECK:STDOUT: %.loc12_24.2.vptr = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 0, !dbg !8 -// CHECK:STDOUT: store ptr null, ptr %.loc12_24.2.vptr, align 8, !dbg !8 -// CHECK:STDOUT: %.loc12_23 = load i32, ptr %i.var, align 4, !dbg !9 -// CHECK:STDOUT: %.loc12_24.5.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !8 -// CHECK:STDOUT: store i32 %.loc12_23, ptr %.loc12_24.5.m, align 4, !dbg !8 -// CHECK:STDOUT: %.loc15_4.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !10 -// CHECK:STDOUT: store i32 5, ptr %.loc15_4.m, align 4, !dbg !10 -// CHECK:STDOUT: ret void, !dbg !11 +// CHECK:STDOUT: ret void, !dbg !7 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CFn.MemberInit() !dbg !8 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %i.var = alloca i32, align 4, !dbg !9 +// CHECK:STDOUT: %v.var = alloca { ptr, i32 }, align 8, !dbg !9 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 4, ptr %i.var), !dbg !9 +// CHECK:STDOUT: store i32 3, ptr %i.var, align 4, !dbg !9 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(i64 16, ptr %v.var), !dbg !9 +// CHECK:STDOUT: %.loc12_24.2.vptr = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 0, !dbg !10 +// CHECK:STDOUT: store ptr null, ptr %.loc12_24.2.vptr, align 8, !dbg !10 +// CHECK:STDOUT: %.loc12_23 = load i32, ptr %i.var, align 4, !dbg !11 +// CHECK:STDOUT: %.loc12_24.5.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !10 +// CHECK:STDOUT: store i32 %.loc12_23, ptr %.loc12_24.5.m, align 4, !dbg !10 +// CHECK:STDOUT: %.loc15_4.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !12 +// CHECK:STDOUT: store i32 5, ptr %.loc15_4.m, align 4, !dbg !12 +// CHECK:STDOUT: ret void, !dbg !13 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) @@ -155,11 +170,13 @@ fn Fn() { // CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} // CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) // CHECK:STDOUT: !3 = !DIFile(filename: "member_init.carbon", directory: "") -// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Fn", linkageName: "_CFn.MemberInit", scope: null, file: !3, line: 9, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Fn", linkageName: "_CFn.Base.MemberInit", scope: null, file: !3, line: 6, type: !5, spFlags: DISPFlagDefinition, unit: !2) // CHECK:STDOUT: !5 = !DISubroutineType(types: !6) // CHECK:STDOUT: !6 = !{} -// CHECK:STDOUT: !7 = !DILocation(line: 10, column: 3, scope: !4) -// CHECK:STDOUT: !8 = !DILocation(line: 12, column: 17, scope: !4) -// CHECK:STDOUT: !9 = !DILocation(line: 12, column: 23, scope: !4) -// CHECK:STDOUT: !10 = !DILocation(line: 15, column: 3, scope: !4) -// CHECK:STDOUT: !11 = !DILocation(line: 9, column: 1, scope: !4) +// CHECK:STDOUT: !7 = !DILocation(line: 6, column: 3, scope: !4) +// CHECK:STDOUT: !8 = distinct !DISubprogram(name: "Fn", linkageName: "_CFn.MemberInit", scope: null, file: !3, line: 9, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !9 = !DILocation(line: 10, column: 3, scope: !8) +// CHECK:STDOUT: !10 = !DILocation(line: 12, column: 17, scope: !8) +// CHECK:STDOUT: !11 = !DILocation(line: 12, column: 23, scope: !8) +// CHECK:STDOUT: !12 = !DILocation(line: 15, column: 3, scope: !8) +// CHECK:STDOUT: !13 = !DILocation(line: 9, column: 1, scope: !8)