فهرست منبع

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.
Richard Smith 2 هفته پیش
والد
کامیت
43867a678b

+ 11 - 0
toolchain/check/cpp/access.cpp

@@ -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 - 0
toolchain/check/cpp/access.h

@@ -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
toolchain/check/cpp/export.cpp

@@ -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
toolchain/check/cpp/generate_ast.cpp

@@ -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
toolchain/check/cpp/type_mapping.cpp

@@ -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
toolchain/check/testdata/interop/cpp/class/access.carbon

@@ -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

+ 36 - 0
toolchain/check/testdata/interop/cpp/type_alias/export.carbon

@@ -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;
+}

+ 2 - 2
toolchain/check/testdata/interop/cpp/typedef.carbon → toolchain/check/testdata/interop/cpp/type_alias/import.carbon

@@ -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 - 0
toolchain/sem_ir/constant.h

@@ -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>