From 6b28213b36982132d76707533e0801a1e675c67d Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 15 Dec 2025 09:45:01 -0800 Subject: [PATCH] Add interop support for naming and "calling" C++ templates. (#6474) Expose C++ class templates, variable templates, alias templates, and concepts as callable values in Carbon, and map calls to them into template-id formation, mirroring how Carbon generics behave. For now, only type template parameters are supported; non-type and template template parameters produce a TODO error. --- toolchain/check/call.cpp | 4 + toolchain/check/cpp/call.cpp | 149 ++++++++++++++ toolchain/check/cpp/call.h | 10 + toolchain/check/cpp/import.cpp | 66 ++++--- toolchain/check/cpp/import.h | 24 ++- toolchain/check/cpp/type_mapping.cpp | 7 +- toolchain/check/cpp/type_mapping.h | 7 + toolchain/check/handle_literal.cpp | 12 +- toolchain/check/literal.cpp | 8 + toolchain/check/literal.h | 4 + .../testdata/interop/cpp/class/class.carbon | 48 +++-- .../testdata/interop/cpp/class/union.carbon | 48 +++-- .../cpp/template/alias_template.carbon | 147 ++++++++++++++ .../cpp/template/argument_count.carbon | 182 ++++++++++++++++++ .../cpp/template/class_template.carbon | 110 +++++++++++ .../interop/cpp/template/concept.carbon | 126 ++++++++++++ .../interop/cpp/template/generic_call.carbon | 38 ++++ .../cpp/template/non_type_param.carbon | 121 ++++++++++++ .../template/template_template_param.carbon | 63 ++++++ .../interop/cpp/template/type_param.carbon | 75 ++++++++ .../interop/cpp/template/var_template.carbon | 139 +++++++++++++ .../interop/cpp/unsupported_decl_type.carbon | 52 ++--- toolchain/check/type.cpp | 6 + toolchain/check/type.h | 4 + toolchain/check/type_completion.cpp | 8 +- toolchain/lower/file_context.cpp | 3 +- toolchain/sem_ir/clang_decl.h | 14 -- toolchain/sem_ir/formatter.cpp | 7 + toolchain/sem_ir/id_kind.h | 1 + toolchain/sem_ir/ids.h | 14 ++ toolchain/sem_ir/inst_fingerprinter.cpp | 7 + toolchain/sem_ir/inst_kind.def | 1 + toolchain/sem_ir/inst_namer.cpp | 10 + toolchain/sem_ir/stringify.cpp | 5 + toolchain/sem_ir/type_iterator.cpp | 1 + toolchain/sem_ir/typed_insts.h | 16 ++ 36 files changed, 1411 insertions(+), 126 deletions(-) create mode 100644 toolchain/check/testdata/interop/cpp/template/alias_template.carbon create mode 100644 toolchain/check/testdata/interop/cpp/template/argument_count.carbon create mode 100644 toolchain/check/testdata/interop/cpp/template/class_template.carbon create mode 100644 toolchain/check/testdata/interop/cpp/template/concept.carbon create mode 100644 toolchain/check/testdata/interop/cpp/template/generic_call.carbon create mode 100644 toolchain/check/testdata/interop/cpp/template/non_type_param.carbon create mode 100644 toolchain/check/testdata/interop/cpp/template/template_template_param.carbon create mode 100644 toolchain/check/testdata/interop/cpp/template/type_param.carbon create mode 100644 toolchain/check/testdata/interop/cpp/template/var_template.carbon diff --git a/toolchain/check/call.cpp b/toolchain/check/call.cpp index ca9270148e55..eea60809e6ad 100644 --- a/toolchain/check/call.cpp +++ b/toolchain/check/call.cpp @@ -324,6 +324,10 @@ static auto PerformCallToNonFunction(Context& context, SemIR::LocId loc_id, auto type_inst = context.types().GetAsInst(context.insts().Get(callee_id).type_id()); CARBON_KIND_SWITCH(type_inst) { + case CARBON_KIND(SemIR::CppTemplateNameType template_name): { + return PerformCallToCppTemplateName(context, loc_id, + template_name.decl_id, arg_ids); + } case CARBON_KIND(SemIR::GenericClassType generic_class): { return PerformCallToGenericClass(context, loc_id, generic_class.class_id, generic_class.enclosing_specific_id, diff --git a/toolchain/check/cpp/call.cpp b/toolchain/check/cpp/call.cpp index d4d990002e2b..33ac0c9b1414 100644 --- a/toolchain/check/cpp/call.cpp +++ b/toolchain/check/cpp/call.cpp @@ -4,10 +4,15 @@ #include "toolchain/check/cpp/call.h" +#include "clang/Sema/Sema.h" #include "toolchain/base/kind_switch.h" #include "toolchain/check/call.h" +#include "toolchain/check/cpp/import.h" +#include "toolchain/check/cpp/location.h" #include "toolchain/check/cpp/operators.h" #include "toolchain/check/cpp/overload_resolution.h" +#include "toolchain/check/cpp/type_mapping.h" +#include "toolchain/check/literal.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/typed_insts.h" @@ -43,4 +48,148 @@ auto PerformCallToCppFunction(Context& context, SemIR::LocId loc_id, } } +// Converts an argument in a call to a C++ template name into a corresponding +// clang template argument, given the template parameter it will be matched +// against. +static auto ConvertArgToTemplateArg(Context& context, + const clang::NamedDecl* param_decl, + SemIR::InstId arg_id) + -> std::optional { + if (isa(param_decl)) { + auto type = ExprAsType(context, SemIR::LocId(arg_id), arg_id); + if (type.type_id == SemIR::ErrorInst::TypeId) { + return std::nullopt; + } + auto clang_type = MapToCppType(context, type.type_id); + if (clang_type.isNull()) { + context.TODO(arg_id, "unsupported type used as template argument"); + return std::nullopt; + } + return clang::TemplateArgumentLoc( + clang_type, + context.ast_context().getTrivialTypeSourceInfo( + clang_type, GetCppLocation(context, SemIR::LocId(arg_id)))); + } + + if (isa(param_decl)) { + // TODO: Check the type of the argument `CppTemplateNameType` and + // convert it to a `clang::TemplateName`. + context.TODO(arg_id, "argument for template template parameter"); + return std::nullopt; + } + + if (isa(param_decl)) { + // TODO: Check the argument has a concrete constant value, and convert it to + // a Clang constant value. + context.TODO(arg_id, "argument for non-type template parameter"); + return std::nullopt; + } + + CARBON_FATAL("Unknown declaration kind for template parameter"); +} + +// Converts a call argument list into a Clang template argument list for a given +// template. Returns true on success, or false if an error was diagnosed. +static auto ConvertArgsToTemplateArgs(Context& context, + clang::TemplateDecl* template_decl, + llvm::ArrayRef arg_ids, + clang::TemplateArgumentListInfo& arg_list) + -> bool { + for (auto* param_decl : template_decl->getTemplateParameters()->asArray()) { + if (arg_ids.empty()) { + return true; + } + + // A parameter pack consumes all remaining arguments; otherwise, it consumes + // a single argument. + // TODO: Handle expanded template parameter packs, which have a known, fixed + // arity. + llvm::ArrayRef args_for_param = + param_decl->isTemplateParameterPack() ? std::exchange(arg_ids, {}) + : arg_ids.consume_front(); + for (auto arg_id : args_for_param) { + if (auto arg = ConvertArgToTemplateArg(context, param_decl, arg_id)) { + arg_list.addArgument(*arg); + } else { + return false; + } + } + } + + // If there are any remaining arguments, that's an error; convert them to + // placeholder template arguments so that Clang will diagnose it for us. + for (auto arg_id : arg_ids) { + // Synthesize a placeholder `void{}` template argument. + auto arg_loc = GetCppLocation(context, SemIR::LocId(arg_id)); + auto void_type = context.ast_context().VoidTy; + auto* arg = new (context.ast_context()) clang::CXXScalarValueInitExpr( + void_type, + context.ast_context().getTrivialTypeSourceInfo(void_type, arg_loc), + arg_loc); + arg_list.addArgument(clang::TemplateArgumentLoc( + clang::TemplateArgument(arg, /*IsCanonical=*/false), arg)); + } + + return true; +} + +// Given a template and an template argument list, builds a Carbon value +// describing the corresponding C++ template-id. +static auto BuildTemplateId(Context& context, SemIR::LocId loc_id, + clang::SourceLocation loc, + clang::TemplateDecl* template_decl, + clang::TemplateArgumentListInfo& arg_list) + -> SemIR::InstId { + if (auto* var_template_decl = + dyn_cast(template_decl)) { + auto decl_result = context.clang_sema().CheckVarTemplateId( + var_template_decl, /*TemplateLoc=*/clang::SourceLocation(), loc, + arg_list, /*SetWrittenArgs=*/false); + return decl_result.isInvalid() + ? SemIR::ErrorInst::InstId + : ImportCppDecl(context, loc_id, + SemIR::ClangDeclKey::ForNonFunctionDecl( + decl_result.get())); + } + + if (auto* concept_decl = dyn_cast(template_decl)) { + auto expr_result = context.clang_sema().CheckConceptTemplateId( + clang::CXXScopeSpec(), /*TemplateKWLoc=*/clang::SourceLocation(), + clang::DeclarationNameInfo(concept_decl->getDeclName(), loc), + concept_decl, concept_decl, &arg_list); + if (expr_result.isInvalid()) { + return SemIR::ErrorInst::InstId; + } + auto* expr = expr_result.getAs(); + return MakeBoolLiteral(context, loc_id, + SemIR::BoolValue::From(expr->isSatisfied())); + } + + clang::TemplateName template_name(template_decl); + auto clang_type = context.clang_sema().CheckTemplateIdType( + clang::ElaboratedTypeKeyword::None, template_name, loc, arg_list, + /*Scope=*/nullptr, /*ForNestedNameSpecifier=*/false); + if (clang_type.isNull()) { + return SemIR::ErrorInst::InstId; + } + return ImportCppType(context, loc_id, clang_type).inst_id; +} + +auto PerformCallToCppTemplateName(Context& context, SemIR::LocId loc_id, + SemIR::ClangDeclId template_decl_id, + llvm::ArrayRef arg_ids) + -> SemIR::InstId { + auto* template_decl = dyn_cast( + context.clang_decls().Get(template_decl_id).key.decl); + auto loc = GetCppLocation(context, loc_id); + + // Form a template argument list for this template. + clang::TemplateArgumentListInfo arg_list(loc, loc); + if (!ConvertArgsToTemplateArgs(context, template_decl, arg_ids, arg_list)) { + return SemIR::ErrorInst::InstId; + } + + return BuildTemplateId(context, loc_id, loc, template_decl, arg_list); +} + } // namespace Carbon::Check diff --git a/toolchain/check/cpp/call.h b/toolchain/check/cpp/call.h index 02e8b7907839..940c732e1417 100644 --- a/toolchain/check/cpp/call.h +++ b/toolchain/check/cpp/call.h @@ -31,6 +31,16 @@ auto PerformCallToCppFunction(Context& context, SemIR::LocId loc_id, llvm::ArrayRef arg_ids) -> SemIR::InstId; +// Checks and builds SemIR for a call to a C++ template name with arguments +// `arg_ids`. +// +// Converts the arguments to a C++ template argument list and attempts to +// instantiate a template specialization and import a declaration of it. +auto PerformCallToCppTemplateName(Context& context, SemIR::LocId loc_id, + SemIR::ClangDeclId template_decl_id, + llvm::ArrayRef arg_ids) + -> SemIR::InstId; + } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_CALL_H_ diff --git a/toolchain/check/cpp/import.cpp b/toolchain/check/cpp/import.cpp index 16a54d550a07..52a854f62ee1 100644 --- a/toolchain/check/cpp/import.cpp +++ b/toolchain/check/cpp/import.cpp @@ -293,9 +293,6 @@ static auto ImportNamespaceDecl(Context& context, return result.inst_id; } -static auto ImportTypeAndDependencies(Context& context, SemIR::LocId loc_id, - clang::QualType type) -> TypeExpr; - // Creates a class declaration for the given class name in the given scope. // Returns the `InstId` for the declaration. static auto BuildClassDecl(Context& context, @@ -470,7 +467,7 @@ static auto ImportClassObjectRepr(Context& context, SemIR::ClassId class_id, } auto [base_type_inst_id, base_type_id] = - ImportTypeAndDependencies(context, import_ir_inst_id, base.getType()); + ImportCppType(context, import_ir_inst_id, base.getType()); if (!base_type_id.has_value()) { // TODO: If the base class's type can't be mapped, skip it. continue; @@ -552,7 +549,7 @@ static auto ImportClassObjectRepr(Context& context, SemIR::ClassId class_id, auto field_name_id = AddIdentifierName(context, field->getName()); auto [field_type_inst_id, field_type_id] = - ImportTypeAndDependencies(context, import_ir_inst_id, field->getType()); + ImportCppType(context, import_ir_inst_id, field->getType()); if (!field_type_inst_id.has_value()) { // TODO: For now, just skip over fields whose types we can't map. continue; @@ -1631,6 +1628,38 @@ static auto ImportVarDecl(Context& context, SemIR::LocId loc_id, return var_storage_inst_id; } +static auto ImportTemplateDecl(Context& context, + clang::TemplateDecl* template_decl) + -> SemIR::InstId { + auto key = SemIR::ClangDeclKey(template_decl); + + // TODO: Avoid doing this lookup both here and in the insertion below. + if (SemIR::InstId existing_inst_id = LookupClangDeclInstId(context, key); + existing_inst_id.has_value()) { + return existing_inst_id; + } + + // Add a placeholder instruction to resolve cycle between the clang + // declaration and the type. + auto import_loc_id = + AddImportIRInst(context.sem_ir(), template_decl->getLocation()); + SemIR::StructValue value = {.type_id = SemIR::TypeId::None, + .elements_id = SemIR::InstBlockId::Empty}; + auto inst_id = AddPlaceholderImportedInstInNoBlock( + context, MakeImportedLocIdAndInst(context, import_loc_id, value)); + + // Create a type for the constant value. + auto name_id = context.entity_names().Add( + {.name_id = AddIdentifierName(context, template_decl->getName()), + .parent_scope_id = GetParentNameScopeId(context, template_decl)}); + auto decl_id = context.clang_decls().Add({.key = key, .inst_id = inst_id}); + value.type_id = GetCppTemplateNameType(context, name_id, decl_id); + + // Update the value with its type. + ReplaceInstBeforeConstantUse(context, inst_id, value); + return inst_id; +} + // Imports a declaration from Clang to Carbon. Returns the instruction for the // new Carbon declaration, which will be an ErrorInst on failure. Assumes all // dependencies have already been imported. @@ -1673,6 +1702,9 @@ static auto ImportDeclAfterDependencies(Context& context, SemIR::LocId loc_id, if (auto* var_decl = dyn_cast(clang_decl)) { return ImportVarDecl(context, loc_id, var_decl); } + if (auto* template_decl = dyn_cast(clang_decl)) { + return ImportTemplateDecl(context, template_decl); + } context.TODO(AddImportIRInst(context.sem_ir(), clang_decl->getLocation()), llvm::formatv("Unsupported: Declaration type {0}", @@ -1718,12 +1750,8 @@ static auto ImportDeclSet(Context& context, SemIR::LocId loc_id, return true; } -// Imports a declaration from Clang to Carbon. If successful, returns the -// instruction for the new Carbon declaration. All unimported dependencies are -// imported first. -static auto ImportDeclAndDependencies(Context& context, SemIR::LocId loc_id, - SemIR::ClangDeclKey key) - -> SemIR::InstId { +auto ImportCppDecl(Context& context, SemIR::LocId loc_id, + SemIR::ClangDeclKey key) -> SemIR::InstId { // Collect dependencies by walking the dependency graph in depth-first order. ImportWorklist worklist; AddDependentDecl(context, key, worklist); @@ -1733,10 +1761,8 @@ static auto ImportDeclAndDependencies(Context& context, SemIR::LocId loc_id, return LookupClangDeclInstId(context, key); } -// Imports a type from Clang to Carbon. If successful, returns the imported -// TypeId. All unimported dependencies are imported first. -static auto ImportTypeAndDependencies(Context& context, SemIR::LocId loc_id, - clang::QualType type) -> TypeExpr { +auto ImportCppType(Context& context, SemIR::LocId loc_id, clang::QualType type) + -> TypeExpr { // Collect dependencies by walking the dependency graph in depth-first order. ImportWorklist worklist; AddDependentUnimportedTypeDecls(context, type, worklist); @@ -1747,14 +1773,6 @@ static auto ImportTypeAndDependencies(Context& context, SemIR::LocId loc_id, return MapType(context, loc_id, type); } -auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id, - clang::FunctionDecl* clang_decl, int num_params) - -> SemIR::InstId { - return ImportDeclAndDependencies( - context, loc_id, - SemIR::ClangDeclKey::ForFunctionDecl(clang_decl, num_params)); -} - // Imports a Clang declaration into Carbon and adds that name into the // `NameScope`. static auto ImportNameDeclIntoScope(Context& context, SemIR::LocId loc_id, @@ -1763,7 +1781,7 @@ static auto ImportNameDeclIntoScope(Context& context, SemIR::LocId loc_id, SemIR::ClangDeclKey key, SemIR::AccessKind access_kind) -> SemIR::ScopeLookupResult { - SemIR::InstId inst_id = ImportDeclAndDependencies(context, loc_id, key); + SemIR::InstId inst_id = ImportCppDecl(context, loc_id, key); if (!inst_id.has_value()) { return SemIR::ScopeLookupResult::MakeNotFound(); } diff --git a/toolchain/check/cpp/import.h b/toolchain/check/cpp/import.h index 1d0b9e505b82..b6bc70f63223 100644 --- a/toolchain/check/cpp/import.h +++ b/toolchain/check/cpp/import.h @@ -10,6 +10,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/VirtualFileSystem.h" #include "toolchain/check/context.h" +#include "toolchain/check/convert.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/diagnostics/diagnostic_emitter.h" #include "toolchain/sem_ir/ids.h" @@ -24,12 +25,29 @@ auto ImportCpp(Context& context, llvm::IntrusiveRefCntPtr fs, std::shared_ptr invocation) -> void; +// Imports a declaration from Clang to Carbon. If successful, returns the new +// Carbon declaration `InstId`. If the declaration was already imported, returns +// the mapped instruction. All unimported dependencies are imported first. +auto ImportCppDecl(Context& context, SemIR::LocId loc_id, + SemIR::ClangDeclKey key) -> SemIR::InstId; + // Imports a function declaration from Clang to Carbon. If successful, returns // the new Carbon function declaration `InstId`. If the declaration was already // imported, returns the mapped instruction. -auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id, - clang::FunctionDecl* clang_decl, int num_params) - -> SemIR::InstId; +inline auto ImportCppFunctionDecl(Context& context, SemIR::LocId loc_id, + clang::FunctionDecl* clang_decl, + int num_params) -> SemIR::InstId { + return ImportCppDecl( + context, loc_id, + SemIR::ClangDeclKey::ForFunctionDecl(clang_decl, num_params)); +} + +// Imports a function declaration from Clang to Carbon. If successful, returns +// the new Carbon function declaration `InstId`. If the declaration was already +// imported, returns the mapped instruction. All unimported dependencies are +// imported first. +auto ImportCppType(Context& context, SemIR::LocId loc_id, clang::QualType type) + -> TypeExpr; // Imports an overloaded function set from Clang to Carbon. auto ImportCppOverloadSet( diff --git a/toolchain/check/cpp/type_mapping.cpp b/toolchain/check/cpp/type_mapping.cpp index 54d46de67c04..76798c83a753 100644 --- a/toolchain/check/cpp/type_mapping.cpp +++ b/toolchain/check/cpp/type_mapping.cpp @@ -284,11 +284,8 @@ static auto TryMapType(Context& context, SemIR::TypeId type_id) return clang::QualType(); } -// Maps a Carbon type to a C++ type. Returns `clang::QualType` if the mapping -// succeeds, or `clang::QualType::isNull()` if the type is not supported. -// TODO: unify this with the C++ to Carbon type mapping function. -static auto MapToCppType(Context& context, SemIR::TypeId type_id) - -> clang::QualType { +auto MapToCppType(Context& context, SemIR::TypeId type_id) -> clang::QualType { + // TODO: unify this with the C++ to Carbon type mapping function. llvm::SmallVector wrap_fns; while (true) { CARBON_KIND_SWITCH(TryMapType(context, type_id)) { diff --git a/toolchain/check/cpp/type_mapping.h b/toolchain/check/cpp/type_mapping.h index 8b6ffb0ba2d9..016d1ea4b62d 100644 --- a/toolchain/check/cpp/type_mapping.h +++ b/toolchain/check/cpp/type_mapping.h @@ -7,10 +7,17 @@ #include "clang/AST/Type.h" #include "toolchain/check/context.h" +#include "toolchain/check/convert.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { +// Converts a Carbon type to a corresponding C++ type. This uses the default +// type mapping, which is suitable for template arguments, typedefs, etc. but +// may not be the right mapping to use in a function signature. Returns a null +// type if there is no mapping. +auto MapToCppType(Context& context, SemIR::TypeId type_id) -> clang::QualType; + // Invents a Clang argument expression to use in overload resolution to // represent the given Carbon argument instruction. auto InventClangArg(Context& context, SemIR::InstId arg_id) -> clang::Expr*; diff --git a/toolchain/check/handle_literal.cpp b/toolchain/check/handle_literal.cpp index de6be1320272..22a1f6c5a19a 100644 --- a/toolchain/check/handle_literal.cpp +++ b/toolchain/check/handle_literal.cpp @@ -20,19 +20,15 @@ namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::BoolLiteralFalseId node_id) -> bool { - AddInstAndPush( - context, node_id, - {.type_id = GetSingletonType(context, SemIR::BoolType::TypeInstId), - .value = SemIR::BoolValue::False}); + context.node_stack().Push( + node_id, MakeBoolLiteral(context, node_id, SemIR::BoolValue::False)); return true; } auto HandleParseNode(Context& context, Parse::BoolLiteralTrueId node_id) -> bool { - AddInstAndPush( - context, node_id, - {.type_id = GetSingletonType(context, SemIR::BoolType::TypeInstId), - .value = SemIR::BoolValue::True}); + context.node_stack().Push( + node_id, MakeBoolLiteral(context, node_id, SemIR::BoolValue::True)); return true; } diff --git a/toolchain/check/literal.cpp b/toolchain/check/literal.cpp index 9c0b8a267b86..df4ddefdde99 100644 --- a/toolchain/check/literal.cpp +++ b/toolchain/check/literal.cpp @@ -17,6 +17,14 @@ namespace Carbon::Check { +auto MakeBoolLiteral(Context& context, SemIR::LocId loc_id, + SemIR::BoolValue value) -> SemIR::InstId { + return AddInst( + context, loc_id, + {.type_id = GetSingletonType(context, SemIR::BoolType::TypeInstId), + .value = value}); +} + auto MakeIntLiteral(Context& context, Parse::NodeId node_id, IntId int_id) -> SemIR::InstId { return AddInst( diff --git a/toolchain/check/literal.h b/toolchain/check/literal.h index a8f638b127e1..b71ac814e07d 100644 --- a/toolchain/check/literal.h +++ b/toolchain/check/literal.h @@ -13,6 +13,10 @@ namespace Carbon::Check { +// Forms a BoolLiteral instruction with the given value and returns it. +auto MakeBoolLiteral(Context& context, SemIR::LocId loc_id, + SemIR::BoolValue value) -> SemIR::InstId; + // Forms an IntValue instruction with type `IntLiteral` for a given literal // integer value, which is assumed to be unsigned. auto MakeIntLiteral(Context& context, Parse::NodeId node_id, IntId int_id) diff --git a/toolchain/check/testdata/interop/cpp/class/class.carbon b/toolchain/check/testdata/interop/cpp/class/class.carbon index e06c4709f4d0..34ba95819c9a 100644 --- a/toolchain/check/testdata/interop/cpp/class/class.carbon +++ b/toolchain/check/testdata/interop/cpp/class/class.carbon @@ -209,25 +209,19 @@ fn MyF() { // --- template.h +struct X {}; + template class Bar {}; -// --- fail_todo_import_template.carbon +// --- import_template.carbon library "[[@TEST_NAME]]"; -// CHECK:STDERR: fail_todo_import_template.carbon:[[@LINE+4]]:10: in file included here [InCppInclude] -// CHECK:STDERR: ./template.h:3:7: error: semantics TODO: `Unsupported: Declaration type ClassTemplate` [SemanticsTodo] -// CHECK:STDERR: class Bar {}; -// CHECK:STDERR: ^ import Cpp library "template.h"; //@dump-sem-ir-begin -// CHECK:STDERR: fail_todo_import_template.carbon:[[@LINE+4]]:13: note: in `Cpp` name lookup for `Bar` [InCppNameLookup] -// CHECK:STDERR: fn MyF(bar: Cpp.Bar*); -// CHECK:STDERR: ^~~~~~~ -// CHECK:STDERR: -fn MyF(bar: Cpp.Bar*); +fn MyF(bar: Cpp.Bar(Cpp.X)*); //@dump-sem-ir-end // CHECK:STDOUT: --- import_declaration.carbon @@ -564,34 +558,46 @@ fn MyF(bar: Cpp.Bar*); // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_import_template.carbon +// CHECK:STDOUT: --- import_template.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { +// CHECK:STDOUT: %Bar.type: type = cpp_type_template_type Bar [concrete] +// CHECK:STDOUT: %Bar.template: %Bar.type = struct_value () [concrete] +// CHECK:STDOUT: %X: type = class_type @X [concrete] +// CHECK:STDOUT: %Bar: type = class_type @Bar [concrete] +// CHECK:STDOUT: %ptr: type = ptr_type %Bar [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %ptr [concrete] // CHECK:STDOUT: %MyF.type: type = fn_type @MyF [concrete] // CHECK:STDOUT: %MyF: %MyF.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { -// CHECK:STDOUT: .Bar = +// CHECK:STDOUT: .Bar = %Bar.template +// CHECK:STDOUT: .X = %X.decl // CHECK:STDOUT: import Cpp//... // CHECK:STDOUT: } +// CHECK:STDOUT: %Bar.template: %Bar.type = struct_value () [concrete = constants.%Bar.template] +// CHECK:STDOUT: %X.decl: type = class_decl @X [concrete = constants.%X] {} {} +// CHECK:STDOUT: %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] { -// CHECK:STDOUT: %bar.patt: = value_binding_pattern bar [concrete] -// CHECK:STDOUT: %bar.param_patt: = value_param_pattern %bar.patt, call_param0 [concrete] +// CHECK:STDOUT: %bar.patt: %pattern_type = value_binding_pattern bar [concrete] +// CHECK:STDOUT: %bar.param_patt: %pattern_type = value_param_pattern %bar.patt, call_param0 [concrete] // CHECK:STDOUT: } { -// CHECK:STDOUT: %bar.param: = value_param call_param0 -// CHECK:STDOUT: %.loc15: type = splice_block %ptr [concrete = ] { -// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %Bar.ref: = name_ref Bar, [concrete = ] -// CHECK:STDOUT: %ptr: type = ptr_type [concrete = ] +// CHECK:STDOUT: %bar.param: %ptr = value_param call_param0 +// CHECK:STDOUT: %.loc7: type = splice_block %ptr [concrete = constants.%ptr] { +// CHECK:STDOUT: %Cpp.ref.loc7_13: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Bar.ref: %Bar.type = name_ref Bar, imports.%Bar.template [concrete = constants.%Bar.template] +// CHECK:STDOUT: %Cpp.ref.loc7_21: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %X.ref: type = name_ref X, imports.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %ptr: type = ptr_type imports.%Bar.decl [concrete = constants.%ptr] // CHECK:STDOUT: } -// CHECK:STDOUT: %bar: = value_binding bar, %bar.param +// CHECK:STDOUT: %bar: %ptr = value_binding bar, %bar.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @MyF(%bar.param: ); +// CHECK:STDOUT: fn @MyF(%bar.param: %ptr); // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/class/union.carbon b/toolchain/check/testdata/interop/cpp/class/union.carbon index f16023112e5a..4aa97ff0e086 100644 --- a/toolchain/check/testdata/interop/cpp/class/union.carbon +++ b/toolchain/check/testdata/interop/cpp/class/union.carbon @@ -158,25 +158,19 @@ fn MyF(bar : Cpp.Bar*) { // --- template.h +struct X {}; + template union Bar {}; -// --- fail_todo_import_template.carbon +// --- import_template.carbon library "[[@TEST_NAME]]"; -// CHECK:STDERR: fail_todo_import_template.carbon:[[@LINE+4]]:10: in file included here [InCppInclude] -// CHECK:STDERR: ./template.h:3:7: error: semantics TODO: `Unsupported: Declaration type ClassTemplate` [SemanticsTodo] -// CHECK:STDERR: union Bar {}; -// CHECK:STDERR: ^ import Cpp library "template.h"; //@dump-sem-ir-begin -// CHECK:STDERR: fail_todo_import_template.carbon:[[@LINE+4]]:13: note: in `Cpp` name lookup for `Bar` [InCppNameLookup] -// CHECK:STDERR: fn MyF(bar: Cpp.Bar*); -// CHECK:STDERR: ^~~~~~~ -// CHECK:STDERR: -fn MyF(bar: Cpp.Bar*); +fn MyF(bar: Cpp.Bar(Cpp.X)*); //@dump-sem-ir-end // CHECK:STDOUT: --- import_declaration.carbon @@ -402,34 +396,46 @@ fn MyF(bar: Cpp.Bar*); // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_import_template.carbon +// CHECK:STDOUT: --- import_template.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { +// CHECK:STDOUT: %Bar.type: type = cpp_type_template_type Bar [concrete] +// CHECK:STDOUT: %Bar.template: %Bar.type = struct_value () [concrete] +// CHECK:STDOUT: %X: type = class_type @X [concrete] +// CHECK:STDOUT: %Bar: type = class_type @Bar [concrete] +// CHECK:STDOUT: %ptr: type = ptr_type %Bar [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %ptr [concrete] // CHECK:STDOUT: %MyF.type: type = fn_type @MyF [concrete] // CHECK:STDOUT: %MyF: %MyF.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { -// CHECK:STDOUT: .Bar = +// CHECK:STDOUT: .Bar = %Bar.template +// CHECK:STDOUT: .X = %X.decl // CHECK:STDOUT: import Cpp//... // CHECK:STDOUT: } +// CHECK:STDOUT: %Bar.template: %Bar.type = struct_value () [concrete = constants.%Bar.template] +// CHECK:STDOUT: %X.decl: type = class_decl @X [concrete = constants.%X] {} {} +// CHECK:STDOUT: %Bar.decl: type = class_decl @Bar [concrete = constants.%Bar] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] { -// CHECK:STDOUT: %bar.patt: = value_binding_pattern bar [concrete] -// CHECK:STDOUT: %bar.param_patt: = value_param_pattern %bar.patt, call_param0 [concrete] +// CHECK:STDOUT: %bar.patt: %pattern_type = value_binding_pattern bar [concrete] +// CHECK:STDOUT: %bar.param_patt: %pattern_type = value_param_pattern %bar.patt, call_param0 [concrete] // CHECK:STDOUT: } { -// CHECK:STDOUT: %bar.param: = value_param call_param0 -// CHECK:STDOUT: %.loc15: type = splice_block %ptr [concrete = ] { -// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %Bar.ref: = name_ref Bar, [concrete = ] -// CHECK:STDOUT: %ptr: type = ptr_type [concrete = ] +// CHECK:STDOUT: %bar.param: %ptr = value_param call_param0 +// CHECK:STDOUT: %.loc7: type = splice_block %ptr [concrete = constants.%ptr] { +// CHECK:STDOUT: %Cpp.ref.loc7_13: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Bar.ref: %Bar.type = name_ref Bar, imports.%Bar.template [concrete = constants.%Bar.template] +// CHECK:STDOUT: %Cpp.ref.loc7_21: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %X.ref: type = name_ref X, imports.%X.decl [concrete = constants.%X] +// CHECK:STDOUT: %ptr: type = ptr_type imports.%Bar.decl [concrete = constants.%ptr] // CHECK:STDOUT: } -// CHECK:STDOUT: %bar: = value_binding bar, %bar.param +// CHECK:STDOUT: %bar: %ptr = value_binding bar, %bar.param // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @MyF(%bar.param: ); +// CHECK:STDOUT: fn @MyF(%bar.param: %ptr); // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/template/alias_template.carbon b/toolchain/check/testdata/interop/cpp/template/alias_template.carbon new file mode 100644 index 000000000000..6c92a3014e8e --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/template/alias_template.carbon @@ -0,0 +1,147 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/template/alias_template.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/template/alias_template.carbon + +// --- alias_template.h + +struct A {}; +struct B {}; + +template using First = T; +template using Second = U; + +// --- use_alias_template.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "alias_template.h"; + +//@dump-sem-ir-begin +var a1: Cpp.First(Cpp.A, Cpp.B) = Cpp.A.A(); +var b1: Cpp.First(Cpp.B, Cpp.A) = Cpp.B.B(); +var b2: Cpp.Second(Cpp.A, Cpp.B) = Cpp.B.B(); +var a2: Cpp.Second(Cpp.B, Cpp.A) = Cpp.A.A(); +//@dump-sem-ir-end + +// CHECK:STDOUT: --- use_alias_template.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %First.type: type = cpp_type_template_type First [concrete] +// CHECK:STDOUT: %First.template: %First.type = struct_value () [concrete] +// CHECK:STDOUT: %A: type = class_type @A [concrete] +// CHECK:STDOUT: %B: type = class_type @B [concrete] +// CHECK:STDOUT: %pattern_type.9de: type = pattern_type %A [concrete] +// CHECK:STDOUT: %A.A.cpp_overload_set.type: type = cpp_overload_set_type @A.A.cpp_overload_set [concrete] +// CHECK:STDOUT: %A.A.cpp_overload_set.value: %A.A.cpp_overload_set.type = cpp_overload_set_value @A.A.cpp_overload_set [concrete] +// CHECK:STDOUT: %ptr.270: type = ptr_type %A [concrete] +// CHECK:STDOUT: %A__carbon_thunk.type: type = fn_type @A__carbon_thunk [concrete] +// CHECK:STDOUT: %A__carbon_thunk: %A__carbon_thunk.type = struct_value () [concrete] +// CHECK:STDOUT: %pattern_type.d0f: type = pattern_type %B [concrete] +// CHECK:STDOUT: %B.B.cpp_overload_set.type: type = cpp_overload_set_type @B.B.cpp_overload_set [concrete] +// CHECK:STDOUT: %B.B.cpp_overload_set.value: %B.B.cpp_overload_set.type = cpp_overload_set_value @B.B.cpp_overload_set [concrete] +// CHECK:STDOUT: %ptr.a04: type = ptr_type %B [concrete] +// CHECK:STDOUT: %B__carbon_thunk.type: type = fn_type @B__carbon_thunk [concrete] +// CHECK:STDOUT: %B__carbon_thunk: %B__carbon_thunk.type = struct_value () [concrete] +// CHECK:STDOUT: %Second.type: type = cpp_type_template_type Second [concrete] +// CHECK:STDOUT: %Second.template: %Second.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .First = %First.template +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: .Second = %Second.template +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %First.template: %First.type = struct_value () [concrete = constants.%First.template] +// CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} +// CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} +// CHECK:STDOUT: %A.A.cpp_overload_set.value: %A.A.cpp_overload_set.type = cpp_overload_set_value @A.A.cpp_overload_set [concrete = constants.%A.A.cpp_overload_set.value] +// CHECK:STDOUT: %A__carbon_thunk.decl: %A__carbon_thunk.type = fn_decl @A__carbon_thunk [concrete = constants.%A__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %B.B.cpp_overload_set.value: %B.B.cpp_overload_set.type = cpp_overload_set_value @B.B.cpp_overload_set [concrete = constants.%B.B.cpp_overload_set.value] +// CHECK:STDOUT: %B__carbon_thunk.decl: %B__carbon_thunk.type = fn_decl @B__carbon_thunk [concrete = constants.%B__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %Second.template: %Second.type = struct_value () [concrete = constants.%Second.template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a1.patt: %pattern_type.9de = ref_binding_pattern a1 [concrete] +// CHECK:STDOUT: %a1.var_patt: %pattern_type.9de = var_pattern %a1.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a1.var: ref %A = var %a1.var_patt [concrete] +// CHECK:STDOUT: %a1: ref %A = ref_binding a1, %a1.var [concrete = %a1.var] +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b1.patt: %pattern_type.d0f = ref_binding_pattern b1 [concrete] +// CHECK:STDOUT: %b1.var_patt: %pattern_type.d0f = var_pattern %b1.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b1.var: ref %B = var %b1.var_patt [concrete] +// CHECK:STDOUT: %b1: ref %B = ref_binding b1, %b1.var [concrete = %b1.var] +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b2.patt: %pattern_type.d0f = ref_binding_pattern b2 [concrete] +// CHECK:STDOUT: %b2.var_patt: %pattern_type.d0f = var_pattern %b2.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b2.var: ref %B = var %b2.var_patt [concrete] +// CHECK:STDOUT: %b2: ref %B = ref_binding b2, %b2.var [concrete = %b2.var] +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a2.patt: %pattern_type.9de = ref_binding_pattern a2 [concrete] +// CHECK:STDOUT: %a2.var_patt: %pattern_type.9de = var_pattern %a2.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a2.var: ref %A = var %a2.var_patt [concrete] +// CHECK:STDOUT: %a2: ref %A = ref_binding a2, %a2.var [concrete = %a2.var] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref.loc7: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %A.ref.loc7_38: type = name_ref A, imports.%A.decl [concrete = constants.%A] +// CHECK:STDOUT: %A.ref.loc7_40: %A.A.cpp_overload_set.type = name_ref A, imports.%A.A.cpp_overload_set.value [concrete = constants.%A.A.cpp_overload_set.value] +// CHECK:STDOUT: %.loc7_1: ref %A = splice_block file.%a1.var [concrete = file.%a1.var] {} +// CHECK:STDOUT: %addr.loc7: %ptr.270 = addr_of %.loc7_1 +// CHECK:STDOUT: %A__carbon_thunk.call.loc7: init %empty_tuple.type = call imports.%A__carbon_thunk.decl(%addr.loc7) +// CHECK:STDOUT: %.loc7_43: init %A = in_place_init %A__carbon_thunk.call.loc7, %.loc7_1 +// CHECK:STDOUT: assign file.%a1.var, %.loc7_43 +// CHECK:STDOUT: %Cpp.ref.loc8: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %B.ref.loc8_38: type = name_ref B, imports.%B.decl [concrete = constants.%B] +// CHECK:STDOUT: %B.ref.loc8_40: %B.B.cpp_overload_set.type = name_ref B, imports.%B.B.cpp_overload_set.value [concrete = constants.%B.B.cpp_overload_set.value] +// CHECK:STDOUT: %.loc8_1: ref %B = splice_block file.%b1.var [concrete = file.%b1.var] {} +// CHECK:STDOUT: %addr.loc8: %ptr.a04 = addr_of %.loc8_1 +// CHECK:STDOUT: %B__carbon_thunk.call.loc8: init %empty_tuple.type = call imports.%B__carbon_thunk.decl(%addr.loc8) +// CHECK:STDOUT: %.loc8_43: init %B = in_place_init %B__carbon_thunk.call.loc8, %.loc8_1 +// CHECK:STDOUT: assign file.%b1.var, %.loc8_43 +// CHECK:STDOUT: %Cpp.ref.loc9: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %B.ref.loc9_39: type = name_ref B, imports.%B.decl [concrete = constants.%B] +// CHECK:STDOUT: %B.ref.loc9_41: %B.B.cpp_overload_set.type = name_ref B, imports.%B.B.cpp_overload_set.value [concrete = constants.%B.B.cpp_overload_set.value] +// CHECK:STDOUT: %.loc9_1: ref %B = splice_block file.%b2.var [concrete = file.%b2.var] {} +// CHECK:STDOUT: %addr.loc9: %ptr.a04 = addr_of %.loc9_1 +// CHECK:STDOUT: %B__carbon_thunk.call.loc9: init %empty_tuple.type = call imports.%B__carbon_thunk.decl(%addr.loc9) +// CHECK:STDOUT: %.loc9_44: init %B = in_place_init %B__carbon_thunk.call.loc9, %.loc9_1 +// CHECK:STDOUT: assign file.%b2.var, %.loc9_44 +// CHECK:STDOUT: %Cpp.ref.loc10: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %A.ref.loc10_39: type = name_ref A, imports.%A.decl [concrete = constants.%A] +// CHECK:STDOUT: %A.ref.loc10_41: %A.A.cpp_overload_set.type = name_ref A, imports.%A.A.cpp_overload_set.value [concrete = constants.%A.A.cpp_overload_set.value] +// CHECK:STDOUT: %.loc10_1: ref %A = splice_block file.%a2.var [concrete = file.%a2.var] {} +// CHECK:STDOUT: %addr.loc10: %ptr.270 = addr_of %.loc10_1 +// CHECK:STDOUT: %A__carbon_thunk.call.loc10: init %empty_tuple.type = call imports.%A__carbon_thunk.decl(%addr.loc10) +// CHECK:STDOUT: %.loc10_44: init %A = in_place_init %A__carbon_thunk.call.loc10, %.loc10_1 +// CHECK:STDOUT: assign file.%a2.var, %.loc10_44 +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/template/argument_count.carbon b/toolchain/check/testdata/interop/cpp/template/argument_count.carbon new file mode 100644 index 000000000000..e3bbd217f14e --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/template/argument_count.carbon @@ -0,0 +1,182 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon +// EXTRA-ARGS: --clang-arg=-std=c++20 +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/template/argument_count.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/template/argument_count.carbon + +// --- templates.h + +struct A {}; +struct B {}; + +template struct TwoTypes {}; + +template struct TypePack {}; + +template concept True = true; + +// FixedSizePack::Inner has a template parameter pack of fixed +// size `n`, because it's expanded to an arity of exactly `n` when instantiating +// the enclosing template, because the inner pack mentions the outer one. +template struct FixedSizePack { + template ...U> struct Inner {}; +}; + +// --- valid.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +//@dump-sem-ir-begin +var x: Cpp.TwoTypes(Cpp.A, Cpp.B); +//@dump-sem-ir-end + +// --- fail_too_few_arguments.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +// CHECK:STDERR: fail_too_few_arguments.carbon:[[@LINE+8]]:26: error: too few template arguments for class template 'TwoTypes' [CppInteropParseError] +// CHECK:STDERR: 14 | var x: Cpp.TwoTypes(Cpp.A); +// CHECK:STDERR: | ^ +// CHECK:STDERR: fail_too_few_arguments.carbon:[[@LINE-5]]:10: in file included here [InCppInclude] +// CHECK:STDERR: ./templates.h:5:41: note: template is declared here [CppInteropParseNote] +// CHECK:STDERR: 5 | template struct TwoTypes {}; +// CHECK:STDERR: | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ +// CHECK:STDERR: +var x: Cpp.TwoTypes(Cpp.A); + +// --- fail_too_many_arguments.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +// CHECK:STDERR: fail_too_many_arguments.carbon:[[@LINE+8]]:40: error: too many template arguments for class template 'TwoTypes' [CppInteropParseError] +// CHECK:STDERR: 14 | var x: Cpp.TwoTypes(Cpp.A, Cpp.A, Cpp.A); +// CHECK:STDERR: | ~~^ +// CHECK:STDERR: fail_too_many_arguments.carbon:[[@LINE-5]]:10: in file included here [InCppInclude] +// CHECK:STDERR: ./templates.h:5:41: note: template is declared here [CppInteropParseNote] +// CHECK:STDERR: 5 | template struct TwoTypes {}; +// CHECK:STDERR: | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ +// CHECK:STDERR: +var x: Cpp.TwoTypes(Cpp.A, Cpp.A, Cpp.A); + +// --- pack.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +//@dump-sem-ir-begin +var a0: Cpp.TypePack(); +var a1: Cpp.TypePack(Cpp.A); +var a2: Cpp.TypePack(Cpp.A, Cpp.A); +var a3: Cpp.TypePack(Cpp.A, Cpp.A, Cpp.A); +//@dump-sem-ir-end + +// --- fixed_size_pack.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +var x: Cpp.FixedSizePack(Cpp.A, Cpp.A).Inner(Cpp.B, Cpp.B); + +// --- fail_fixed_size_pack_wrong_size.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +// CHECK:STDERR: fail_fixed_size_pack_wrong_size.carbon:[[@LINE+8]]:57: error: too few template arguments for class template 'Inner' [CppInteropParseError] +// CHECK:STDERR: 14 | var too_few: Cpp.FixedSizePack(Cpp.A, Cpp.A).Inner(Cpp.B); +// CHECK:STDERR: | ^ +// CHECK:STDERR: fail_fixed_size_pack_wrong_size.carbon:[[@LINE-5]]:10: in file included here [InCppInclude] +// CHECK:STDERR: ./templates.h:15:33: note: template is declared here [CppInteropParseNote] +// CHECK:STDERR: 15 | template ...U> struct Inner {}; +// CHECK:STDERR: | ~~~~~~~~~~~~~~~~~~~~~~ ^ +// CHECK:STDERR: +var too_few: Cpp.FixedSizePack(Cpp.A, Cpp.A).Inner(Cpp.B); + +// CHECK:STDERR: fail_fixed_size_pack_wrong_size.carbon:[[@LINE+8]]:72: error: too many template arguments for class template 'Inner' [CppInteropParseError] +// CHECK:STDERR: 24 | var too_many: Cpp.FixedSizePack(Cpp.A, Cpp.A).Inner(Cpp.B, Cpp.B, Cpp.B); +// CHECK:STDERR: | ~~^ +// CHECK:STDERR: fail_fixed_size_pack_wrong_size.carbon:[[@LINE-15]]:10: in file included here [InCppInclude] +// CHECK:STDERR: ./templates.h:15:33: note: template is declared here [CppInteropParseNote] +// CHECK:STDERR: 15 | template ...U> struct Inner {}; +// CHECK:STDERR: | ~~~~~~~~~~~~~~~~~~~~~~ ^ +// CHECK:STDERR: +var too_many: Cpp.FixedSizePack(Cpp.A, Cpp.A).Inner(Cpp.B, Cpp.B, Cpp.B); + +// CHECK:STDOUT: --- valid.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %TwoTypes: type = class_type @TwoTypes [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %TwoTypes [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x.patt: %pattern_type = ref_binding_pattern x [concrete] +// CHECK:STDOUT: %x.var_patt: %pattern_type = var_pattern %x.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x.var: ref %TwoTypes = var %x.var_patt [concrete] +// CHECK:STDOUT: %x: ref %TwoTypes = ref_binding x, %x.var [concrete = %x.var] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- pack.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %TypePack.49edc4.1: type = class_type @TypePack.1 [concrete] +// CHECK:STDOUT: %pattern_type.9db866.1: type = pattern_type %TypePack.49edc4.1 [concrete] +// CHECK:STDOUT: %TypePack.49edc4.2: type = class_type @TypePack.2 [concrete] +// CHECK:STDOUT: %pattern_type.9db866.2: type = pattern_type %TypePack.49edc4.2 [concrete] +// CHECK:STDOUT: %TypePack.49edc4.3: type = class_type @TypePack.3 [concrete] +// CHECK:STDOUT: %pattern_type.9db866.3: type = pattern_type %TypePack.49edc4.3 [concrete] +// CHECK:STDOUT: %TypePack.49edc4.4: type = class_type @TypePack.4 [concrete] +// CHECK:STDOUT: %pattern_type.9db866.4: type = pattern_type %TypePack.49edc4.4 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a0.patt: %pattern_type.9db866.1 = ref_binding_pattern a0 [concrete] +// CHECK:STDOUT: %a0.var_patt: %pattern_type.9db866.1 = var_pattern %a0.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a0.var: ref %TypePack.49edc4.1 = var %a0.var_patt [concrete] +// CHECK:STDOUT: %a0: ref %TypePack.49edc4.1 = ref_binding a0, %a0.var [concrete = %a0.var] +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a1.patt: %pattern_type.9db866.2 = ref_binding_pattern a1 [concrete] +// CHECK:STDOUT: %a1.var_patt: %pattern_type.9db866.2 = var_pattern %a1.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a1.var: ref %TypePack.49edc4.2 = var %a1.var_patt [concrete] +// CHECK:STDOUT: %a1: ref %TypePack.49edc4.2 = ref_binding a1, %a1.var [concrete = %a1.var] +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a2.patt: %pattern_type.9db866.3 = ref_binding_pattern a2 [concrete] +// CHECK:STDOUT: %a2.var_patt: %pattern_type.9db866.3 = var_pattern %a2.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a2.var: ref %TypePack.49edc4.3 = var %a2.var_patt [concrete] +// CHECK:STDOUT: %a2: ref %TypePack.49edc4.3 = ref_binding a2, %a2.var [concrete = %a2.var] +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a3.patt: %pattern_type.9db866.4 = ref_binding_pattern a3 [concrete] +// CHECK:STDOUT: %a3.var_patt: %pattern_type.9db866.4 = var_pattern %a3.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a3.var: ref %TypePack.49edc4.4 = var %a3.var_patt [concrete] +// CHECK:STDOUT: %a3: ref %TypePack.49edc4.4 = ref_binding a3, %a3.var [concrete = %a3.var] +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/template/class_template.carbon b/toolchain/check/testdata/interop/cpp/template/class_template.carbon new file mode 100644 index 000000000000..214a8dcd0058 --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/template/class_template.carbon @@ -0,0 +1,110 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/template/class_template.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/template/class_template.carbon + +// --- class_template.h + +struct A {}; +struct B {}; + +template +struct X { + static T f(U); +}; + +// --- use_class_template.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "class_template.h"; + +//@dump-sem-ir-begin +var a: Cpp.A = Cpp.X(Cpp.A, Cpp.B).f({} as Cpp.B); +//@dump-sem-ir-end + +// CHECK:STDOUT: --- use_class_template.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %A: type = class_type @A [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %pattern_type.9de: type = pattern_type %A [concrete] +// CHECK:STDOUT: %X.type: type = cpp_type_template_type X [concrete] +// CHECK:STDOUT: %X.template: %X.type = struct_value () [concrete] +// CHECK:STDOUT: %B: type = class_type @B [concrete] +// CHECK:STDOUT: %X.f.cpp_overload_set.type: type = cpp_overload_set_type @X.f.cpp_overload_set [concrete] +// CHECK:STDOUT: %X.f.cpp_overload_set.value: %X.f.cpp_overload_set.type = cpp_overload_set_value @X.f.cpp_overload_set [concrete] +// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %B.val: %B = struct_value () [concrete] +// CHECK:STDOUT: %ptr.a04: type = ptr_type %B [concrete] +// CHECK:STDOUT: %ptr.270: type = ptr_type %A [concrete] +// CHECK:STDOUT: %f__carbon_thunk.type: type = fn_type @f__carbon_thunk [concrete] +// CHECK:STDOUT: %f__carbon_thunk: %f__carbon_thunk.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: .X = %X.template +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} +// CHECK:STDOUT: %X.template: %X.type = struct_value () [concrete = constants.%X.template] +// CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} +// CHECK:STDOUT: %X.f.cpp_overload_set.value: %X.f.cpp_overload_set.type = cpp_overload_set_value @X.f.cpp_overload_set [concrete = constants.%X.f.cpp_overload_set.value] +// CHECK:STDOUT: %f__carbon_thunk.decl: %f__carbon_thunk.type = fn_decl @f__carbon_thunk [concrete = constants.%f__carbon_thunk] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.9de = ref_binding_pattern a [concrete] +// CHECK:STDOUT: %a.var_patt: %pattern_type.9de = var_pattern %a.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a.var: ref %A = var %a.var_patt [concrete] +// CHECK:STDOUT: %.loc7: type = splice_block %A.ref [concrete = constants.%A] { +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %A.ref: type = name_ref A, imports.%A.decl [concrete = constants.%A] +// CHECK:STDOUT: } +// CHECK:STDOUT: %a: ref %A = ref_binding a, %a.var [concrete = %a.var] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref.loc7_16: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %X.ref: %X.type = name_ref X, imports.%X.template [concrete = constants.%X.template] +// CHECK:STDOUT: %Cpp.ref.loc7_22: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %A.ref: type = name_ref A, imports.%A.decl [concrete = constants.%A] +// CHECK:STDOUT: %Cpp.ref.loc7_29: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %B.ref.loc7_32: type = name_ref B, imports.%B.decl [concrete = constants.%B] +// CHECK:STDOUT: %f.ref: %X.f.cpp_overload_set.type = name_ref f, imports.%X.f.cpp_overload_set.value [concrete = constants.%X.f.cpp_overload_set.value] +// CHECK:STDOUT: %.loc7_39.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %Cpp.ref.loc7_44: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %B.ref.loc7_47: type = name_ref B, imports.%B.decl [concrete = constants.%B] +// CHECK:STDOUT: %.loc7_39.2: ref %B = temporary_storage +// CHECK:STDOUT: %.loc7_39.3: init %B = class_init (), %.loc7_39.2 [concrete = constants.%B.val] +// CHECK:STDOUT: %.loc7_39.4: ref %B = temporary %.loc7_39.2, %.loc7_39.3 +// CHECK:STDOUT: %.loc7_41.1: ref %B = converted %.loc7_39.1, %.loc7_39.4 +// CHECK:STDOUT: %.loc7_1: ref %A = splice_block file.%a.var [concrete = file.%a.var] {} +// CHECK:STDOUT: %.loc7_41.2: %B = acquire_value %.loc7_41.1 +// CHECK:STDOUT: %.loc7_41.3: ref %B = value_as_ref %.loc7_41.2 +// CHECK:STDOUT: %addr.loc7_49.1: %ptr.a04 = addr_of %.loc7_41.3 +// CHECK:STDOUT: %addr.loc7_49.2: %ptr.270 = addr_of %.loc7_1 +// CHECK:STDOUT: %f__carbon_thunk.call: init %empty_tuple.type = call imports.%f__carbon_thunk.decl(%addr.loc7_49.1, %addr.loc7_49.2) +// CHECK:STDOUT: %.loc7_49: init %A = in_place_init %f__carbon_thunk.call, %.loc7_1 +// CHECK:STDOUT: assign file.%a.var, %.loc7_49 +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/template/concept.carbon b/toolchain/check/testdata/interop/cpp/template/concept.carbon new file mode 100644 index 000000000000..6a2eb543085e --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/template/concept.carbon @@ -0,0 +1,126 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/bool.carbon +// EXTRA-ARGS: --clang-arg=--std=c++20 +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/template/concept.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/template/concept.carbon + +// --- concept.h + +struct Small {}; +struct Large { int n; }; + +template concept IsLarge = sizeof(T) > 1; + +// --- use_concept.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "concept.h"; + +class X(B:! bool) { + fn Make() -> Self { return {}; } +} + +//@dump-sem-ir-begin +var x1: X(Cpp.IsLarge(Cpp.Small)) = X(false).Make(); +var x2: X(Cpp.IsLarge(Cpp.Large)) = X(true).Make(); +//@dump-sem-ir-end + +// CHECK:STDOUT: --- use_concept.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %X.type: type = generic_class_type @X [concrete] +// CHECK:STDOUT: %X.generic: %X.type = struct_value () [concrete] +// CHECK:STDOUT: %IsLarge.type: type = cpp_type_template_type IsLarge [concrete] +// CHECK:STDOUT: %IsLarge.template: %IsLarge.type = struct_value () [concrete] +// CHECK:STDOUT: %Small: type = class_type @Small [concrete] +// CHECK:STDOUT: %false: bool = bool_literal false [concrete] +// CHECK:STDOUT: %X.363: type = class_type @X, @X(%false) [concrete] +// CHECK:STDOUT: %X.Make.type.58a: type = fn_type @X.Make, @X(%false) [concrete] +// CHECK:STDOUT: %X.Make.cb1: %X.Make.type.58a = struct_value () [concrete] +// CHECK:STDOUT: %pattern_type.a08: type = pattern_type %X.363 [concrete] +// CHECK:STDOUT: %X.Make.specific_fn.f4f: = specific_function %X.Make.cb1, @X.Make(%false) [concrete] +// CHECK:STDOUT: %Large: type = class_type @Large [concrete] +// CHECK:STDOUT: %true: bool = bool_literal true [concrete] +// CHECK:STDOUT: %X.57c: type = class_type @X, @X(%true) [concrete] +// CHECK:STDOUT: %X.Make.type.d3c: type = fn_type @X.Make, @X(%true) [concrete] +// CHECK:STDOUT: %X.Make.c40: %X.Make.type.d3c = struct_value () [concrete] +// CHECK:STDOUT: %pattern_type.ea2: type = pattern_type %X.57c [concrete] +// CHECK:STDOUT: %X.Make.specific_fn.70c: = specific_function %X.Make.c40, @X.Make(%true) [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .IsLarge = %IsLarge.template +// CHECK:STDOUT: .Small = %Small.decl +// CHECK:STDOUT: .Large = %Large.decl +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %IsLarge.template: %IsLarge.type = struct_value () [concrete = constants.%IsLarge.template] +// CHECK:STDOUT: %Small.decl: type = class_decl @Small [concrete = constants.%Small] {} {} +// CHECK:STDOUT: %Large.decl: type = class_decl @Large [concrete = constants.%Large] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x1.patt: %pattern_type.a08 = ref_binding_pattern x1 [concrete] +// CHECK:STDOUT: %x1.var_patt: %pattern_type.a08 = var_pattern %x1.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x1.var: ref %X.363 = var %x1.var_patt [concrete] +// CHECK:STDOUT: %.loc11: type = splice_block %X.loc11 [concrete = constants.%X.363] { +// CHECK:STDOUT: %X.ref.loc11: %X.type = name_ref X, %X.decl [concrete = constants.%X.generic] +// CHECK:STDOUT: %Cpp.ref.loc11_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %IsLarge.ref.loc11: %IsLarge.type = name_ref IsLarge, imports.%IsLarge.template [concrete = constants.%IsLarge.template] +// CHECK:STDOUT: %Cpp.ref.loc11_23: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Small.ref: type = name_ref Small, imports.%Small.decl [concrete = constants.%Small] +// CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] +// CHECK:STDOUT: %X.loc11: type = class_type @X, @X(constants.%false) [concrete = constants.%X.363] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x1: ref %X.363 = ref_binding x1, %x1.var [concrete = %x1.var] +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x2.patt: %pattern_type.ea2 = ref_binding_pattern x2 [concrete] +// CHECK:STDOUT: %x2.var_patt: %pattern_type.ea2 = var_pattern %x2.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x2.var: ref %X.57c = var %x2.var_patt [concrete] +// CHECK:STDOUT: %.loc12: type = splice_block %X.loc12 [concrete = constants.%X.57c] { +// CHECK:STDOUT: %X.ref.loc12: %X.type = name_ref X, %X.decl [concrete = constants.%X.generic] +// CHECK:STDOUT: %Cpp.ref.loc12_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %IsLarge.ref.loc12: %IsLarge.type = name_ref IsLarge, imports.%IsLarge.template [concrete = constants.%IsLarge.template] +// CHECK:STDOUT: %Cpp.ref.loc12_23: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Large.ref: type = name_ref Large, imports.%Large.decl [concrete = constants.%Large] +// CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] +// CHECK:STDOUT: %X.loc12: type = class_type @X, @X(constants.%true) [concrete = constants.%X.57c] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x2: ref %X.57c = ref_binding x2, %x2.var [concrete = %x2.var] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %X.ref.loc11: %X.type = name_ref X, file.%X.decl [concrete = constants.%X.generic] +// CHECK:STDOUT: %false: bool = bool_literal false [concrete = constants.%false] +// CHECK:STDOUT: %X.loc11: type = class_type @X, @X(constants.%false) [concrete = constants.%X.363] +// CHECK:STDOUT: %.loc11_45: %X.Make.type.58a = specific_constant @X.%X.Make.decl, @X(constants.%false) [concrete = constants.%X.Make.cb1] +// CHECK:STDOUT: %Make.ref.loc11: %X.Make.type.58a = name_ref Make, %.loc11_45 [concrete = constants.%X.Make.cb1] +// CHECK:STDOUT: %X.Make.specific_fn.loc11: = specific_function %Make.ref.loc11, @X.Make(constants.%false) [concrete = constants.%X.Make.specific_fn.f4f] +// CHECK:STDOUT: %.loc11_1: ref %X.363 = splice_block file.%x1.var [concrete = file.%x1.var] {} +// CHECK:STDOUT: %X.Make.call.loc11: init %X.363 = call %X.Make.specific_fn.loc11() to %.loc11_1 +// CHECK:STDOUT: assign file.%x1.var, %X.Make.call.loc11 +// CHECK:STDOUT: %X.ref.loc12: %X.type = name_ref X, file.%X.decl [concrete = constants.%X.generic] +// CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] +// CHECK:STDOUT: %X.loc12: type = class_type @X, @X(constants.%true) [concrete = constants.%X.57c] +// CHECK:STDOUT: %.loc12_44: %X.Make.type.d3c = specific_constant @X.%X.Make.decl, @X(constants.%true) [concrete = constants.%X.Make.c40] +// CHECK:STDOUT: %Make.ref.loc12: %X.Make.type.d3c = name_ref Make, %.loc12_44 [concrete = constants.%X.Make.c40] +// CHECK:STDOUT: %X.Make.specific_fn.loc12: = specific_function %Make.ref.loc12, @X.Make(constants.%true) [concrete = constants.%X.Make.specific_fn.70c] +// CHECK:STDOUT: %.loc12_1: ref %X.57c = splice_block file.%x2.var [concrete = file.%x2.var] {} +// CHECK:STDOUT: %X.Make.call.loc12: init %X.57c = call %X.Make.specific_fn.loc12() to %.loc12_1 +// CHECK:STDOUT: assign file.%x2.var, %X.Make.call.loc12 +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/template/generic_call.carbon b/toolchain/check/testdata/interop/cpp/template/generic_call.carbon new file mode 100644 index 000000000000..c6f3d186d581 --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/template/generic_call.carbon @@ -0,0 +1,38 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon +// EXTRA-ARGS: --clang-arg=--std=c++20 +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/template/generic_call.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/template/generic_call.carbon + +// --- header.h + +struct X {}; + +template struct S {}; + +// --- fail_todo_generic_call.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "header.h"; + +fn F[T:! type](x: T) { + // TODO: This should be treated as being template-dependent on T, + // and we should perform the call during monomorphization. + // CHECK:STDERR: fail_todo_generic_call.carbon:[[@LINE+4]]:16: error: semantics TODO: `unsupported type used as template argument` [SemanticsTodo] + // CHECK:STDERR: var v: Cpp.S(T); + // CHECK:STDERR: ^ + // CHECK:STDERR: + var v: Cpp.S(T); +} + +fn G() { + F({} as Cpp.X); +} diff --git a/toolchain/check/testdata/interop/cpp/template/non_type_param.carbon b/toolchain/check/testdata/interop/cpp/template/non_type_param.carbon new file mode 100644 index 000000000000..174793edc68f --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/template/non_type_param.carbon @@ -0,0 +1,121 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/template/non_type_param.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/template/non_type_param.carbon + +// --- templates.h + +struct A {}; + +template struct TwoNonType {}; +template struct DependentNonType {}; + +// --- fail_todo_valid.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +//@dump-sem-ir-begin +// CHECK:STDERR: fail_todo_valid.carbon:[[@LINE+4]]:23: error: semantics TODO: `argument for non-type template parameter` [SemanticsTodo] +// CHECK:STDERR: var x: Cpp.TwoNonType(1, 2); +// CHECK:STDERR: ^ +// CHECK:STDERR: +var x: Cpp.TwoNonType(1, 2); +//@dump-sem-ir-end + +// --- fail_todo_dependent.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +var n: i32; + +//@dump-sem-ir-begin +// CHECK:STDERR: fail_todo_dependent.carbon:[[@LINE+4]]:34: error: semantics TODO: `argument for non-type template parameter` [SemanticsTodo] +// CHECK:STDERR: var x: Cpp.DependentNonType(i32, 1); +// CHECK:STDERR: ^ +// CHECK:STDERR: +var x: Cpp.DependentNonType(i32, 1); + +// CHECK:STDERR: fail_todo_dependent.carbon:[[@LINE+4]]:35: error: semantics TODO: `argument for non-type template parameter` [SemanticsTodo] +// CHECK:STDERR: var y: Cpp.DependentNonType(i32*, &n); +// CHECK:STDERR: ^~ +// CHECK:STDERR: +var y: Cpp.DependentNonType(i32*, &n); +//@dump-sem-ir-end + +// --- fail_type_argument.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +// CHECK:STDERR: fail_type_argument.carbon:[[@LINE+4]]:23: error: semantics TODO: `argument for non-type template parameter` [SemanticsTodo] +// CHECK:STDERR: var x: Cpp.TwoNonType(1, Cpp.A); +// CHECK:STDERR: ^ +// CHECK:STDERR: +var x: Cpp.TwoNonType(1, Cpp.A); + +// --- fail_no_conversion.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +var n: i32; + +// CHECK:STDERR: fail_no_conversion.carbon:[[@LINE+4]]:23: error: semantics TODO: `argument for non-type template parameter` [SemanticsTodo] +// CHECK:STDERR: var x: Cpp.TwoNonType(1, &n); +// CHECK:STDERR: ^ +// CHECK:STDERR: +var x: Cpp.TwoNonType(1, &n); + +// CHECK:STDOUT: --- fail_todo_valid.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x.patt: = ref_binding_pattern x [concrete] +// CHECK:STDOUT: %x.var_patt: = var_pattern %x.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x.var: ref = var %x.var_patt [concrete = ] +// CHECK:STDOUT: %x: ref = ref_binding x, [concrete = ] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_dependent.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x.patt: = ref_binding_pattern x [concrete] +// CHECK:STDOUT: %x.var_patt: = var_pattern %x.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x.var: ref = var %x.var_patt [concrete = ] +// CHECK:STDOUT: %x: ref = ref_binding x, [concrete = ] +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %y.patt: = ref_binding_pattern y [concrete] +// CHECK:STDOUT: %y.var_patt: = var_pattern %y.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %y.var: ref = var %y.var_patt [concrete = ] +// CHECK:STDOUT: %y: ref = ref_binding y, [concrete = ] +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/template/template_template_param.carbon b/toolchain/check/testdata/interop/cpp/template/template_template_param.carbon new file mode 100644 index 000000000000..6fd106edd1d6 --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/template/template_template_param.carbon @@ -0,0 +1,63 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/template/template_template_param.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/template/template_template_param.carbon + +// --- templates.h + +template struct A {}; +template struct B {}; + +template typename, template typename> +struct TwoTemplates {}; + +// --- fail_todo_valid.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +//@dump-sem-ir-begin +// CHECK:STDERR: fail_todo_valid.carbon:[[@LINE+4]]:25: error: semantics TODO: `argument for template template parameter` [SemanticsTodo] +// CHECK:STDERR: var x: Cpp.TwoTemplates(Cpp.A, Cpp.B); +// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: +var x: Cpp.TwoTemplates(Cpp.A, Cpp.B); +//@dump-sem-ir-end + +// --- fail_non_type_argument.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +// CHECK:STDERR: fail_non_type_argument.carbon:[[@LINE+4]]:25: error: semantics TODO: `argument for template template parameter` [SemanticsTodo] +// CHECK:STDERR: var x: Cpp.TwoTemplates(Cpp.A, true); +// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: +var x: Cpp.TwoTemplates(Cpp.A, true); + +// CHECK:STDOUT: --- fail_todo_valid.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x.patt: = ref_binding_pattern x [concrete] +// CHECK:STDOUT: %x.var_patt: = var_pattern %x.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x.var: ref = var %x.var_patt [concrete = ] +// CHECK:STDOUT: %x: ref = ref_binding x, [concrete = ] +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/template/type_param.carbon b/toolchain/check/testdata/interop/cpp/template/type_param.carbon new file mode 100644 index 000000000000..f22f703eb88a --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/template/type_param.carbon @@ -0,0 +1,75 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/template/type_param.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/template/type_param.carbon + +// --- templates.h + +struct A {}; +struct B {}; + +template struct TwoTypes {}; + +// --- valid.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +//@dump-sem-ir-begin +var x: Cpp.TwoTypes(Cpp.A, Cpp.B); +//@dump-sem-ir-end + +// --- fail_non_type_argument.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +// CHECK:STDERR: fail_non_type_argument.carbon:[[@LINE+7]]:28: error: cannot implicitly convert non-type value of type `bool` to `type` [ConversionFailureNonTypeToFacet] +// CHECK:STDERR: var x: Cpp.TwoTypes(Cpp.A, true); +// CHECK:STDERR: ^~~~ +// CHECK:STDERR: fail_non_type_argument.carbon:[[@LINE+4]]:28: note: type `bool` does not implement interface `Core.ImplicitAs(type)` [MissingImplInMemberAccessNote] +// CHECK:STDERR: var x: Cpp.TwoTypes(Cpp.A, true); +// CHECK:STDERR: ^~~~ +// CHECK:STDERR: +var x: Cpp.TwoTypes(Cpp.A, true); + +// --- fail_unsupported_type.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +// CHECK:STDERR: fail_unsupported_type.carbon:[[@LINE+4]]:28: error: semantics TODO: `unsupported type used as template argument` [SemanticsTodo] +// CHECK:STDERR: var x: Cpp.TwoTypes(Cpp.A, {}); +// CHECK:STDERR: ^~ +// CHECK:STDERR: +var x: Cpp.TwoTypes(Cpp.A, {}); + +// CHECK:STDOUT: --- valid.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %TwoTypes: type = class_type @TwoTypes [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %TwoTypes [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %x.patt: %pattern_type = ref_binding_pattern x [concrete] +// CHECK:STDOUT: %x.var_patt: %pattern_type = var_pattern %x.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x.var: ref %TwoTypes = var %x.var_patt [concrete] +// CHECK:STDOUT: %x: ref %TwoTypes = ref_binding x, %x.var [concrete = %x.var] +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/template/var_template.carbon b/toolchain/check/testdata/interop/cpp/template/var_template.carbon new file mode 100644 index 000000000000..f0cdf45ed99b --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/template/var_template.carbon @@ -0,0 +1,139 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/template/var_template.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/template/var_template.carbon + +// --- var_template.h + +struct A {}; +struct B {}; + +template T var = {}; + +struct Wrap { + template static T var = {}; +}; + +// --- use_var_template.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "var_template.h"; + +//@dump-sem-ir-begin +let pa: Cpp.A* = &Cpp.r#var(Cpp.A); +let pb: Cpp.B* = &Cpp.r#var(Cpp.B); +let pwa: Cpp.A* = &Cpp.Wrap.r#var(Cpp.A); +let pwb: Cpp.B* = &Cpp.Wrap.r#var(Cpp.B); +//@dump-sem-ir-end + +// CHECK:STDOUT: --- use_var_template.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %A: type = class_type @A [concrete] +// CHECK:STDOUT: %ptr.270: type = ptr_type %A [concrete] +// CHECK:STDOUT: %pattern_type.bcf: type = pattern_type %ptr.270 [concrete] +// CHECK:STDOUT: %var.type.230: type = cpp_type_template_type r#var [concrete] +// CHECK:STDOUT: %var.template.326: %var.type.230 = struct_value () [concrete] +// CHECK:STDOUT: %addr.0b0: %ptr.270 = addr_of imports.%var.var.2f7 [concrete] +// CHECK:STDOUT: %B: type = class_type @B [concrete] +// CHECK:STDOUT: %ptr.a04: type = ptr_type %B [concrete] +// CHECK:STDOUT: %pattern_type.837: type = pattern_type %ptr.a04 [concrete] +// CHECK:STDOUT: %addr.324: %ptr.a04 = addr_of imports.%var.var.a15 [concrete] +// CHECK:STDOUT: %Wrap: type = class_type @Wrap [concrete] +// CHECK:STDOUT: %var.type.17b: type = cpp_type_template_type r#var [concrete] +// CHECK:STDOUT: %var.template.df3: %var.type.17b = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: .r#var = %var.template.326 +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: .Wrap = %Wrap.decl +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} +// CHECK:STDOUT: %var.template.326: %var.type.230 = struct_value () [concrete = constants.%var.template.326] +// CHECK:STDOUT: %var.var.2f7: ref %A = var %var.var_patt.093 [concrete] +// CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} +// CHECK:STDOUT: %var.var.a15: ref %B = var %var.var_patt.56c [concrete] +// CHECK:STDOUT: %Wrap.decl: type = class_decl @Wrap [concrete = constants.%Wrap] {} {} +// CHECK:STDOUT: %var.template.df3: %var.type.17b = struct_value () [concrete = constants.%var.template.df3] +// CHECK:STDOUT: %var.var.2e6: ref %A = var %var.var_patt.0cb +// CHECK:STDOUT: %var.var.bab: ref %B = var %var.var_patt.efb +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %pa.patt: %pattern_type.bcf = value_binding_pattern pa [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc7: type = splice_block %ptr.loc7 [concrete = constants.%ptr.270] { +// CHECK:STDOUT: %Cpp.ref.loc7: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %A.ref.loc7: type = name_ref A, imports.%A.decl [concrete = constants.%A] +// CHECK:STDOUT: %ptr.loc7: type = ptr_type %A.ref.loc7 [concrete = constants.%ptr.270] +// CHECK:STDOUT: } +// CHECK:STDOUT: %pa: %ptr.270 = value_binding pa, @__global_init.%addr.loc7 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %pb.patt: %pattern_type.837 = value_binding_pattern pb [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc8: type = splice_block %ptr.loc8 [concrete = constants.%ptr.a04] { +// CHECK:STDOUT: %Cpp.ref.loc8: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %B.ref.loc8: type = name_ref B, imports.%B.decl [concrete = constants.%B] +// CHECK:STDOUT: %ptr.loc8: type = ptr_type %B.ref.loc8 [concrete = constants.%ptr.a04] +// CHECK:STDOUT: } +// CHECK:STDOUT: %pb: %ptr.a04 = value_binding pb, @__global_init.%addr.loc8 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %pwa.patt: %pattern_type.bcf = value_binding_pattern pwa [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc9: type = splice_block %ptr.loc9 [concrete = constants.%ptr.270] { +// CHECK:STDOUT: %Cpp.ref.loc9: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %A.ref.loc9: type = name_ref A, imports.%A.decl [concrete = constants.%A] +// CHECK:STDOUT: %ptr.loc9: type = ptr_type %A.ref.loc9 [concrete = constants.%ptr.270] +// CHECK:STDOUT: } +// CHECK:STDOUT: %pwa: %ptr.270 = value_binding pwa, @__global_init.%addr.loc9 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %pwb.patt: %pattern_type.837 = value_binding_pattern pwb [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc10: type = splice_block %ptr.loc10 [concrete = constants.%ptr.a04] { +// CHECK:STDOUT: %Cpp.ref.loc10: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %B.ref.loc10: type = name_ref B, imports.%B.decl [concrete = constants.%B] +// CHECK:STDOUT: %ptr.loc10: type = ptr_type %B.ref.loc10 [concrete = constants.%ptr.a04] +// CHECK:STDOUT: } +// CHECK:STDOUT: %pwb: %ptr.a04 = value_binding pwb, @__global_init.%addr.loc10 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref.loc7_19: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %var.ref.loc7: %var.type.230 = name_ref r#var, imports.%var.template.326 [concrete = constants.%var.template.326] +// CHECK:STDOUT: %Cpp.ref.loc7_29: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %A.ref.loc7: type = name_ref A, imports.%A.decl [concrete = constants.%A] +// CHECK:STDOUT: %addr.loc7: %ptr.270 = addr_of imports.%var.var.2f7 [concrete = constants.%addr.0b0] +// CHECK:STDOUT: %Cpp.ref.loc8_19: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %var.ref.loc8: %var.type.230 = name_ref r#var, imports.%var.template.326 [concrete = constants.%var.template.326] +// CHECK:STDOUT: %Cpp.ref.loc8_29: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %B.ref.loc8: type = name_ref B, imports.%B.decl [concrete = constants.%B] +// CHECK:STDOUT: %addr.loc8: %ptr.a04 = addr_of imports.%var.var.a15 [concrete = constants.%addr.324] +// CHECK:STDOUT: %Cpp.ref.loc9_20: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Wrap.ref.loc9: type = name_ref Wrap, imports.%Wrap.decl [concrete = constants.%Wrap] +// CHECK:STDOUT: %var.ref.loc9: %var.type.17b = name_ref r#var, imports.%var.template.df3 [concrete = constants.%var.template.df3] +// CHECK:STDOUT: %Cpp.ref.loc9_35: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %A.ref.loc9: type = name_ref A, imports.%A.decl [concrete = constants.%A] +// CHECK:STDOUT: %addr.loc9: %ptr.270 = addr_of imports.%var.var.2e6 +// CHECK:STDOUT: %Cpp.ref.loc10_20: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Wrap.ref.loc10: type = name_ref Wrap, imports.%Wrap.decl [concrete = constants.%Wrap] +// CHECK:STDOUT: %var.ref.loc10: %var.type.17b = name_ref r#var, imports.%var.template.df3 [concrete = constants.%var.template.df3] +// CHECK:STDOUT: %Cpp.ref.loc10_35: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %B.ref.loc10: type = name_ref B, imports.%B.decl [concrete = constants.%B] +// CHECK:STDOUT: %addr.loc10: %ptr.a04 = addr_of imports.%var.var.bab +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/unsupported_decl_type.carbon b/toolchain/check/testdata/interop/cpp/unsupported_decl_type.carbon index a635f6f9685b..1639206406f0 100644 --- a/toolchain/check/testdata/interop/cpp/unsupported_decl_type.carbon +++ b/toolchain/check/testdata/interop/cpp/unsupported_decl_type.carbon @@ -2,7 +2,7 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/destroy.carbon +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: @@ -10,47 +10,49 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/unsupported_decl_type.carbon -// --- template.h +// --- structured_binding.h -template class C {}; +struct X { int a, b; }; +auto [a, b] = X{}; -// --- fail_todo_use_template.carbon +// --- fail_todo_use_structured_binding.carbon library "[[@TEST_NAME]]"; -// CHECK:STDERR: fail_todo_use_template.carbon:[[@LINE+4]]:10: in file included here [InCppInclude] -// CHECK:STDERR: ./template.h:2:28: error: semantics TODO: `Unsupported: Declaration type ClassTemplate` [SemanticsTodo] -// CHECK:STDERR: template class C {}; -// CHECK:STDERR: ^ -import Cpp library "template.h"; +// CHECK:STDERR: fail_todo_use_structured_binding.carbon:[[@LINE+4]]:10: in file included here [InCppInclude] +// CHECK:STDERR: ./structured_binding.h:3:7: error: semantics TODO: `Unsupported: Declaration type Binding` [SemanticsTodo] +// CHECK:STDERR: auto [a, b] = X{}; +// CHECK:STDERR: ^ +import Cpp library "structured_binding.h"; -fn F() { +fn F() -> i32 { //@dump-sem-ir-begin - // CHECK:STDERR: fail_todo_use_template.carbon:[[@LINE+4]]:10: note: in `Cpp` name lookup for `C` [InCppNameLookup] - // CHECK:STDERR: var c: Cpp.C({}); + // CHECK:STDERR: fail_todo_use_structured_binding.carbon:[[@LINE+4]]:10: note: in `Cpp` name lookup for `a` [InCppNameLookup] + // CHECK:STDERR: return Cpp.a; // CHECK:STDERR: ^~~~~ // CHECK:STDERR: - var c: Cpp.C({}); + return Cpp.a; //@dump-sem-ir-end } -// CHECK:STDOUT: --- fail_todo_use_template.carbon +// CHECK:STDOUT: --- fail_todo_use_structured_binding.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @F() { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: name_binding_decl { -// CHECK:STDOUT: %c.patt: = ref_binding_pattern c [concrete] -// CHECK:STDOUT: %c.var_patt: = var_pattern %c.patt [concrete] +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .a = +// CHECK:STDOUT: import Cpp//... // CHECK:STDOUT: } -// CHECK:STDOUT: %c.var: ref = var %c.var_patt [concrete = ] -// CHECK:STDOUT: -// CHECK:STDOUT: %c: ref = ref_binding c, [concrete = ] -// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> %i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %a.ref: = name_ref a, [concrete = ] +// CHECK:STDOUT: return to %return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/type.cpp b/toolchain/check/type.cpp index 466e2af0a895..b9267a0ec60c 100644 --- a/toolchain/check/type.cpp +++ b/toolchain/check/type.cpp @@ -178,6 +178,12 @@ auto GetCppOverloadSetType(Context& context, context, overload_set_id, specific_id); } +auto GetCppTemplateNameType(Context& context, SemIR::EntityNameId name_id, + SemIR::ClangDeclId decl_id) -> SemIR::TypeId { + return GetCompleteTypeImpl(context, name_id, + decl_id); +} + auto GetFunctionType(Context& context, SemIR::FunctionId fn_id, SemIR::SpecificId specific_id) -> SemIR::TypeId { return GetCompleteTypeImpl(context, fn_id, specific_id); diff --git a/toolchain/check/type.h b/toolchain/check/type.h index 50b9d262c6a3..d59702f9c2f6 100644 --- a/toolchain/check/type.h +++ b/toolchain/check/type.h @@ -52,6 +52,10 @@ auto GetCppOverloadSetType(Context& context, SemIR::CppOverloadSetId overload_set_id, SemIR::SpecificId specific_id) -> SemIR::TypeId; +// Gets a C++ template name type. The returned type will be complete. +auto GetCppTemplateNameType(Context& context, SemIR::EntityNameId name_id, + SemIR::ClangDeclId decl_id) -> SemIR::TypeId; + // Gets a function type. The returned type will be complete. auto GetFunctionType(Context& context, SemIR::FunctionId fn_id, SemIR::SpecificId specific_id) -> SemIR::TypeId; diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 18e80c0be934..5c86bf4ddf3a 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -223,10 +223,10 @@ class TypeCompleter { template requires(InstT::Kind.template IsAnyOf< SemIR::AssociatedEntityType, SemIR::CppOverloadSetType, - SemIR::FunctionType, SemIR::FunctionTypeWithSelfType, - SemIR::GenericClassType, SemIR::GenericInterfaceType, - SemIR::GenericNamedConstraintType, SemIR::InstType, - SemIR::UnboundElementType, SemIR::WhereExpr>()) + SemIR::CppTemplateNameType, SemIR::FunctionType, + SemIR::FunctionTypeWithSelfType, SemIR::GenericClassType, + SemIR::GenericInterfaceType, SemIR::GenericNamedConstraintType, + SemIR::InstType, SemIR::UnboundElementType, SemIR::WhereExpr>()) auto BuildInfoForInst(SemIR::TypeId /*type_id*/, InstT /*inst*/) const -> SemIR::CompleteTypeInfo { // These types have no runtime operations, so we use an empty value diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index fcdfbaa96c3d..c47187aa90ba 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -1010,7 +1010,8 @@ static auto BuildTypeForInst(FileContext& context, template requires(InstT::Kind.template IsAnyOf< SemIR::AssociatedEntityType, SemIR::AutoType, SemIR::BoundMethodType, - SemIR::CharLiteralType, SemIR::CppOverloadSetType, SemIR::FacetType, + SemIR::CharLiteralType, SemIR::CppOverloadSetType, + SemIR::CppTemplateNameType, SemIR::FacetType, SemIR::FloatLiteralType, SemIR::FunctionType, SemIR::FunctionTypeWithSelfType, SemIR::GenericClassType, SemIR::GenericInterfaceType, SemIR::GenericNamedConstraintType, diff --git a/toolchain/sem_ir/clang_decl.h b/toolchain/sem_ir/clang_decl.h index 04fc052a3b19..d7d0e75dee24 100644 --- a/toolchain/sem_ir/clang_decl.h +++ b/toolchain/sem_ir/clang_decl.h @@ -94,20 +94,6 @@ struct ClangDecl : public Printable { auto GetAsKey() const -> ClangDeclKey { return key; } }; -// The ID of a `ClangDecl`. -// -// These IDs are importantly distinct from the `inst_id` associated with each -// declaration. These form a dense range of IDs that is used to reference the -// AST node pointers without storing those pointers directly into SemIR and -// needing space to hold a full pointer. We can't avoid having these IDs without -// embedding pointers directly into the storage of SemIR as part of an -// instruction. -struct ClangDeclId : public IdBase { - static constexpr llvm::StringLiteral Label = "clang_decl_id"; - - using IdBase::IdBase; -}; - // Use the AST node pointer directly when doing `Lookup` to find an ID. using ClangDeclStore = CanonicalValueStore; diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index c76eef2b9c22..a5043317fcfc 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -1086,6 +1086,13 @@ auto Formatter::FormatInstRhs(Inst inst) -> void { return; } + case CARBON_KIND(CppTemplateNameType type): { + // Omit the Clang declaration. We don't have a good way to format it, and + // the entity name should suffice to identify the template. + FormatArgs(type.name_id); + return; + } + case CARBON_KIND(CustomLayoutType type): { out_ << " {"; auto layout = sem_ir_->custom_layouts().Get(type.layout_id); diff --git a/toolchain/sem_ir/id_kind.h b/toolchain/sem_ir/id_kind.h index 7a1e50c02f78..6b6711beca46 100644 --- a/toolchain/sem_ir/id_kind.h +++ b/toolchain/sem_ir/id_kind.h @@ -33,6 +33,7 @@ using IdKind = TypeEnum< BoolValue, CallParamIndex, CharId, + ClangDeclId, ClassId, CompileTimeBindIndex, ConstantId, diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index 32c3d4cb8645..1081bf706074 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -472,6 +472,20 @@ struct ImportIRId : public IdBase { inline constexpr ImportIRId ImportIRId::ApiForImpl = ImportIRId(0); inline constexpr ImportIRId ImportIRId::Cpp = ImportIRId(ApiForImpl.index + 1); +// The ID of a `ClangDecl`. +// +// These IDs are importantly distinct from the `inst_id` associated with each +// declaration. These form a dense range of IDs that is used to reference the +// AST node pointers without storing those pointers directly into SemIR and +// needing space to hold a full pointer. We can't avoid having these IDs without +// embedding pointers directly into the storage of SemIR as part of an +// instruction. +struct ClangDeclId : public IdBase { + static constexpr llvm::StringLiteral Label = "clang_decl_id"; + + using IdBase::IdBase; +}; + // A boolean value. struct BoolValue : public IdBase { // Not used by `Print`, but for `IdKind`. diff --git a/toolchain/sem_ir/inst_fingerprinter.cpp b/toolchain/sem_ir/inst_fingerprinter.cpp index e359bb1c1a79..277f70e7493a 100644 --- a/toolchain/sem_ir/inst_fingerprinter.cpp +++ b/toolchain/sem_ir/inst_fingerprinter.cpp @@ -229,6 +229,13 @@ struct Worklist { } } + auto Add(ClangDeclId /*decl_id*/) -> void { + // TODO: For `CppTemplateNameType` we don't need to fingerprint the + // `decl_id`, because fingerprinting the `NameId` is sufficient to identify + // the template, but this won't necessarily be true for other + // `ClangDeclId`s. + } + auto Add(ClassId class_id) -> void { AddEntity(sem_ir->classes().Get(class_id)); } diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index e6c93dcd3e86..a6170526e407 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -55,6 +55,7 @@ CARBON_SEM_IR_INST_KIND(ConvertToValueAction) CARBON_SEM_IR_INST_KIND(Converted) CARBON_SEM_IR_INST_KIND(CppOverloadSetType) CARBON_SEM_IR_INST_KIND(CppOverloadSetValue) +CARBON_SEM_IR_INST_KIND(CppTemplateNameType) CARBON_SEM_IR_INST_KIND(CppWitness) CARBON_SEM_IR_INST_KIND(CustomLayoutType) CARBON_SEM_IR_INST_KIND(Deref) diff --git a/toolchain/sem_ir/inst_namer.cpp b/toolchain/sem_ir/inst_namer.cpp index 7024d22d1734..6544bb06ba30 100644 --- a/toolchain/sem_ir/inst_namer.cpp +++ b/toolchain/sem_ir/inst_namer.cpp @@ -907,6 +907,10 @@ auto InstNamer::NamingContext::NameInst() -> void { AddInstName("const"); return; } + case CARBON_KIND(CppTemplateNameType inst): { + AddInstNameId(sem_ir().entity_names().Get(inst.name_id).name_id, ".type"); + return; + } case CppWitness::Kind: { AddInstName("cpp_witness"); return; @@ -1264,6 +1268,12 @@ auto InstNamer::NamingContext::NameInst() -> void { .Get(generic_interface_ty->interface_id) .name_id, ".generic"); + } else if (auto template_name_ty = + sem_ir().types().TryGetAs( + inst.type_id)) { + AddInstNameId( + sem_ir().entity_names().Get(template_name_ty->name_id).name_id, + ".template"); } else { if (sem_ir().inst_blocks().Get(inst.elements_id).empty()) { AddInstName("empty_struct"); diff --git a/toolchain/sem_ir/stringify.cpp b/toolchain/sem_ir/stringify.cpp index 449f001c346b..a3e149599674 100644 --- a/toolchain/sem_ir/stringify.cpp +++ b/toolchain/sem_ir/stringify.cpp @@ -327,6 +327,11 @@ class Stringifier { step_stack_->PushInstId(inst.inner_id); } + auto StringifyInst(InstId /*inst_id*/, CppTemplateNameType inst) -> void { + *out_ << "Push(inst.name_id, ">"); + } + auto StringifyInst(InstId /*inst_id*/, CustomLayoutType inst) -> void { auto layout = sem_ir_->custom_layouts().Get(inst.layout_id); *out_ << " std::optional { case BoolType::Kind: case CharLiteralType::Kind: case CppOverloadSetType::Kind: + case CppTemplateNameType::Kind: case FacetType::Kind: case FloatLiteralType::Kind: case FloatType::Kind: diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 6b5d56845cda..7fc920e42cf0 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -754,6 +754,22 @@ struct CppOverloadSetValue { CppOverloadSetId overload_set_id; }; +// The type of the name of a C++ template. The corresponding value is an empty +// `StructValue`. This does not handle function templates, which are instead +// represented as a `CppOverloadSetValue` of type `CppOverloadSetType`. +struct CppTemplateNameType { + // This is only ever created as a constant, so doesn't have a location. + static constexpr auto Kind = + InstKind::CppTemplateNameType.Define( + {.ir_name = "cpp_type_template_type", + .is_type = InstIsType::Always, + .constant_kind = InstConstantKind::Always}); + + TypeId type_id; + EntityNameId name_id; + ClangDeclId decl_id; +}; + // A witness synthesized for a C++ construct such as a constructor, conversion // function, or overloaded operator. struct CppWitness {