Reverse interop support for type aliases. (#7043)

Allow any type that has a mapping from Carbon to C++ to be exposed to
C++ via name lookup. This also exposes the logic to export Carbon
classes to C++ to apply during type mapping, which gives very slight
support for passing Carbon types to C++ functions from Carbon, but not
really enough to sensibly test yet.

Depends on #7042.
This commit is contained in:
Richard Smith
2026-04-14 00:18:33 +00:00
committed by GitHub
parent 5b1de7633c
commit 43867a678b
9 changed files with 135 additions and 46 deletions
+11
View File
@@ -44,4 +44,15 @@ auto MapCppAccess(clang::DeclAccessPair access_pair) -> SemIR::AccessKind {
}
}
auto MapToCppAccess(SemIR::AccessKind access) -> clang::AccessSpecifier {
switch (access) {
case SemIR::AccessKind::Public:
return clang::AS_public;
case SemIR::AccessKind::Protected:
return clang::AS_protected;
case SemIR::AccessKind::Private:
return clang::AS_private;
}
}
} // namespace Carbon::Check
+4
View File
@@ -13,6 +13,10 @@ namespace Carbon::Check {
// access) pair.
auto MapCppAccess(clang::DeclAccessPair access_pair) -> SemIR::AccessKind;
// Maps a Carbon access kind to a C++ access specifier, suitable for use when
// declaring a C++ class member with the same access. Never returns AS_none.
auto MapToCppAccess(SemIR::AccessKind access) -> clang::AccessSpecifier;
} // namespace Carbon::Check
#endif // CARBON_TOOLCHAIN_CHECK_CPP_ACCESS_H_
+2 -6
View File
@@ -131,12 +131,8 @@ auto ExportClassToCpp(Context& context, SemIR::LocId loc_id,
}
auto* identifier_info = GetClangIdentifierInfo(context, class_info.name_id);
if (!identifier_info) {
// TODO: Handle keyword package names like `Cpp` and `Core`. These can
// be named from C++ via an alias.
context.TODO(loc_id, "interop with non-identifier package name");
return nullptr;
}
CARBON_CHECK(identifier_info, "non-identifier class name {0}",
class_info.name_id);
auto* decl_context =
ExportNameScopeToCpp(context, loc_id, class_info.parent_scope_id);
+51 -17
View File
@@ -28,8 +28,11 @@
#include "llvm/Support/raw_ostream.h"
#include "toolchain/base/kind_switch.h"
#include "toolchain/check/context.h"
#include "toolchain/check/cpp/access.h"
#include "toolchain/check/cpp/export.h"
#include "toolchain/check/cpp/import.h"
#include "toolchain/check/cpp/location.h"
#include "toolchain/check/cpp/type_mapping.h"
#include "toolchain/check/import_ref.h"
#include "toolchain/check/name_lookup.h"
#include "toolchain/check/type_completion.h"
@@ -38,6 +41,7 @@
#include "toolchain/diagnostics/format_providers.h"
#include "toolchain/parse/node_ids.h"
#include "toolchain/sem_ir/cpp_file.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::Check {
@@ -340,7 +344,8 @@ class CarbonExternalASTSource : public clang::ExternalASTSource {
// Map a Carbon entity to a Clang NamedDecl. Returns null if the entity cannot
// currently be represented in C++.
auto MapInstIdToClangDecl(LookupResult lookup) -> clang::NamedDecl*;
auto MapInstIdToClangDeclOrType(LookupResult lookup)
-> std::variant<clang::NamedDecl*, clang::QualType>;
// Get a current best-effort location for the current position within C++
// processing.
@@ -374,12 +379,23 @@ void CarbonExternalASTSource::StartTranslationUnit(
BuildCarbonNamespace();
}
auto CarbonExternalASTSource::MapInstIdToClangDecl(LookupResult lookup)
-> clang::NamedDecl* {
auto CarbonExternalASTSource::MapInstIdToClangDeclOrType(LookupResult lookup)
-> std::variant<clang::NamedDecl*, clang::QualType> {
auto target_inst_id = lookup.scope_result.target_inst_id();
auto target_constant =
context_->constant_values().GetConstantInstId(target_inst_id);
auto target_inst = context_->insts().Get(target_constant);
auto target_const_id = context_->constant_values().Get(target_inst_id);
auto target_inst = context_->constant_values().GetInst(target_const_id);
if (target_inst.type_id() == SemIR::TypeType::TypeId) {
auto type_id =
context_->types().GetTypeIdForTypeConstantId(target_const_id);
auto type = MapToCppType(*context_, type_id);
if (type.isNull()) {
context_->TODO(GetCurrentCppLocId(), "interop with unsupported type");
return nullptr;
}
return type;
}
CARBON_KIND_SWITCH(target_inst) {
case CARBON_KIND(SemIR::Namespace namespace_info): {
auto* decl_context =
@@ -395,12 +411,8 @@ auto CarbonExternalASTSource::MapInstIdToClangDecl(LookupResult lookup)
}
return cast<clang::NamedDecl>(decl_context);
}
case CARBON_KIND(SemIR::ClassType class_type): {
return ExportClassToCpp(*context_, SemIR::LocId(target_inst_id),
target_inst_id, class_type);
}
case SemIR::StructValue::Kind: {
auto callee = GetCallee(context_->sem_ir(), target_constant);
auto callee = GetCallee(context_->sem_ir(), target_inst_id);
auto* callee_function = std::get_if<SemIR::CalleeFunction>(&callee);
if (!callee_function) {
return nullptr;
@@ -490,13 +502,35 @@ auto CarbonExternalASTSource::FindExternalVisibleDeclsByName(
}
// Map the found Carbon entity to a Clang NamedDecl.
auto* clang_decl = MapInstIdToClangDecl(result);
if (!clang_decl) {
return false;
}
CARBON_KIND_SWITCH(MapInstIdToClangDeclOrType(result)) {
case CARBON_KIND(clang::NamedDecl* clang_decl): {
if (clang_decl) {
SetExternalVisibleDeclsForName(decl_context, decl_name, {clang_decl});
return true;
} else {
SetNoExternalVisibleDeclsForName(decl_context, decl_name);
return false;
}
}
SetExternalVisibleDeclsForName(decl_context, decl_name, {clang_decl});
return true;
case CARBON_KIND(clang::QualType type): {
// Create a typedef declaration to model the type result.
// TODO: If the type is a tag type that was declared with this name in
// this context, use the tag decl directly.
auto& ast_context = context_->ast_context();
auto loc = GetCppLocation(
*context_, SemIR::LocId(result.scope_result.target_inst_id()));
auto* typedef_decl = clang::TypedefDecl::Create(
ast_context, const_cast<clang::DeclContext*>(decl_context), loc, loc,
identifier, ast_context.getTrivialTypeSourceInfo(type, loc));
if (isa<clang::CXXRecordDecl>(decl_context)) {
typedef_decl->setAccess(
MapToCppAccess(result.scope_result.access_kind()));
}
SetExternalVisibleDeclsForName(decl_context, decl_name, {typedef_decl});
return true;
}
}
}
auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void {
+17 -18
View File
@@ -17,6 +17,7 @@
#include "toolchain/base/value_ids.h"
#include "toolchain/check/context.h"
#include "toolchain/check/convert.h"
#include "toolchain/check/cpp/export.h"
#include "toolchain/check/cpp/import.h"
#include "toolchain/check/cpp/location.h"
#include "toolchain/check/literal.h"
@@ -137,21 +138,10 @@ static auto VerifyIntegerTypeWidth(Context& context, clang::QualType type,
// Maps a Carbon class type to a C++ type. Returns a null `QualType` if the
// type is not supported.
static auto TryMapClassType(Context& context, SemIR::ClassType class_type)
-> TryMapTypeResult {
static auto TryMapClassType(Context& context, SemIR::TypeInstId class_inst_id,
SemIR::ClassType class_type) -> TryMapTypeResult {
clang::ASTContext& ast_context = context.ast_context();
// If the class was imported from C++, return the original C++ type.
auto clang_decl_id =
context.name_scopes()
.Get(context.classes().Get(class_type.class_id).scope_id)
.clang_decl_context_id();
if (clang_decl_id.has_value()) {
clang::Decl* clang_decl = context.clang_decls().Get(clang_decl_id).key.decl;
auto* tag_type_decl = clang::cast<clang::TagDecl>(clang_decl);
return ast_context.getCanonicalTagType(tag_type_decl);
}
// If the class represents a Carbon type literal, map it to the corresponding
// C++ builtin type.
auto type_info =
@@ -230,10 +220,18 @@ static auto TryMapClassType(Context& context, SemIR::ClassType class_type)
}
}
// Otherwise we don't have a mapping for this Carbon class type.
// TODO: If the class type wasn't imported from C++, create a corresponding
// C++ class type.
return clang::QualType();
// TODO: We cannot yet map specific classes.
if (class_type.specific_id.has_value()) {
return clang::QualType();
}
// Otherwise, find the existing C++ declaration or create a new one.
auto* tag_decl = ExportClassToCpp(context, SemIR::LocId(class_inst_id),
class_inst_id, class_type);
if (!tag_decl) {
return clang::QualType();
}
return ast_context.getCanonicalTagType(tag_decl);
}
// Maps a Carbon type to a C++ type. Either returns the mapped type, a null type
@@ -253,7 +251,8 @@ static auto TryMapType(Context& context, SemIR::TypeId type_id)
return context.ast_context().CharTy;
}
case CARBON_KIND(SemIR::ClassType class_type): {
return TryMapClassType(context, class_type);
return TryMapClassType(context, context.types().GetTypeInstId(type_id),
class_type);
}
case CARBON_KIND(SemIR::ConstType const_type): {
return WrappedType{
+7 -3
View File
@@ -316,9 +316,13 @@ class Derived {
fn F[self: Self]() {
//@dump-sem-ir-begin
// TODO: It should be possible to call protected methods in the base class.
// CHECK:STDERR: fail_todo_import_function_member_protected_extend_call_instance_in_member.carbon:[[@LINE+4]]:5: error: call argument of type `Derived` is not supported [CppCallArgTypeNotSupported]
// CHECK:STDERR: self.instance_fn();
// CHECK:STDERR: ^~~~
// CHECK:STDERR: fail_todo_import_function_member_protected_extend_call_instance_in_member.carbon:[[@LINE+8]]:22: error: no matching function for call to 'instance_fn' [CppInteropParseError]
// CHECK:STDERR: 19 | self.instance_fn();
// CHECK:STDERR: | ^
// CHECK:STDERR: fail_todo_import_function_member_protected_extend_call_instance_in_member.carbon:[[@LINE-10]]:10: in file included here [InCppInclude]
// CHECK:STDERR: ./function_member_protected.h:4:8: note: candidate function not viable: cannot convert argument of incomplete type 'const Carbon::Derived' to 'C' for object argument [CppInteropParseNote]
// CHECK:STDERR: 4 | auto instance_fn() -> void;
// CHECK:STDERR: | ^
// CHECK:STDERR:
self.instance_fn();
//@dump-sem-ir-end
@@ -0,0 +1,36 @@
// 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/type_alias/export.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/type_alias/export.carbon
// --- aliases.carbon
library "[[@TEST_NAME]]";
import Cpp;
alias A = i32;
class C {}
alias CA = C;
inline Cpp '''
// Valid redeclarations -- same type.
using T = Carbon::A;
using T = int;
// Valid redeclarations -- same type.
using U = Carbon::C;
using U = Carbon::CA;
''';
// Valid: Cpp.U is the same type as CA.
fn F(p: CA*) -> Cpp.U* {
return p;
}
@@ -6,9 +6,9 @@
//
// AUTOUPDATE
// TIP: To test this file alone, run:
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/typedef.carbon
// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/type_alias/import.carbon
// TIP: To dump output, run:
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/typedef.carbon
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/type_alias/import.carbon
// --- i32_typedef.h
+5
View File
@@ -188,6 +188,11 @@ class ConstantValueStore {
return insts_->Is<InstT>(GetInstId(const_id));
}
// Returns the requested instruction from the underlying constant inst.
auto GetInst(ConstantId const_id) const -> Inst {
return insts_->Get(GetInstId(const_id));
}
// Returns the requested instruction from the underlying constant inst, which
// is known to have the specified type.
template <typename InstT>