From 28ceb4dd4e39acc8b2c8113533af973e12970db6 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 10 Apr 2024 11:57:48 +0200 Subject: [PATCH] Basic check support for adapters. (#3874) No support is provided yet for converting between a type and an adapter for that type. --- toolchain/check/context.cpp | 1 + toolchain/check/decl_state.h | 1 + toolchain/check/eval.cpp | 1 + toolchain/check/handle_class.cpp | 184 ++++++++-- toolchain/check/testdata/class/adapt.carbon | 146 ++++++++ .../check/testdata/class/extend_adapt.carbon | 314 ++++++++++++++++++ .../testdata/class/fail_adapt_bad_decl.carbon | 256 ++++++++++++++ .../testdata/class/fail_adapt_bad_type.carbon | 50 +++ .../class/fail_adapt_modifiers.carbon | 133 ++++++++ .../class/fail_adapt_with_subobjects.carbon | 195 +++++++++++ .../check/testdata/class/init_adapt.carbon | 295 ++++++++++++++++ toolchain/diagnostics/diagnostic_kind.def | 11 +- toolchain/lower/handle.cpp | 5 + toolchain/lower/testdata/class/adapt.carbon | 26 ++ .../testdata/class/fail_modifiers.carbon | 49 ++- toolchain/sem_ir/class.h | 5 + toolchain/sem_ir/file.cpp | 3 + toolchain/sem_ir/inst_kind.def | 1 + toolchain/sem_ir/typed_insts.h | 9 + 19 files changed, 1642 insertions(+), 43 deletions(-) create mode 100644 toolchain/check/testdata/class/adapt.carbon create mode 100644 toolchain/check/testdata/class/extend_adapt.carbon create mode 100644 toolchain/check/testdata/class/fail_adapt_bad_decl.carbon create mode 100644 toolchain/check/testdata/class/fail_adapt_bad_type.carbon create mode 100644 toolchain/check/testdata/class/fail_adapt_modifiers.carbon create mode 100644 toolchain/check/testdata/class/fail_adapt_with_subobjects.carbon create mode 100644 toolchain/check/testdata/class/init_adapt.carbon create mode 100644 toolchain/lower/testdata/class/adapt.carbon diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index d5e264c4ce8a..6ee4f65b764f 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -950,6 +950,7 @@ class TypeCompleter { auto BuildValueRepr(SemIR::TypeId type_id, SemIR::Inst inst) const -> SemIR::ValueRepr { CARBON_KIND_SWITCH(inst) { + case SemIR::AdaptDecl::Kind: case SemIR::AddrOf::Kind: case SemIR::AddrPattern::Kind: case SemIR::ArrayIndex::Kind: diff --git a/toolchain/check/decl_state.h b/toolchain/check/decl_state.h index 9638b9d837f8..44a8d13a03b4 100644 --- a/toolchain/check/decl_state.h +++ b/toolchain/check/decl_state.h @@ -66,6 +66,7 @@ struct DeclState { // The kind of declaration. enum DeclKind : int8_t { FileScope, + Adapt, Alias, Base, Class, diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index c1d382a86451..0b843931e14f 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -901,6 +901,7 @@ auto TryEvalInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) } // These cases are either not expressions or not constant. + case SemIR::AdaptDecl::Kind: case SemIR::AddrPattern::Kind: case SemIR::Assign::Kind: case SemIR::BindName::Kind: diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index c686106f620c..ec90b790ee16 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -9,6 +9,17 @@ namespace Carbon::Check { +// If `type_id` is a class type, get its corresponding `SemIR::Class` object. +// Otherwise returns `nullptr`. +static auto TryGetAsClass(Context& context, SemIR::TypeId type_id) + -> SemIR::Class* { + auto class_type = context.types().TryGetAs(type_id); + if (!class_type) { + return nullptr; + } + return &context.classes().Get(class_type->class_id); +} + auto HandleClassIntroducer(Context& context, Parse::ClassIntroducerId node_id) -> bool { // Create an instruction block to hold the instructions created as part of the @@ -172,15 +183,113 @@ auto HandleClassDefinitionStart(Context& context, return true; } -auto HandleAdaptIntroducer(Context& /*context*/, +// Diagnoses a class-specific declaration appearing outside a class. +static auto DiagnoseClassSpecificDeclOutsideClass(Context& context, + SemIRLoc loc, + Lex::TokenKind tok) -> void { + CARBON_DIAGNOSTIC(ClassSpecificDeclOutsideClass, Error, + "`{0}` declaration can only be used in a class.", + Lex::TokenKind); + context.emitter().Emit(loc, ClassSpecificDeclOutsideClass, tok); +} + +// Returns the declaration of the immediately-enclosing class scope, or +// diagonses if there isn't one. +static auto GetEnclosingClassOrDiagnose(Context& context, SemIRLoc loc, + Lex::TokenKind tok) + -> std::optional { + auto class_scope = context.GetCurrentScopeAs(); + if (!class_scope) { + DiagnoseClassSpecificDeclOutsideClass(context, loc, tok); + } + return class_scope; +} + +// Diagnoses a class-specific declaration that is repeated within a class, but +// is not permitted to be repeated. +static auto DiagnoseClassSpecificDeclRepeated(Context& context, + SemIRLoc new_loc, + SemIRLoc prev_loc, + Lex::TokenKind tok) -> void { + CARBON_DIAGNOSTIC(ClassSpecificDeclRepeated, Error, + "Multiple `{0}` declarations in class.{1}", Lex::TokenKind, + std::string); + const llvm::StringRef extra = tok == Lex::TokenKind::Base + ? " Multiple inheritance is not permitted." + : ""; + CARBON_DIAGNOSTIC(ClassSpecificDeclPrevious, Note, + "Previous `{0}` declaration is here.", Lex::TokenKind); + context.emitter() + .Build(new_loc, ClassSpecificDeclRepeated, tok, extra.str()) + .Note(prev_loc, ClassSpecificDeclPrevious, tok) + .Emit(); +} + +auto HandleAdaptIntroducer(Context& context, Parse::AdaptIntroducerId /*node_id*/) -> bool { + context.decl_state_stack().Push(DeclState::Adapt); return true; } -auto HandleAdaptDecl(Context& context, Parse::AdaptDeclId /*node_id*/) -> bool { - auto adapted_expr_id = context.node_stack().PopExpr(); - (void)adapted_expr_id; - // TODO: Process the `adapt` declaration. +auto HandleAdaptDecl(Context& context, Parse::AdaptDeclId node_id) -> bool { + auto [adapted_type_node, adapted_type_expr_id] = + context.node_stack().PopExprWithNodeId(); + + // Process modifiers. `extend` is permitted, no others are allowed. + LimitModifiersOnDecl(context, KeywordModifierSet::Extend, + Lex::TokenKind::Adapt); + auto modifiers = context.decl_state_stack().innermost().modifier_set; + context.decl_state_stack().Pop(DeclState::Adapt); + + auto enclosing_class_decl = + GetEnclosingClassOrDiagnose(context, node_id, Lex::TokenKind::Adapt); + if (!enclosing_class_decl) { + return true; + } + + auto& class_info = context.classes().Get(enclosing_class_decl->class_id); + if (class_info.adapt_id.is_valid()) { + DiagnoseClassSpecificDeclRepeated(context, node_id, class_info.adapt_id, + Lex::TokenKind::Adapt); + return true; + } + + auto adapted_type_id = ExprAsType(context, node_id, adapted_type_expr_id); + adapted_type_id = context.AsCompleteType(adapted_type_id, [&] { + CARBON_DIAGNOSTIC(IncompleteTypeInAdaptDecl, Error, + "Adapted type `{0}` is an incomplete type.", + SemIR::TypeId); + return context.emitter().Build(node_id, IncompleteTypeInAdaptDecl, + adapted_type_id); + }); + + // Build a SemIR representation for the declaration. + class_info.adapt_id = + context.AddInst({node_id, SemIR::AdaptDecl{adapted_type_id}}); + + // Extend the class scope with the adapted type's scope if requested. + if (!!(modifiers & KeywordModifierSet::Extend)) { + auto extended_scope_id = SemIR::NameScopeId::Invalid; + if (adapted_type_id == SemIR::TypeId::Error) { + // Recover by not extending any scope. We instead set has_error to true + // below. + } else if (auto* adapted_class_info = + TryGetAsClass(context, adapted_type_id)) { + extended_scope_id = adapted_class_info->scope_id; + CARBON_CHECK(adapted_class_info->scope_id.is_valid()) + << "Complete class should have a scope"; + } else { + // TODO: Accept any type that has a scope. + context.TODO(node_id, "extending non-class type"); + } + + auto& class_scope = context.name_scopes().Get(class_info.scope_id); + if (extended_scope_id.is_valid()) { + class_scope.extended_scopes.push_back(extended_scope_id); + } else { + class_scope.has_error = true; + } + } return true; } @@ -208,17 +317,6 @@ constexpr BaseInfo BaseInfo::Error = {.type_id = SemIR::TypeId::Error, .scope_id = SemIR::NameScopeId::Invalid}; } // namespace -// If `type_id` is a class type, get its corresponding `SemIR::Class` object. -// Otherwise returns `nullptr`. -static auto TryGetAsClass(Context& context, SemIR::TypeId type_id) - -> SemIR::Class* { - auto class_type = context.types().TryGetAs(type_id); - if (!class_type) { - return nullptr; - } - return &context.classes().Get(class_type->class_id); -} - // Diagnoses an attempt to derive from a final type. static auto DiagnoseBaseIsFinal(Context& context, Parse::NodeId node_id, SemIR::TypeId base_type_id) -> void { @@ -268,7 +366,7 @@ auto HandleBaseDecl(Context& context, Parse::BaseDeclId node_id) -> bool { auto [base_type_node_id, base_type_expr_id] = context.node_stack().PopExprWithNodeId(); - // Process modifiers. `extend` is required, none others are allowed. + // Process modifiers. `extend` is required, no others are allowed. LimitModifiersOnDecl(context, KeywordModifierSet::Extend, Lex::TokenKind::Base); auto modifiers = context.decl_state_stack().innermost().modifier_set; @@ -279,25 +377,16 @@ auto HandleBaseDecl(Context& context, Parse::BaseDeclId node_id) -> bool { } context.decl_state_stack().Pop(DeclState::Base); - auto enclosing_class_decl = context.GetCurrentScopeAs(); + auto enclosing_class_decl = + GetEnclosingClassOrDiagnose(context, node_id, Lex::TokenKind::Base); if (!enclosing_class_decl) { - CARBON_DIAGNOSTIC(BaseOutsideClass, Error, - "`base` declaration can only be used in a class."); - context.emitter().Emit(node_id, BaseOutsideClass); return true; } auto& class_info = context.classes().Get(enclosing_class_decl->class_id); if (class_info.base_id.is_valid()) { - CARBON_DIAGNOSTIC(BaseRepeated, Error, - "Multiple `base` declarations in class. Multiple " - "inheritance is not permitted."); - CARBON_DIAGNOSTIC(BasePrevious, Note, - "Previous `base` declaration is here."); - context.emitter() - .Build(node_id, BaseRepeated) - .Note(class_info.base_id, BasePrevious) - .Emit(); + DiagnoseClassSpecificDeclRepeated(context, node_id, class_info.base_id, + Lex::TokenKind::Base); return true; } @@ -316,6 +405,7 @@ auto HandleBaseDecl(Context& context, Parse::BaseDeclId node_id) -> bool { // Add a corresponding field to the object representation of the class. // TODO: Consider whether we want to use `partial T` here. + // TODO: Should we diagnose if there are already any fields? context.args_type_info_stack().AddInstId(context.AddInstInNoBlock( {node_id, SemIR::StructTypeField{SemIR::NameId::Base, base_info.type_id}})); @@ -345,9 +435,39 @@ auto HandleClassDefinition(Context& context, context.node_stack().Pop(); context.inst_block_stack().Pop(); - // The class type is now fully defined. + // The class type is now fully defined. Compute its object representation. auto& class_info = context.classes().Get(class_id); - class_info.object_repr_id = context.GetStructType(fields_id); + if (class_info.adapt_id.is_valid()) { + class_info.object_repr_id = SemIR::TypeId::Error; + if (class_info.base_id.is_valid()) { + CARBON_DIAGNOSTIC(AdaptWithBase, Error, + "Adapter cannot have a base class."); + CARBON_DIAGNOSTIC(AdaptBaseHere, Note, "`base` declaration is here."); + context.emitter() + .Build(class_info.adapt_id, AdaptWithBase) + .Note(class_info.base_id, AdaptBaseHere) + .Emit(); + } else if (!context.inst_blocks().Get(fields_id).empty()) { + auto first_field_id = context.inst_blocks().Get(fields_id).front(); + CARBON_DIAGNOSTIC(AdaptWithFields, Error, "Adapter cannot have fields."); + CARBON_DIAGNOSTIC(AdaptFieldHere, Note, + "First field declaration is here."); + context.emitter() + .Build(class_info.adapt_id, AdaptWithFields) + .Note(first_field_id, AdaptFieldHere) + .Emit(); + } else { + // The object representation of the adapter is the adapted type. + // TODO: If the adapted type is a class, should we use its object + // representation type instead? + class_info.object_repr_id = + context.insts() + .GetAs(class_info.adapt_id) + .adapted_type_id; + } + } else { + class_info.object_repr_id = context.GetStructType(fields_id); + } // The decl_name_stack and scopes are popped by `ProcessNodeIds`. return true; diff --git a/toolchain/check/testdata/class/adapt.carbon b/toolchain/check/testdata/class/adapt.carbon new file mode 100644 index 000000000000..c80a9949b279 --- /dev/null +++ b/toolchain/check/testdata/class/adapt.carbon @@ -0,0 +1,146 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- basic.carbon + +library "basic" api; + +class SomeClass { + var a: i32; + var b: i32; +} + +class SomeClassAdapter { + adapt SomeClass; +} + +class StructAdapter { + adapt {.a: i32, .b: i32}; +} + +// --- fail_not_extend.carbon + +library "fail_not_extend" api; + +class Adapted { + fn F(); +} + +class AdaptNotExtend { + adapt Adapted; +} + +fn F(a: AdaptNotExtend) { + // `Adapted` is not extended, so lookup for `F` finds nothing. + // CHECK:STDERR: fail_not_extend.carbon:[[@LINE+3]]:3: ERROR: Name `F` not found. + // CHECK:STDERR: a.F(); + // CHECK:STDERR: ^~~ + a.F(); +} + +// CHECK:STDOUT: --- basic.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %SomeClass: type = class_type @SomeClass [template] +// CHECK:STDOUT: %.1: type = unbound_element_type SomeClass, i32 [template] +// CHECK:STDOUT: %.2: type = struct_type {.a: i32, .b: i32} [template] +// CHECK:STDOUT: %SomeClassAdapter: type = class_type @SomeClassAdapter [template] +// CHECK:STDOUT: %.3: type = ptr_type {.a: i32, .b: i32} [template] +// CHECK:STDOUT: %StructAdapter: type = class_type @StructAdapter [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .SomeClass = %SomeClass.decl +// CHECK:STDOUT: .SomeClassAdapter = %SomeClassAdapter.decl +// CHECK:STDOUT: .StructAdapter = %StructAdapter.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %SomeClass.decl: type = class_decl @SomeClass [template = constants.%SomeClass] {} +// CHECK:STDOUT: %SomeClassAdapter.decl: type = class_decl @SomeClassAdapter [template = constants.%SomeClassAdapter] {} +// CHECK:STDOUT: %StructAdapter.decl: type = class_decl @StructAdapter [template = constants.%StructAdapter] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @SomeClass { +// CHECK:STDOUT: %.loc5: = field_decl a, element0 [template] +// CHECK:STDOUT: %.loc6: = field_decl b, element1 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%SomeClass +// CHECK:STDOUT: .a = %.loc5 +// CHECK:STDOUT: .b = %.loc6 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @SomeClassAdapter { +// CHECK:STDOUT: %SomeClass.ref: type = name_ref SomeClass, file.%SomeClass.decl [template = constants.%SomeClass] +// CHECK:STDOUT: adapt_decl SomeClass +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%SomeClassAdapter +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @StructAdapter { +// CHECK:STDOUT: %.loc14: type = struct_type {.a: i32, .b: i32} [template = constants.%.2] +// CHECK:STDOUT: adapt_decl {.a: i32, .b: i32} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%StructAdapter +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_not_extend.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Adapted: type = class_type @Adapted [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %AdaptNotExtend: type = class_type @AdaptNotExtend [template] +// CHECK:STDOUT: %.2: type = tuple_type () [template] +// CHECK:STDOUT: %.3: type = ptr_type {} [template] +// CHECK:STDOUT: %.4: type = ptr_type Adapted [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .Adapted = %Adapted.decl +// CHECK:STDOUT: .AdaptNotExtend = %AdaptNotExtend.decl +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %Adapted.decl: type = class_decl @Adapted [template = constants.%Adapted] {} +// CHECK:STDOUT: %AdaptNotExtend.decl: type = class_decl @AdaptNotExtend [template = constants.%AdaptNotExtend] {} +// CHECK:STDOUT: %F: = fn_decl @F.2 [template] { +// CHECK:STDOUT: %AdaptNotExtend.ref: type = name_ref AdaptNotExtend, %AdaptNotExtend.decl [template = constants.%AdaptNotExtend] +// CHECK:STDOUT: %a.loc12_6.1: AdaptNotExtend = param a +// CHECK:STDOUT: @F.2.%a: AdaptNotExtend = bind_name a, %a.loc12_6.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Adapted { +// CHECK:STDOUT: %F: = fn_decl @F.1 [template] {} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%Adapted +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @AdaptNotExtend { +// CHECK:STDOUT: %Adapted.ref: type = name_ref Adapted, file.%Adapted.decl [template = constants.%Adapted] +// CHECK:STDOUT: adapt_decl Adapted +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%AdaptNotExtend +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.1(); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.2(%a: AdaptNotExtend) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %a.ref: AdaptNotExtend = name_ref a, %a +// CHECK:STDOUT: %F.ref: = name_ref F, [template = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/extend_adapt.carbon b/toolchain/check/testdata/class/extend_adapt.carbon new file mode 100644 index 000000000000..7be81f3dfb4a --- /dev/null +++ b/toolchain/check/testdata/class/extend_adapt.carbon @@ -0,0 +1,314 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- basic.carbon + +library "basic" api; + +class SomeClassAdapter; + +class SomeClass { + var a: i32; + var b: i32; + + fn StaticMemberFunction(); + + fn AdapterMethod[self: SomeClassAdapter](); +} + +class SomeClassAdapter { + extend adapt SomeClass; +} + +fn TestStaticMemberFunction(a: SomeClassAdapter) { + a.StaticMemberFunction(); +} + +fn TestAdapterMethod(a: SomeClassAdapter) { + a.AdapterMethod(); +} + +// --- fail_todo_method_access.carbon + +library "fail_todo_method_access" api; + +class SomeClass { + fn F[self: Self](); +} + +class SomeClassAdapter { + extend adapt SomeClass; +} + +fn F(a: SomeClassAdapter) { + // CHECK:STDERR: fail_todo_method_access.carbon:[[@LINE+7]]:3: ERROR: Cannot implicitly convert from `SomeClassAdapter` to `SomeClass`. + // CHECK:STDERR: a.F(); + // CHECK:STDERR: ^~~~ + // CHECK:STDERR: fail_todo_method_access.carbon:[[@LINE-11]]:8: Initializing `self` parameter of method declared here. + // CHECK:STDERR: fn F[self: Self](); + // CHECK:STDERR: ^~~~ + // CHECK:STDERR: + a.F(); +} + +// --- fail_todo_field_access.carbon + +library "fail_todo_field_access" api; + +class SomeClass { + var a: i32; + var b: i32; +} + +class SomeClassAdapter { + extend adapt SomeClass; +} + +fn F(a: SomeClassAdapter) -> i32 { + // CHECK:STDERR: fail_todo_field_access.carbon:[[@LINE+4]]:10: ERROR: Cannot implicitly convert from `SomeClassAdapter` to `SomeClass`. + // CHECK:STDERR: return a.b; + // CHECK:STDERR: ^~~ + // CHECK:STDERR: + return a.b; +} + +// --- fail_todo_adapt_non_class.carbon + +library "fail_todo_adapt_non_class" api; + +class StructAdapter { + // CHECK:STDERR: fail_todo_adapt_non_class.carbon:[[@LINE+3]]:3: ERROR: Semantics TODO: `extending non-class type`. + // CHECK:STDERR: extend adapt {.a: i32, .b: i32}; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + extend adapt {.a: i32, .b: i32}; +} + +// CHECK:STDOUT: --- basic.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %SomeClassAdapter: type = class_type @SomeClassAdapter [template] +// CHECK:STDOUT: %SomeClass: type = class_type @SomeClass [template] +// CHECK:STDOUT: %.1: type = unbound_element_type SomeClass, i32 [template] +// CHECK:STDOUT: %.2: type = struct_type {.a: i32, .b: i32} [template] +// CHECK:STDOUT: %.3: type = ptr_type {.a: i32, .b: i32} [template] +// CHECK:STDOUT: %.4: type = ptr_type SomeClass [template] +// CHECK:STDOUT: %.5: type = tuple_type () [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .SomeClassAdapter = %SomeClassAdapter.decl.loc4 +// CHECK:STDOUT: .SomeClass = %SomeClass.decl +// CHECK:STDOUT: .TestStaticMemberFunction = %TestStaticMemberFunction +// CHECK:STDOUT: .TestAdapterMethod = %TestAdapterMethod +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %SomeClassAdapter.decl.loc4: type = class_decl @SomeClassAdapter [template = constants.%SomeClassAdapter] {} +// CHECK:STDOUT: %SomeClass.decl: type = class_decl @SomeClass [template = constants.%SomeClass] {} +// CHECK:STDOUT: %SomeClassAdapter.decl.loc15: type = class_decl @SomeClassAdapter [template = constants.%SomeClassAdapter] {} +// CHECK:STDOUT: %TestStaticMemberFunction: = fn_decl @TestStaticMemberFunction [template] { +// CHECK:STDOUT: %SomeClassAdapter.ref.loc19: type = name_ref SomeClassAdapter, %SomeClassAdapter.decl.loc4 [template = constants.%SomeClassAdapter] +// CHECK:STDOUT: %a.loc19_29.1: SomeClassAdapter = param a +// CHECK:STDOUT: @TestStaticMemberFunction.%a: SomeClassAdapter = bind_name a, %a.loc19_29.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: %TestAdapterMethod: = fn_decl @TestAdapterMethod [template] { +// CHECK:STDOUT: %SomeClassAdapter.ref.loc23: type = name_ref SomeClassAdapter, %SomeClassAdapter.decl.loc4 [template = constants.%SomeClassAdapter] +// CHECK:STDOUT: %a.loc23_22.1: SomeClassAdapter = param a +// CHECK:STDOUT: @TestAdapterMethod.%a: SomeClassAdapter = bind_name a, %a.loc23_22.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @SomeClassAdapter { +// CHECK:STDOUT: %SomeClass.ref: type = name_ref SomeClass, file.%SomeClass.decl [template = constants.%SomeClass] +// CHECK:STDOUT: adapt_decl SomeClass +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%SomeClassAdapter +// CHECK:STDOUT: extend name_scope2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @SomeClass { +// CHECK:STDOUT: %.loc7: = field_decl a, element0 [template] +// CHECK:STDOUT: %.loc8: = field_decl b, element1 [template] +// CHECK:STDOUT: %StaticMemberFunction: = fn_decl @StaticMemberFunction [template] {} +// CHECK:STDOUT: %AdapterMethod: = fn_decl @AdapterMethod [template] { +// CHECK:STDOUT: %SomeClassAdapter.ref: type = name_ref SomeClassAdapter, file.%SomeClassAdapter.decl.loc4 [template = constants.%SomeClassAdapter] +// CHECK:STDOUT: %self.loc12_20.1: SomeClassAdapter = param self +// CHECK:STDOUT: %self.loc12_20.2: SomeClassAdapter = bind_name self, %self.loc12_20.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%SomeClass +// CHECK:STDOUT: .a = %.loc7 +// CHECK:STDOUT: .b = %.loc8 +// CHECK:STDOUT: .StaticMemberFunction = %StaticMemberFunction +// CHECK:STDOUT: .AdapterMethod = %AdapterMethod +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @StaticMemberFunction(); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @AdapterMethod[@SomeClass.%self.loc12_20.2: SomeClassAdapter](); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @TestStaticMemberFunction(%a: SomeClassAdapter) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %a.ref: SomeClassAdapter = name_ref a, %a +// CHECK:STDOUT: %StaticMemberFunction.ref: = name_ref StaticMemberFunction, @SomeClass.%StaticMemberFunction [template = @SomeClass.%StaticMemberFunction] +// CHECK:STDOUT: %.loc20: init () = call %StaticMemberFunction.ref() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @TestAdapterMethod(%a: SomeClassAdapter) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %a.ref: SomeClassAdapter = name_ref a, %a +// CHECK:STDOUT: %AdapterMethod.ref: = name_ref AdapterMethod, @SomeClass.%AdapterMethod [template = @SomeClass.%AdapterMethod] +// CHECK:STDOUT: %.loc24_4: = bound_method %a.ref, %AdapterMethod.ref +// CHECK:STDOUT: %.loc24_18: init () = call %.loc24_4(%a.ref) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_method_access.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %SomeClass: type = class_type @SomeClass [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %SomeClassAdapter: type = class_type @SomeClassAdapter [template] +// CHECK:STDOUT: %.2: type = tuple_type () [template] +// CHECK:STDOUT: %.3: type = ptr_type {} [template] +// CHECK:STDOUT: %.4: type = ptr_type SomeClass [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .SomeClass = %SomeClass.decl +// CHECK:STDOUT: .SomeClassAdapter = %SomeClassAdapter.decl +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %SomeClass.decl: type = class_decl @SomeClass [template = constants.%SomeClass] {} +// CHECK:STDOUT: %SomeClassAdapter.decl: type = class_decl @SomeClassAdapter [template = constants.%SomeClassAdapter] {} +// CHECK:STDOUT: %F: = fn_decl @F.2 [template] { +// CHECK:STDOUT: %SomeClassAdapter.ref: type = name_ref SomeClassAdapter, %SomeClassAdapter.decl [template = constants.%SomeClassAdapter] +// CHECK:STDOUT: %a.loc12_6.1: SomeClassAdapter = param a +// CHECK:STDOUT: @F.2.%a: SomeClassAdapter = bind_name a, %a.loc12_6.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @SomeClass { +// CHECK:STDOUT: %F: = fn_decl @F.1 [template] { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%SomeClass [template = constants.%SomeClass] +// CHECK:STDOUT: %self.loc5_8.1: SomeClass = param self +// CHECK:STDOUT: %self.loc5_8.2: SomeClass = bind_name self, %self.loc5_8.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%SomeClass +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @SomeClassAdapter { +// CHECK:STDOUT: %SomeClass.ref: type = name_ref SomeClass, file.%SomeClass.decl [template = constants.%SomeClass] +// CHECK:STDOUT: adapt_decl SomeClass +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%SomeClassAdapter +// CHECK:STDOUT: extend name_scope2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.1[@SomeClass.%self.loc5_8.2: SomeClass](); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F.2(%a: SomeClassAdapter) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %a.ref: SomeClassAdapter = name_ref a, %a +// CHECK:STDOUT: %F.ref: = name_ref F, @SomeClass.%F [template = @SomeClass.%F] +// CHECK:STDOUT: %.loc20_4: = bound_method %a.ref, %F.ref +// CHECK:STDOUT: %.loc20_6: init () = call %.loc20_4() [template = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_field_access.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %SomeClass: type = class_type @SomeClass [template] +// CHECK:STDOUT: %.1: type = unbound_element_type SomeClass, i32 [template] +// CHECK:STDOUT: %.2: type = struct_type {.a: i32, .b: i32} [template] +// CHECK:STDOUT: %SomeClassAdapter: type = class_type @SomeClassAdapter [template] +// CHECK:STDOUT: %.3: type = ptr_type {.a: i32, .b: i32} [template] +// CHECK:STDOUT: %.4: type = ptr_type SomeClass [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .SomeClass = %SomeClass.decl +// CHECK:STDOUT: .SomeClassAdapter = %SomeClassAdapter.decl +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %SomeClass.decl: type = class_decl @SomeClass [template = constants.%SomeClass] {} +// CHECK:STDOUT: %SomeClassAdapter.decl: type = class_decl @SomeClassAdapter [template = constants.%SomeClassAdapter] {} +// CHECK:STDOUT: %F: = fn_decl @F [template] { +// CHECK:STDOUT: %SomeClassAdapter.ref: type = name_ref SomeClassAdapter, %SomeClassAdapter.decl [template = constants.%SomeClassAdapter] +// CHECK:STDOUT: %a.loc13_6.1: SomeClassAdapter = param a +// CHECK:STDOUT: @F.%a: SomeClassAdapter = bind_name a, %a.loc13_6.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @SomeClass { +// CHECK:STDOUT: %.loc5: = field_decl a, element0 [template] +// CHECK:STDOUT: %.loc6: = field_decl b, element1 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%SomeClass +// CHECK:STDOUT: .a = %.loc5 +// CHECK:STDOUT: .b = %.loc6 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @SomeClassAdapter { +// CHECK:STDOUT: %SomeClass.ref: type = name_ref SomeClass, file.%SomeClass.decl [template = constants.%SomeClass] +// CHECK:STDOUT: adapt_decl SomeClass +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%SomeClassAdapter +// CHECK:STDOUT: extend name_scope2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%a: SomeClassAdapter) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %a.ref: SomeClassAdapter = name_ref a, %a +// CHECK:STDOUT: %b.ref: = name_ref b, @SomeClass.%.loc6 [template = @SomeClass.%.loc6] +// CHECK:STDOUT: %.loc18: i32 = class_element_access , element1 [template = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_adapt_non_class.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %StructAdapter: type = class_type @StructAdapter [template] +// CHECK:STDOUT: %.1: type = struct_type {.a: i32, .b: i32} [template] +// CHECK:STDOUT: %.2: type = ptr_type {.a: i32, .b: i32} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .StructAdapter = %StructAdapter.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %StructAdapter.decl: type = class_decl @StructAdapter [template = constants.%StructAdapter] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @StructAdapter { +// CHECK:STDOUT: %.loc8: type = struct_type {.a: i32, .b: i32} [template = constants.%.1] +// CHECK:STDOUT: adapt_decl {.a: i32, .b: i32} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%StructAdapter +// CHECK:STDOUT: has_error +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_adapt_bad_decl.carbon b/toolchain/check/testdata/class/fail_adapt_bad_decl.carbon new file mode 100644 index 000000000000..cc508e05df9d --- /dev/null +++ b/toolchain/check/testdata/class/fail_adapt_bad_decl.carbon @@ -0,0 +1,256 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- fail_not_type.carbon + +library "not_type" api; + +class Bad { + // CHECK:STDERR: fail_not_type.carbon:[[@LINE+4]]:3: ERROR: Cannot implicitly convert from `i32` to `type`. + // CHECK:STDERR: adapt 100; + // CHECK:STDERR: ^~~~~~~~~~ + // CHECK:STDERR: + adapt 100; +} + +// CHECK:STDERR: fail_not_type.carbon:[[@LINE+4]]:18: ERROR: Name `F` not found. +// CHECK:STDERR: fn Use(b: Bad) { b.F(); } +// CHECK:STDERR: ^~~ +// CHECK:STDERR: +fn Use(b: Bad) { b.F(); } + +// --- fail_extend_not_type.carbon + +library "extend_not_type" api; + +class Bad { + // CHECK:STDERR: fail_extend_not_type.carbon:[[@LINE+4]]:3: ERROR: Cannot implicitly convert from `i32` to `type`. + // CHECK:STDERR: extend adapt 100; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + extend adapt 100; +} + +// No diagnostic here, we don't know what names Bad has. +fn Use(b: Bad) { b.F(); } + +// --- fail_repeated.carbon + +library "fail_repeated" api; + +class MultipleAdapts { + adapt (); + // CHECK:STDERR: fail_repeated.carbon:[[@LINE+7]]:3: ERROR: Multiple `adapt` declarations in class. + // CHECK:STDERR: adapt {}; + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: fail_repeated.carbon:[[@LINE-4]]:3: Previous `adapt` declaration is here. + // CHECK:STDERR: adapt (); + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: + adapt {}; +} + +class MultipleAdaptsSameType { + adapt (); + // CHECK:STDERR: fail_repeated.carbon:[[@LINE+7]]:3: ERROR: Multiple `adapt` declarations in class. + // CHECK:STDERR: adapt (); + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: fail_repeated.carbon:[[@LINE-4]]:3: Previous `adapt` declaration is here. + // CHECK:STDERR: adapt (); + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: + adapt (); +} + +// --- fail_bad_scope.carbon + +library "fail_bad_scope" api; + +// CHECK:STDERR: fail_bad_scope.carbon:[[@LINE+4]]:1: ERROR: `adapt` declaration can only be used in a class. +// CHECK:STDERR: adapt {}; +// CHECK:STDERR: ^~~~~~~~~ +// CHECK:STDERR: +adapt {}; + +interface I { + // CHECK:STDERR: fail_bad_scope.carbon:[[@LINE+4]]:3: ERROR: `adapt` declaration can only be used in a class. + // CHECK:STDERR: adapt {}; + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: + adapt {}; +} + +class C { + interface I { + // CHECK:STDERR: fail_bad_scope.carbon:[[@LINE+3]]:5: ERROR: `adapt` declaration can only be used in a class. + // CHECK:STDERR: adapt {}; + // CHECK:STDERR: ^~~~~~~~~ + adapt {}; + } +} + +// CHECK:STDOUT: --- fail_not_type.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Bad: type = class_type @Bad [template] +// CHECK:STDOUT: %.1: i32 = int_literal 100 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .Bad = %Bad.decl +// CHECK:STDOUT: .Use = %Use +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %Bad.decl: type = class_decl @Bad [template = constants.%Bad] {} +// CHECK:STDOUT: %Use: = fn_decl @Use [template] { +// CHECK:STDOUT: %Bad.ref: type = name_ref Bad, %Bad.decl [template = constants.%Bad] +// CHECK:STDOUT: %b.loc16_8.1: Bad = param b +// CHECK:STDOUT: @Use.%b: Bad = bind_name b, %b.loc16_8.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Bad { +// CHECK:STDOUT: %.loc9: i32 = int_literal 100 [template = constants.%.1] +// CHECK:STDOUT: adapt_decl +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%Bad +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Use(%b: Bad) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %b.ref: Bad = name_ref b, %b +// CHECK:STDOUT: %F.ref: = name_ref F, [template = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_extend_not_type.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Bad: type = class_type @Bad [template] +// CHECK:STDOUT: %.1: i32 = int_literal 100 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .Bad = %Bad.decl +// CHECK:STDOUT: .Use = %Use +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %Bad.decl: type = class_decl @Bad [template = constants.%Bad] {} +// CHECK:STDOUT: %Use: = fn_decl @Use [template] { +// CHECK:STDOUT: %Bad.ref: type = name_ref Bad, %Bad.decl [template = constants.%Bad] +// CHECK:STDOUT: %b.loc13_8.1: Bad = param b +// CHECK:STDOUT: @Use.%b: Bad = bind_name b, %b.loc13_8.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Bad { +// CHECK:STDOUT: %.loc9: i32 = int_literal 100 [template = constants.%.1] +// CHECK:STDOUT: adapt_decl +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%Bad +// CHECK:STDOUT: has_error +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Use(%b: Bad) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %b.ref: Bad = name_ref b, %b +// CHECK:STDOUT: %F.ref: = name_ref F, [template = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_repeated.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %MultipleAdapts: type = class_type @MultipleAdapts [template] +// CHECK:STDOUT: %.1: type = tuple_type () [template] +// CHECK:STDOUT: %.2: type = struct_type {} [template] +// CHECK:STDOUT: %MultipleAdaptsSameType: type = class_type @MultipleAdaptsSameType [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .MultipleAdapts = %MultipleAdapts.decl +// CHECK:STDOUT: .MultipleAdaptsSameType = %MultipleAdaptsSameType.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %MultipleAdapts.decl: type = class_decl @MultipleAdapts [template = constants.%MultipleAdapts] {} +// CHECK:STDOUT: %MultipleAdaptsSameType.decl: type = class_decl @MultipleAdaptsSameType [template = constants.%MultipleAdaptsSameType] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @MultipleAdapts { +// CHECK:STDOUT: %.loc5_10.1: () = tuple_literal () +// CHECK:STDOUT: %.loc5_10.2: type = converted %.loc5_10.1, constants.%.1 [template = constants.%.1] +// CHECK:STDOUT: adapt_decl () +// CHECK:STDOUT: %.loc13: {} = struct_literal () +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%MultipleAdapts +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @MultipleAdaptsSameType { +// CHECK:STDOUT: %.loc17_10.1: () = tuple_literal () +// CHECK:STDOUT: %.loc17_10.2: type = converted %.loc17_10.1, constants.%.1 [template = constants.%.1] +// CHECK:STDOUT: adapt_decl () +// CHECK:STDOUT: %.loc25: () = tuple_literal () +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%MultipleAdaptsSameType +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_bad_scope.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %.2: type = interface_type @I.1 [template] +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.3: type = interface_type @I.2 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %.loc8: {} = struct_literal () +// CHECK:STDOUT: %I.decl: type = interface_decl @I.1 [template = constants.%.2] {} +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @I.1 { +// CHECK:STDOUT: %Self: I = bind_symbolic_name Self [symbolic] +// CHECK:STDOUT: %.loc15: {} = struct_literal () +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @I.2 { +// CHECK:STDOUT: %Self: I = bind_symbolic_name Self [symbolic] +// CHECK:STDOUT: %.loc23: {} = struct_literal () +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: witness = () +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %I.decl: type = interface_decl @I.2 [template = constants.%.3] {} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .I = %I.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_adapt_bad_type.carbon b/toolchain/check/testdata/class/fail_adapt_bad_type.carbon new file mode 100644 index 000000000000..1761ba513177 --- /dev/null +++ b/toolchain/check/testdata/class/fail_adapt_bad_type.carbon @@ -0,0 +1,50 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- fail_incomplete_type.carbon + +library "incomplete_type" api; + +class Incomplete; + +class AdaptIncomplete { + // CHECK:STDERR: fail_incomplete_type.carbon:[[@LINE+6]]:3: ERROR: Adapted type `Incomplete` is an incomplete type. + // CHECK:STDERR: adapt Incomplete; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_incomplete_type.carbon:[[@LINE-6]]:1: Class was forward declared here. + // CHECK:STDERR: class Incomplete; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ + adapt Incomplete; +} + +// CHECK:STDOUT: --- fail_incomplete_type.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Incomplete: type = class_type @Incomplete [template] +// CHECK:STDOUT: %AdaptIncomplete: type = class_type @AdaptIncomplete [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .Incomplete = %Incomplete.decl +// CHECK:STDOUT: .AdaptIncomplete = %AdaptIncomplete.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %Incomplete.decl: type = class_decl @Incomplete [template = constants.%Incomplete] {} +// CHECK:STDOUT: %AdaptIncomplete.decl: type = class_decl @AdaptIncomplete [template = constants.%AdaptIncomplete] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Incomplete; +// CHECK:STDOUT: +// CHECK:STDOUT: class @AdaptIncomplete { +// CHECK:STDOUT: %Incomplete.ref: type = name_ref Incomplete, file.%Incomplete.decl [template = constants.%Incomplete] +// CHECK:STDOUT: adapt_decl +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%AdaptIncomplete +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_adapt_modifiers.carbon b/toolchain/check/testdata/class/fail_adapt_modifiers.carbon new file mode 100644 index 000000000000..4b1fb0e876cb --- /dev/null +++ b/toolchain/check/testdata/class/fail_adapt_modifiers.carbon @@ -0,0 +1,133 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +class B {} + +class C1 { + // CHECK:STDERR: fail_adapt_modifiers.carbon:[[@LINE+4]]:3: ERROR: `private` not allowed on `adapt` declaration. + // CHECK:STDERR: private adapt B; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + private adapt B; +} + +class C2 { + // CHECK:STDERR: fail_adapt_modifiers.carbon:[[@LINE+4]]:3: ERROR: `abstract` not allowed on `adapt` declaration. + // CHECK:STDERR: abstract adapt B; + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: + abstract adapt B; +} + +class C3 { + // CHECK:STDERR: fail_adapt_modifiers.carbon:[[@LINE+4]]:3: ERROR: `default` not allowed on `adapt` declaration. + // CHECK:STDERR: default adapt B; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + default adapt B; +} + +class C4 { + // CHECK:STDERR: fail_adapt_modifiers.carbon:[[@LINE+7]]:10: ERROR: `extend` repeated on declaration. + // CHECK:STDERR: extend extend adapt B; + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: fail_adapt_modifiers.carbon:[[@LINE+4]]:3: `extend` previously appeared here. + // CHECK:STDERR: extend extend adapt B; + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + extend extend adapt B; +} + +class C5 { + // CHECK:STDERR: fail_adapt_modifiers.carbon:[[@LINE+6]]:10: ERROR: `base` not allowed on declaration with `extend`. + // CHECK:STDERR: extend base adapt B; + // CHECK:STDERR: ^~~~ + // CHECK:STDERR: fail_adapt_modifiers.carbon:[[@LINE+3]]:3: `extend` previously appeared here. + // CHECK:STDERR: extend base adapt B; + // CHECK:STDERR: ^~~~~~ + extend base adapt B; +} + +// CHECK:STDOUT: --- fail_adapt_modifiers.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %B: type = class_type @B [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %C1: type = class_type @C1 [template] +// CHECK:STDOUT: %.2: type = tuple_type () [template] +// CHECK:STDOUT: %.3: type = ptr_type {} [template] +// CHECK:STDOUT: %C2: type = class_type @C2 [template] +// CHECK:STDOUT: %C3: type = class_type @C3 [template] +// CHECK:STDOUT: %C4: type = class_type @C4 [template] +// CHECK:STDOUT: %C5: type = class_type @C5 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: .C1 = %C1.decl +// CHECK:STDOUT: .C2 = %C2.decl +// CHECK:STDOUT: .C3 = %C3.decl +// CHECK:STDOUT: .C4 = %C4.decl +// CHECK:STDOUT: .C5 = %C5.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %B.decl: type = class_decl @B [template = constants.%B] {} +// CHECK:STDOUT: %C1.decl: type = class_decl @C1 [template = constants.%C1] {} +// CHECK:STDOUT: %C2.decl: type = class_decl @C2 [template = constants.%C2] {} +// CHECK:STDOUT: %C3.decl: type = class_decl @C3 [template = constants.%C3] {} +// CHECK:STDOUT: %C4.decl: type = class_decl @C4 [template = constants.%C4] {} +// CHECK:STDOUT: %C5.decl: type = class_decl @C5 [template = constants.%C5] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @B { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%B +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C1 { +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [template = constants.%B] +// CHECK:STDOUT: adapt_decl B +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C2 { +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [template = constants.%B] +// CHECK:STDOUT: adapt_decl B +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C3 { +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [template = constants.%B] +// CHECK:STDOUT: adapt_decl B +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C3 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C4 { +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [template = constants.%B] +// CHECK:STDOUT: adapt_decl B +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C4 +// CHECK:STDOUT: extend name_scope2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C5 { +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [template = constants.%B] +// CHECK:STDOUT: adapt_decl B +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C5 +// CHECK:STDOUT: extend name_scope2 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/fail_adapt_with_subobjects.carbon b/toolchain/check/testdata/class/fail_adapt_with_subobjects.carbon new file mode 100644 index 000000000000..1cac08fceca8 --- /dev/null +++ b/toolchain/check/testdata/class/fail_adapt_with_subobjects.carbon @@ -0,0 +1,195 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- fail_adapt_with_base.carbon + +library "fail_adapt_with_base" api; + +base class Base {} + +class AdaptWithBase { + // CHECK:STDERR: fail_adapt_with_base.carbon:[[@LINE+3]]:3: ERROR: Adapter cannot have a base class. + // CHECK:STDERR: adapt i32; + // CHECK:STDERR: ^~~~~~~~~~ + adapt i32; + // CHECK:STDERR: fail_adapt_with_base.carbon:[[@LINE+4]]:3: `base` declaration is here. + // CHECK:STDERR: extend base: Base; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + extend base: Base; +} + +// --- fail_adapt_with_fields.carbon + +library "fail_adapt_with_fields" api; + +class AdaptWithField { + // CHECK:STDERR: fail_adapt_with_fields.carbon:[[@LINE+3]]:3: ERROR: Adapter cannot have fields. + // CHECK:STDERR: adapt i32; + // CHECK:STDERR: ^~~~~~~~~~ + adapt i32; + // CHECK:STDERR: fail_adapt_with_fields.carbon:[[@LINE+4]]:7: First field declaration is here. + // CHECK:STDERR: var n: i32; + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + var n: i32; +} + +class AdaptWithFields { + // CHECK:STDERR: fail_adapt_with_fields.carbon:[[@LINE+3]]:3: ERROR: Adapter cannot have fields. + // CHECK:STDERR: adapt i32; + // CHECK:STDERR: ^~~~~~~~~~ + adapt i32; + // CHECK:STDERR: fail_adapt_with_fields.carbon:[[@LINE+4]]:7: First field declaration is here. + // CHECK:STDERR: var a: i32; + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + var a: i32; + var b: i32; + var c: i32; +} + +// --- fail_adapt_with_base_and_fields.carbon + +library "fail_adapt_with_base_and_fields" api; + +base class Base {} + +class AdaptWithBaseAndFields { + extend base: Base; + var n: i32; + // CHECK:STDERR: fail_adapt_with_base_and_fields.carbon:[[@LINE+6]]:3: ERROR: Adapter cannot have a base class. + // CHECK:STDERR: adapt {}; + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: fail_adapt_with_base_and_fields.carbon:[[@LINE-5]]:3: `base` declaration is here. + // CHECK:STDERR: extend base: Base; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ + adapt {}; +} + +// CHECK:STDOUT: --- fail_adapt_with_base.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Base: type = class_type @Base [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %AdaptWithBase: type = class_type @AdaptWithBase [template] +// CHECK:STDOUT: %.2: type = tuple_type () [template] +// CHECK:STDOUT: %.3: type = ptr_type {} [template] +// CHECK:STDOUT: %.4: type = unbound_element_type AdaptWithBase, Base [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .Base = %Base.decl +// CHECK:STDOUT: .AdaptWithBase = %AdaptWithBase.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %Base.decl: type = class_decl @Base [template = constants.%Base] {} +// CHECK:STDOUT: %AdaptWithBase.decl: type = class_decl @AdaptWithBase [template = constants.%AdaptWithBase] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Base { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%Base +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @AdaptWithBase { +// CHECK:STDOUT: adapt_decl i32 +// CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [template = constants.%Base] +// CHECK:STDOUT: %.loc15: = base_decl Base, element0 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%AdaptWithBase +// CHECK:STDOUT: .base = %.loc15 +// CHECK:STDOUT: extend name_scope2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_adapt_with_fields.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %AdaptWithField: type = class_type @AdaptWithField [template] +// CHECK:STDOUT: %.1: type = unbound_element_type AdaptWithField, i32 [template] +// CHECK:STDOUT: %AdaptWithFields: type = class_type @AdaptWithFields [template] +// CHECK:STDOUT: %.2: type = unbound_element_type AdaptWithFields, i32 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .AdaptWithField = %AdaptWithField.decl +// CHECK:STDOUT: .AdaptWithFields = %AdaptWithFields.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %AdaptWithField.decl: type = class_decl @AdaptWithField [template = constants.%AdaptWithField] {} +// CHECK:STDOUT: %AdaptWithFields.decl: type = class_decl @AdaptWithFields [template = constants.%AdaptWithFields] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @AdaptWithField { +// CHECK:STDOUT: adapt_decl i32 +// CHECK:STDOUT: %.loc13: = field_decl n, element0 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%AdaptWithField +// CHECK:STDOUT: .n = %.loc13 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @AdaptWithFields { +// CHECK:STDOUT: adapt_decl i32 +// CHECK:STDOUT: %.loc25: = field_decl a, element0 [template] +// CHECK:STDOUT: %.loc26: = field_decl b, element1 [template] +// CHECK:STDOUT: %.loc27: = field_decl c, element2 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%AdaptWithFields +// CHECK:STDOUT: .a = %.loc25 +// CHECK:STDOUT: .b = %.loc26 +// CHECK:STDOUT: .c = %.loc27 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_adapt_with_base_and_fields.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Base: type = class_type @Base [template] +// CHECK:STDOUT: %.1: type = struct_type {} [template] +// CHECK:STDOUT: %AdaptWithBaseAndFields: type = class_type @AdaptWithBaseAndFields [template] +// CHECK:STDOUT: %.2: type = tuple_type () [template] +// CHECK:STDOUT: %.3: type = ptr_type {} [template] +// CHECK:STDOUT: %.4: type = unbound_element_type AdaptWithBaseAndFields, Base [template] +// CHECK:STDOUT: %.5: type = unbound_element_type AdaptWithBaseAndFields, i32 [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .Base = %Base.decl +// CHECK:STDOUT: .AdaptWithBaseAndFields = %AdaptWithBaseAndFields.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %Base.decl: type = class_decl @Base [template = constants.%Base] {} +// CHECK:STDOUT: %AdaptWithBaseAndFields.decl: type = class_decl @AdaptWithBaseAndFields [template = constants.%AdaptWithBaseAndFields] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Base { +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%Base +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @AdaptWithBaseAndFields { +// CHECK:STDOUT: %Base.ref: type = name_ref Base, file.%Base.decl [template = constants.%Base] +// CHECK:STDOUT: %.loc7: = base_decl Base, element0 [template] +// CHECK:STDOUT: %.loc8: = field_decl n, element1 [template] +// CHECK:STDOUT: %.loc15_10.1: {} = struct_literal () +// CHECK:STDOUT: %.loc15_10.2: type = converted %.loc15_10.1, constants.%.1 [template = constants.%.1] +// CHECK:STDOUT: adapt_decl {} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%AdaptWithBaseAndFields +// CHECK:STDOUT: .base = %.loc7 +// CHECK:STDOUT: .n = %.loc8 +// CHECK:STDOUT: extend name_scope2 +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/init_adapt.carbon b/toolchain/check/testdata/class/init_adapt.carbon new file mode 100644 index 000000000000..94745ed3ead3 --- /dev/null +++ b/toolchain/check/testdata/class/init_adapt.carbon @@ -0,0 +1,295 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +// --- fail_todo_init_adapt.carbon + +library "fail_todo_init_adapt" api; + +class C { + var a: i32; + var b: i32; +} + +class AdaptC { + adapt C; +} + +let a: C = {.a = 1, .b = 2}; + +// TODO: Allow these as explicit conversions. + +// CHECK:STDERR: fail_todo_init_adapt.carbon:[[@LINE+4]]:17: ERROR: Cannot convert from `C` to `AdaptC` with `as`. +// CHECK:STDERR: let b: AdaptC = a as AdaptC; +// CHECK:STDERR: ^~~~~~~~~~~ +// CHECK:STDERR: +let b: AdaptC = a as AdaptC; + +// CHECK:STDERR: fail_todo_init_adapt.carbon:[[@LINE+4]]:12: ERROR: Cannot convert from `AdaptC` to `C` with `as`. +// CHECK:STDERR: let c: C = b as C; +// CHECK:STDERR: ^~~~~~ +// CHECK:STDERR: +let c: C = b as C; + +fn MakeC() -> C; + +fn MakeAdaptC() -> AdaptC; + +// CHECK:STDERR: fail_todo_init_adapt.carbon:[[@LINE+4]]:17: ERROR: Cannot convert from `C` to `AdaptC` with `as`. +// CHECK:STDERR: var d: AdaptC = MakeC() as AdaptC; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +var d: AdaptC = MakeC() as AdaptC; + +// CHECK:STDERR: fail_todo_init_adapt.carbon:[[@LINE+4]]:12: ERROR: Cannot convert from `AdaptC` to `C` with `as`. +// CHECK:STDERR: var e: C = MakeAdaptC() as C; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +var e: C = MakeAdaptC() as C; + +// --- fail_not_implicit.carbon + +library "fail_not_implicit" api; + +class C { + var a: i32; + var b: i32; +} + +class AdaptC { + adapt C; +} + +let a: C = {.a = 1, .b = 2}; + +// Cannot implicitly convert between a type and an adapter for the type. + +// CHECK:STDERR: fail_not_implicit.carbon:[[@LINE+4]]:1: ERROR: Cannot implicitly convert from `C` to `AdaptC`. +// CHECK:STDERR: let b: AdaptC = a; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +let b: AdaptC = a; + +// CHECK:STDERR: fail_not_implicit.carbon:[[@LINE+4]]:1: ERROR: Cannot implicitly convert from `AdaptC` to `C`. +// CHECK:STDERR: let c: C = b; +// CHECK:STDERR: ^~~~~~~~~~~~~ +// CHECK:STDERR: +let c: C = b; + +fn MakeC() -> C; + +fn MakeAdaptC() -> AdaptC; + +// CHECK:STDERR: fail_not_implicit.carbon:[[@LINE+4]]:1: ERROR: Cannot implicitly convert from `C` to `AdaptC`. +// CHECK:STDERR: var d: AdaptC = MakeC(); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +var d: AdaptC = MakeC(); + +// CHECK:STDERR: fail_not_implicit.carbon:[[@LINE+3]]:1: ERROR: Cannot implicitly convert from `AdaptC` to `C`. +// CHECK:STDERR: var e: C = MakeAdaptC(); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~ +var e: C = MakeAdaptC(); + +// CHECK:STDOUT: --- fail_todo_init_adapt.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = unbound_element_type C, i32 [template] +// CHECK:STDOUT: %.2: type = struct_type {.a: i32, .b: i32} [template] +// CHECK:STDOUT: %AdaptC: type = class_type @AdaptC [template] +// CHECK:STDOUT: %.3: type = ptr_type {.a: i32, .b: i32} [template] +// CHECK:STDOUT: %.4: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.5: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.6: C = struct_value (%.4, %.5) [template] +// CHECK:STDOUT: %.7: type = ptr_type C [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .AdaptC = %AdaptC.decl +// CHECK:STDOUT: .MakeC = %MakeC +// CHECK:STDOUT: .MakeAdaptC = %MakeAdaptC +// CHECK:STDOUT: .d = %d +// CHECK:STDOUT: .e = %e +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %AdaptC.decl: type = class_decl @AdaptC [template = constants.%AdaptC] {} +// CHECK:STDOUT: %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %.loc13_18: i32 = int_literal 1 [template = constants.%.4] +// CHECK:STDOUT: %.loc13_26: i32 = int_literal 2 [template = constants.%.5] +// CHECK:STDOUT: %.loc13_27.1: {.a: i32, .b: i32} = struct_literal (%.loc13_18, %.loc13_26) +// CHECK:STDOUT: %.loc13_27.2: ref C = temporary_storage +// CHECK:STDOUT: %.loc13_27.3: ref i32 = class_element_access %.loc13_27.2, element0 +// CHECK:STDOUT: %.loc13_27.4: init i32 = initialize_from %.loc13_18 to %.loc13_27.3 [template = constants.%.4] +// CHECK:STDOUT: %.loc13_27.5: ref i32 = class_element_access %.loc13_27.2, element1 +// CHECK:STDOUT: %.loc13_27.6: init i32 = initialize_from %.loc13_26 to %.loc13_27.5 [template = constants.%.5] +// CHECK:STDOUT: %.loc13_27.7: init C = class_init (%.loc13_27.4, %.loc13_27.6), %.loc13_27.2 [template = constants.%.6] +// CHECK:STDOUT: %.loc13_27.8: ref C = temporary %.loc13_27.2, %.loc13_27.7 +// CHECK:STDOUT: %.loc13_27.9: ref C = converted %.loc13_27.1, %.loc13_27.8 +// CHECK:STDOUT: %.loc13_27.10: C = bind_value %.loc13_27.9 +// CHECK:STDOUT: %a: C = bind_name a, %.loc13_27.10 +// CHECK:STDOUT: %AdaptC.ref.loc21_8: type = name_ref AdaptC, %AdaptC.decl [template = constants.%AdaptC] +// CHECK:STDOUT: %a.ref: C = name_ref a, %a +// CHECK:STDOUT: %AdaptC.ref.loc21_22: type = name_ref AdaptC, %AdaptC.decl [template = constants.%AdaptC] +// CHECK:STDOUT: %b: AdaptC = bind_name b, +// CHECK:STDOUT: %C.ref.loc27_8: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %b.ref: AdaptC = name_ref b, %b +// CHECK:STDOUT: %C.ref.loc27_17: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %c: C = bind_name c, +// CHECK:STDOUT: %MakeC: = fn_decl @MakeC [template] { +// CHECK:STDOUT: %C.ref.loc29: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: @MakeC.%return: ref C = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %MakeAdaptC: = fn_decl @MakeAdaptC [template] { +// CHECK:STDOUT: %AdaptC.ref.loc31: type = name_ref AdaptC, %AdaptC.decl [template = constants.%AdaptC] +// CHECK:STDOUT: @MakeAdaptC.%return: ref AdaptC = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %AdaptC.ref.loc37: type = name_ref AdaptC, %AdaptC.decl [template = constants.%AdaptC] +// CHECK:STDOUT: %d.var: ref AdaptC = var d +// CHECK:STDOUT: %d: ref AdaptC = bind_name d, %d.var +// CHECK:STDOUT: %C.ref.loc43: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %e.var: ref C = var e +// CHECK:STDOUT: %e: ref C = bind_name e, %e.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %.loc5: = field_decl a, element0 [template] +// CHECK:STDOUT: %.loc6: = field_decl b, element1 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .a = %.loc5 +// CHECK:STDOUT: .b = %.loc6 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @AdaptC { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: adapt_decl C +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%AdaptC +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @MakeC() -> %return: C; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @MakeAdaptC() -> %return: AdaptC; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %MakeC.ref: = name_ref MakeC, file.%MakeC [template = file.%MakeC] +// CHECK:STDOUT: %.loc37_22.1: ref C = temporary_storage +// CHECK:STDOUT: %.loc37_22.2: init C = call %MakeC.ref() to %.loc37_22.1 +// CHECK:STDOUT: %AdaptC.ref: type = name_ref AdaptC, file.%AdaptC.decl [template = constants.%AdaptC] +// CHECK:STDOUT: assign file.%d.var, +// CHECK:STDOUT: %MakeAdaptC.ref: = name_ref MakeAdaptC, file.%MakeAdaptC [template = file.%MakeAdaptC] +// CHECK:STDOUT: %.loc43_22.1: ref AdaptC = temporary_storage +// CHECK:STDOUT: %.loc43_22.2: init AdaptC = call %MakeAdaptC.ref() to %.loc43_22.1 +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: assign file.%e.var, +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_not_implicit.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [template] +// CHECK:STDOUT: %.1: type = unbound_element_type C, i32 [template] +// CHECK:STDOUT: %.2: type = struct_type {.a: i32, .b: i32} [template] +// CHECK:STDOUT: %AdaptC: type = class_type @AdaptC [template] +// CHECK:STDOUT: %.3: type = ptr_type {.a: i32, .b: i32} [template] +// CHECK:STDOUT: %.4: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.5: i32 = int_literal 2 [template] +// CHECK:STDOUT: %.6: C = struct_value (%.4, %.5) [template] +// CHECK:STDOUT: %.7: type = ptr_type C [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .Core = %Core +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: .AdaptC = %AdaptC.decl +// CHECK:STDOUT: .MakeC = %MakeC +// CHECK:STDOUT: .MakeAdaptC = %MakeAdaptC +// CHECK:STDOUT: .d = %d +// CHECK:STDOUT: .e = %e +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core: = namespace [template] {} +// CHECK:STDOUT: %C.decl: type = class_decl @C [template = constants.%C] {} +// CHECK:STDOUT: %AdaptC.decl: type = class_decl @AdaptC [template = constants.%AdaptC] {} +// CHECK:STDOUT: %C.ref.loc13: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %.loc13_18: i32 = int_literal 1 [template = constants.%.4] +// CHECK:STDOUT: %.loc13_26: i32 = int_literal 2 [template = constants.%.5] +// CHECK:STDOUT: %.loc13_27.1: {.a: i32, .b: i32} = struct_literal (%.loc13_18, %.loc13_26) +// CHECK:STDOUT: %.loc13_27.2: ref C = temporary_storage +// CHECK:STDOUT: %.loc13_27.3: ref i32 = class_element_access %.loc13_27.2, element0 +// CHECK:STDOUT: %.loc13_27.4: init i32 = initialize_from %.loc13_18 to %.loc13_27.3 [template = constants.%.4] +// CHECK:STDOUT: %.loc13_27.5: ref i32 = class_element_access %.loc13_27.2, element1 +// CHECK:STDOUT: %.loc13_27.6: init i32 = initialize_from %.loc13_26 to %.loc13_27.5 [template = constants.%.5] +// CHECK:STDOUT: %.loc13_27.7: init C = class_init (%.loc13_27.4, %.loc13_27.6), %.loc13_27.2 [template = constants.%.6] +// CHECK:STDOUT: %.loc13_27.8: ref C = temporary %.loc13_27.2, %.loc13_27.7 +// CHECK:STDOUT: %.loc13_27.9: ref C = converted %.loc13_27.1, %.loc13_27.8 +// CHECK:STDOUT: %.loc13_27.10: C = bind_value %.loc13_27.9 +// CHECK:STDOUT: %a: C = bind_name a, %.loc13_27.10 +// CHECK:STDOUT: %AdaptC.ref.loc21: type = name_ref AdaptC, %AdaptC.decl [template = constants.%AdaptC] +// CHECK:STDOUT: %a.ref: C = name_ref a, %a +// CHECK:STDOUT: %b: AdaptC = bind_name b, +// CHECK:STDOUT: %C.ref.loc27: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %b.ref: AdaptC = name_ref b, %b +// CHECK:STDOUT: %c: C = bind_name c, +// CHECK:STDOUT: %MakeC: = fn_decl @MakeC [template] { +// CHECK:STDOUT: %C.ref.loc29: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: @MakeC.%return: ref C = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %MakeAdaptC: = fn_decl @MakeAdaptC [template] { +// CHECK:STDOUT: %AdaptC.ref.loc31: type = name_ref AdaptC, %AdaptC.decl [template = constants.%AdaptC] +// CHECK:STDOUT: @MakeAdaptC.%return: ref AdaptC = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %AdaptC.ref.loc37: type = name_ref AdaptC, %AdaptC.decl [template = constants.%AdaptC] +// CHECK:STDOUT: %d.var: ref AdaptC = var d +// CHECK:STDOUT: %d: ref AdaptC = bind_name d, %d.var +// CHECK:STDOUT: %C.ref.loc42: type = name_ref C, %C.decl [template = constants.%C] +// CHECK:STDOUT: %e.var: ref C = var e +// CHECK:STDOUT: %e: ref C = bind_name e, %e.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %.loc5: = field_decl a, element0 [template] +// CHECK:STDOUT: %.loc6: = field_decl b, element1 [template] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .a = %.loc5 +// CHECK:STDOUT: .b = %.loc6 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @AdaptC { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [template = constants.%C] +// CHECK:STDOUT: adapt_decl C +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%AdaptC +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @MakeC() -> %return: C; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @MakeAdaptC() -> %return: AdaptC; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %MakeC.ref: = name_ref MakeC, file.%MakeC [template = file.%MakeC] +// CHECK:STDOUT: %.loc37_22.1: ref C = temporary_storage +// CHECK:STDOUT: %.loc37_22.2: init C = call %MakeC.ref() to %.loc37_22.1 +// CHECK:STDOUT: assign file.%d.var, +// CHECK:STDOUT: %MakeAdaptC.ref: = name_ref MakeAdaptC, file.%MakeAdaptC [template = file.%MakeAdaptC] +// CHECK:STDOUT: %.loc42_22.1: ref AdaptC = temporary_storage +// CHECK:STDOUT: %.loc42_22.2: init AdaptC = call %MakeAdaptC.ref() to %.loc42_22.1 +// CHECK:STDOUT: assign file.%e.var, +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 9fe6be0fff8e..c0f2fc9744bb 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -183,16 +183,20 @@ CARBON_DIAGNOSTIC_KIND(InvalidBuiltinSignature) CARBON_DIAGNOSTIC_KIND(FunctionExternMismatch) // Class checking. +CARBON_DIAGNOSTIC_KIND(AdaptWithBase) +CARBON_DIAGNOSTIC_KIND(AdaptWithFields) +CARBON_DIAGNOSTIC_KIND(AdaptBaseHere) +CARBON_DIAGNOSTIC_KIND(AdaptFieldHere) CARBON_DIAGNOSTIC_KIND(BaseIsFinal) CARBON_DIAGNOSTIC_KIND(BaseMissingExtend) -CARBON_DIAGNOSTIC_KIND(BaseOutsideClass) -CARBON_DIAGNOSTIC_KIND(BasePrevious) -CARBON_DIAGNOSTIC_KIND(BaseRepeated) CARBON_DIAGNOSTIC_KIND(ClassForwardDeclaredHere) CARBON_DIAGNOSTIC_KIND(ClassPreviousDefinition) CARBON_DIAGNOSTIC_KIND(ClassRedeclarationDifferentIntroducer) CARBON_DIAGNOSTIC_KIND(ClassRedeclarationDifferentIntroducerPrevious) CARBON_DIAGNOSTIC_KIND(ClassRedefinition) +CARBON_DIAGNOSTIC_KIND(ClassSpecificDeclOutsideClass) +CARBON_DIAGNOSTIC_KIND(ClassSpecificDeclPrevious) +CARBON_DIAGNOSTIC_KIND(ClassSpecificDeclRepeated) CARBON_DIAGNOSTIC_KIND(ClassIncompleteWithinDefinition) CARBON_DIAGNOSTIC_KIND(ConstructionOfAbstractClass) @@ -260,6 +264,7 @@ CARBON_DIAGNOSTIC_KIND(NameNotFound) CARBON_DIAGNOSTIC_KIND(NameDeclDuplicate) CARBON_DIAGNOSTIC_KIND(NameDeclPrevious) CARBON_DIAGNOSTIC_KIND(RepeatedConst) +CARBON_DIAGNOSTIC_KIND(IncompleteTypeInAdaptDecl) CARBON_DIAGNOSTIC_KIND(IncompleteTypeInBaseDecl) CARBON_DIAGNOSTIC_KIND(IncompleteTypeInConversion) CARBON_DIAGNOSTIC_KIND(IncompleteTypeInFunctionParam) diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 437098b1de57..93e585ede376 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -26,6 +26,11 @@ static auto FatalErrorIfEncountered(InstT inst) -> void { << inst; } +auto HandleAdaptDecl(FunctionContext& /*context*/, SemIR::InstId /*inst_id*/, + SemIR::AdaptDecl inst) -> void { + FatalErrorIfEncountered(inst); +} + auto HandleAddrOf(FunctionContext& context, SemIR::InstId inst_id, SemIR::AddrOf inst) -> void { context.SetLocal(inst_id, context.GetValue(inst.lvalue_id)); diff --git a/toolchain/lower/testdata/class/adapt.carbon b/toolchain/lower/testdata/class/adapt.carbon new file mode 100644 index 000000000000..95cd5fbaedf4 --- /dev/null +++ b/toolchain/lower/testdata/class/adapt.carbon @@ -0,0 +1,26 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE + +class PairOfInts { + var a: i32; + var b: i32; +} + +class PairAdapter { + adapt PairOfInts; +} + +fn MakePair() -> PairAdapter { + returned var v: PairAdapter; + return var; +} + +// CHECK:STDOUT: ; ModuleID = 'adapt.carbon' +// CHECK:STDOUT: source_filename = "adapt.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define void @MakePair(ptr sret({ i32, i32 }) %return) { +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } diff --git a/toolchain/parse/testdata/class/fail_modifiers.carbon b/toolchain/parse/testdata/class/fail_modifiers.carbon index 5daf57a0345b..8a28a3604ba2 100644 --- a/toolchain/parse/testdata/class/fail_modifiers.carbon +++ b/toolchain/parse/testdata/class/fail_modifiers.carbon @@ -4,26 +4,59 @@ // // AUTOUPDATE -virtual class B +// --- fail_virtual_class.carbon -// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+7]]:1: ERROR: `virtual` declarations must either end with a `;` or have a `{ ... }` block for a definition. +virtual class B +// CHECK:STDERR: fail_virtual_class.carbon:[[@LINE+4]]:1: ERROR: `virtual` declarations must either end with a `;` or have a `{ ... }` block for a definition. +// CHECK:STDERR: +// CHECK:STDERR: ^ +// CHECK:STDERR: + +// --- fail_impl_class.carbon + +// CHECK:STDERR: fail_impl_class.carbon:[[@LINE+4]]:1: ERROR: `impl` introducer should be followed by a name. // CHECK:STDERR: impl class // CHECK:STDERR: ^~~~ // CHECK:STDERR: -// CHECK:STDERR: fail_modifiers.carbon:[[@LINE+3]]:6: ERROR: `class` introducer should be followed by a name. -// CHECK:STDERR: impl class -// CHECK:STDERR: ^~~~~ impl class -// CHECK:STDOUT: - filename: fail_modifiers.carbon +// --- fail_extend_adapt_base.carbon + +class C { + // CHECK:STDERR: fail_extend_adapt_base.carbon:[[@LINE+3]]:16: ERROR: Expected expression. + // CHECK:STDERR: extend adapt base B; + // CHECK:STDERR: ^~~~ + extend adapt base B; +} + +// CHECK:STDOUT: - filename: fail_virtual_class.carbon // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, // CHECK:STDOUT: {kind: 'VirtualModifier', text: 'virtual'}, // CHECK:STDOUT: {kind: 'IdentifierName', text: 'B'}, -// CHECK:STDOUT: {kind: 'ClassDecl', text: 'impl', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ClassDecl', text: 'B', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_impl_class.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, // CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'ImplModifier', text: 'impl'}, // CHECK:STDOUT: {kind: 'InvalidParse', text: '', has_error: yes}, -// CHECK:STDOUT: {kind: 'ClassDecl', text: 'class', has_error: yes, subtree_size: 3}, +// CHECK:STDOUT: {kind: 'ClassDecl', text: 'class', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_extend_adapt_base.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'ClassIntroducer', text: 'class'}, +// CHECK:STDOUT: {kind: 'IdentifierName', text: 'C'}, +// CHECK:STDOUT: {kind: 'ClassDefinitionStart', text: '{', subtree_size: 3}, +// CHECK:STDOUT: {kind: 'AdaptIntroducer', text: 'adapt'}, +// CHECK:STDOUT: {kind: 'ExtendModifier', text: 'extend'}, +// CHECK:STDOUT: {kind: 'InvalidParse', text: 'base', has_error: yes}, +// CHECK:STDOUT: {kind: 'AdaptDecl', text: ';', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ClassDefinition', text: '}', subtree_size: 8}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] diff --git a/toolchain/sem_ir/class.h b/toolchain/sem_ir/class.h index 132c6f7e0f37..b8c63d3b1adb 100644 --- a/toolchain/sem_ir/class.h +++ b/toolchain/sem_ir/class.h @@ -56,6 +56,11 @@ struct Class : public Printable { // The following members are accumulated throughout the class definition. + // The adapted type declaration, if any. Invalid if the class is not an + // adapter. This is an AdaptDecl instruction. + // TODO: Consider sharing the storage for `adapt_id` and `base_id`. A class + // can't have both. + InstId adapt_id = InstId::Invalid; // The base class declaration. Invalid if the class has no base class. This is // a BaseDecl instruction. InstId base_id = InstId::Invalid; diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 6d7017618de8..7c9422ac735d 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -221,6 +221,7 @@ static auto GetTypePrecedence(InstKind kind) -> int { case PointerType::Kind: return -2; + case AdaptDecl::Kind: case AddrOf::Kind: case AddrPattern::Kind: case ArrayIndex::Kind: @@ -477,6 +478,7 @@ static auto StringifyTypeExprImpl(const SemIR::File& outer_sem_ir, } break; } + case AdaptDecl::Kind: case AddrOf::Kind: case AddrPattern::Kind: case ArrayIndex::Kind: @@ -560,6 +562,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { while (true) { auto untyped_inst = ir->insts().Get(inst_id); CARBON_KIND_SWITCH(untyped_inst) { + case AdaptDecl::Kind: case Assign::Kind: case BaseDecl::Kind: case Branch::Kind: diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 2b765480e44e..d1f4d1aa40d1 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -17,6 +17,7 @@ // For each instruction kind declared here there is a matching definition in // `typed_insts.h`. +CARBON_SEM_IR_INST_KIND(AdaptDecl) CARBON_SEM_IR_INST_KIND(AddrOf) CARBON_SEM_IR_INST_KIND(AddrPattern) CARBON_SEM_IR_INST_KIND(ArrayIndex) diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 36a5882a2a05..9a90947d5c76 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -44,6 +44,15 @@ namespace Carbon::SemIR { +// An adapted type declaration in a class, of the form `adapt T;`. +struct AdaptDecl { + static constexpr auto Kind = + InstKind::AdaptDecl.Define("adapt_decl"); + + // No type_id; this is not a value. + TypeId adapted_type_id; +}; + struct AddrOf { // TODO: Make Parse::NodeId more specific. static constexpr auto Kind =