Просмотр исходного кода

Support for mapping str to std::string_view in interop. (#6079)

We already did the opposite direction; this enables use of `str` in
overload resolution.

Fixes #6062

---------

Co-authored-by: Dana Jansens <danakj@orodu.net>
Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
Richard Smith 7 месяцев назад
Родитель
Сommit
730935691a

+ 2 - 2
toolchain/check/cpp_import.cpp

@@ -1983,7 +1983,7 @@ static auto IsTopCppScope(Context& context, SemIR::NameScopeId scope_id)
 }
 
 // For builtin names like `Cpp.long`, return the associated types.
-static auto LookupBuiltInTypes(Context& context, SemIR::LocId loc_id,
+static auto LookupBuiltinTypes(Context& context, SemIR::LocId loc_id,
                                SemIR::NameScopeId scope_id,
                                SemIR::NameId name_id) -> SemIR::InstId {
   if (!IsTopCppScope(context, scope_id)) {
@@ -2118,7 +2118,7 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id,
   auto lookup = ClangLookupName(context, scope_id, name_id);
   if (!lookup) {
     SemIR::InstId builtin_inst_id =
-        LookupBuiltInTypes(context, loc_id, scope_id, name_id);
+        LookupBuiltinTypes(context, loc_id, scope_id, name_id);
     if (builtin_inst_id.has_value()) {
       AddNameToScope(context, scope_id, name_id, SemIR::AccessKind::Public,
                      builtin_inst_id);

+ 101 - 62
toolchain/check/cpp_type_mapping.cpp

@@ -10,6 +10,7 @@
 
 #include "clang/AST/Type.h"
 #include "clang/Basic/TargetInfo.h"
+#include "clang/Sema/Lookup.h"
 #include "toolchain/base/int.h"
 #include "toolchain/base/kind_switch.h"
 #include "toolchain/base/value_ids.h"
@@ -20,6 +21,7 @@
 #include "toolchain/sem_ir/ids.h"
 #include "toolchain/sem_ir/inst.h"
 #include "toolchain/sem_ir/type.h"
+#include "toolchain/sem_ir/type_info.h"
 #include "toolchain/sem_ir/typed_insts.h"
 
 namespace Carbon::Check {
@@ -47,20 +49,106 @@ static auto FindIntLiteralBitWidth(Context& context, SemIR::InstId arg_id)
   return (arg_non_sign_bits <= 32) ? IntId::MakeRaw(32) : IntId::MakeRaw(64);
 }
 
-// Maps a Carbon builtin type to a Cpp type. Returns an empty QualType if the
+// Attempts to look up a type by name, and returns the corresponding `QualType`,
+// or a null type if lookup fails. `name_components` is the full path of the
+// type, including any namespaces or nested types, separated into separate
+// strings.
+static auto LookupCppType(
+    Context& context, std::initializer_list<llvm::StringRef> name_components)
+    -> clang::QualType {
+  clang::ASTUnit* ast = context.sem_ir().clang_ast_unit();
+  CARBON_CHECK(ast);
+  clang::Sema& sema = ast->getSema();
+
+  clang::Decl* decl = sema.getASTContext().getTranslationUnitDecl();
+  for (auto name_component : name_components) {
+    auto* scope = dyn_cast<clang::DeclContext>(decl);
+    if (!scope) {
+      return clang::QualType();
+    }
+
+    // TODO: Map the LocId of the lookup to a clang SourceLocation and provide
+    // it here so that clang's diagnostics can point into the carbon code that
+    // uses the name.
+    auto* identifier = sema.getPreprocessor().getIdentifierInfo(name_component);
+    clang::LookupResult lookup(
+        sema, clang::DeclarationNameInfo(identifier, clang::SourceLocation()),
+        clang::Sema::LookupNameKind::LookupOrdinaryName);
+    if (!sema.LookupQualifiedName(lookup, scope) || !lookup.isSingleResult()) {
+      return clang::QualType();
+    }
+    decl = lookup.getFoundDecl();
+  }
+
+  auto* type_decl = dyn_cast<clang::TypeDecl>(decl);
+  return type_decl ? sema.getASTContext().getTypeDeclType(type_decl)
+                   : clang::QualType();
+}
+
+// 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)
+    -> clang::QualType {
+  // If the class was imported from C++, return the original C++ type.
+  auto clang_decl_id =
+      context.name_scopes()
+          .Get(context.sem_ir().classes().Get(class_type.class_id).scope_id)
+          .clang_decl_context_id();
+  if (clang_decl_id.has_value()) {
+    clang::Decl* clang_decl =
+        context.sem_ir().clang_decls().Get(clang_decl_id).decl;
+    auto* tag_type_decl = clang::cast<clang::TagDecl>(clang_decl);
+    return context.ast_context().getCanonicalTagType(tag_type_decl);
+  }
+
+  // If the class represents a Carbon type literal, map it to the corresponding
+  // C++ builtin type.
+  auto literal = SemIR::TypeLiteralInfo::ForType(context.sem_ir(), class_type);
+  switch (literal.kind) {
+    case SemIR::TypeLiteralInfo::None: {
+      break;
+    }
+    case SemIR::TypeLiteralInfo::Numeric: {
+      switch (literal.numeric.kind) {
+        case SemIR::NumericTypeLiteralInfo::None: {
+          CARBON_FATAL("Unexpected invalid numeric type literal");
+        }
+        case SemIR::NumericTypeLiteralInfo::Float: {
+          return context.ast_context().getRealTypeForBitwidth(
+              literal.numeric.bit_width_id.AsValue(),
+              clang::FloatModeKind::NoFloat);
+        }
+        case SemIR::NumericTypeLiteralInfo::Int: {
+          return context.ast_context().getIntTypeForBitwidth(
+              literal.numeric.bit_width_id.AsValue(), true);
+        }
+        case SemIR::NumericTypeLiteralInfo::UInt: {
+          return context.ast_context().getIntTypeForBitwidth(
+              literal.numeric.bit_width_id.AsValue(), false);
+        }
+      }
+    }
+    case SemIR::TypeLiteralInfo::Char: {
+      return context.ast_context().CharTy;
+    }
+    case SemIR::TypeLiteralInfo::Str: {
+      return LookupCppType(context, {"std", "string_view"});
+    }
+  }
+
+  // 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();
+}
+
+// Maps a non-wrapper (no const or pointer) Carbon type to a C++ type. Returns a
+// null QualType if the type is not supported.
 // TODO: Have both Carbon -> C++ and C++ -> Carbon mappings in a single place
 // to keep them in sync.
-static auto TryMapBuiltinType(Context& context, SemIR::InstId inst_id,
+static auto MapNonWrapperType(Context& context, SemIR::InstId inst_id,
                               SemIR::TypeId type_id) -> clang::QualType {
-  // TODO: Object representation should not be used here. Instead, use
-  // NumericTypeLiteralInfo to decompose a ClassType into a uN / iN / fN type.
-  auto object_repr_id = context.sem_ir().types().GetObjectRepr(type_id);
-  if (!object_repr_id.has_value()) {
-    return clang::QualType();
-  }
-  auto type_inst =
-      context.insts().Get(context.sem_ir().types().GetInstId(object_repr_id));
+  auto type_inst = context.sem_ir().types().GetAsInst(type_id);
 
   CARBON_KIND_SWITCH(type_inst) {
     case SemIR::BoolType::Kind: {
@@ -69,6 +157,9 @@ static auto TryMapBuiltinType(Context& context, SemIR::InstId inst_id,
     case Carbon::SemIR::CharLiteralType::Kind: {
       return context.ast_context().CharTy;
     }
+    case CARBON_KIND(SemIR::ClassType class_type): {
+      return TryMapClassType(context, class_type);
+    }
     case SemIR::IntLiteralType::Kind: {
       IntId bit_width_id = FindIntLiteralBitWidth(context, inst_id);
       if (bit_width_id == IntId::None) {
@@ -77,68 +168,16 @@ static auto TryMapBuiltinType(Context& context, SemIR::InstId inst_id,
       return context.ast_context().getIntTypeForBitwidth(bit_width_id.AsValue(),
                                                          true);
     }
-    case CARBON_KIND(SemIR::IntType int_type): {
-      auto bit_width_inst = context.sem_ir().insts().TryGetAs<SemIR::IntValue>(
-          int_type.bit_width_id);
-      return context.ast_context().getIntTypeForBitwidth(
-          bit_width_inst->int_id.AsValue(), int_type.int_kind.is_signed());
-    }
     // TODO: What if the value doesn't fit to f64?
     case SemIR::FloatLiteralType::Kind: {
       return context.ast_context().DoubleTy;
     }
-    case CARBON_KIND(SemIR::FloatType float_type): {
-      auto bit_width_inst = context.sem_ir().insts().TryGetAs<SemIR::IntValue>(
-          float_type.bit_width_id);
-      return context.ast_context().getRealTypeForBitwidth(
-          bit_width_inst->int_id.AsValue(), clang::FloatModeKind::NoFloat);
-    }
     default: {
       return clang::QualType();
     }
   }
 }
 
-// Maps a Carbon class type to a C++ type. Returns a null `QualType` if the
-// Carbon type is not a `ClassType` or was not imported from C++.
-// TODO: If the class type wasn't imported from C++, create a corresponding C++
-// class type.
-static auto TryMapClassType(Context& context, SemIR::TypeId type_id)
-    -> clang::QualType {
-  auto class_type =
-      context.sem_ir().types().TryGetAs<SemIR::ClassType>(type_id);
-  if (!class_type) {
-    return clang::QualType();
-  }
-
-  auto clang_decl_id =
-      context.name_scopes()
-          .Get(context.sem_ir().classes().Get(class_type->class_id).scope_id)
-          .clang_decl_context_id();
-  if (!clang_decl_id.has_value()) {
-    return clang::QualType();
-  }
-  clang::Decl* clang_decl =
-      context.sem_ir().clang_decls().Get(clang_decl_id).decl;
-  auto* tag_type_decl = clang::cast<clang::TagDecl>(clang_decl);
-  return context.ast_context().getCanonicalTagType(tag_type_decl);
-}
-
-// Maps a non-wrapper (no const or pointer) Carbon type to a C++ type.
-static auto MapNonWrapperType(Context& context, SemIR::InstId inst_id,
-                              SemIR::TypeId type_id) -> clang::QualType {
-  // It's important to check for a class type first, because an enum imported
-  // from C++ is both a Carbon class type and has an object representation that
-  // is a builtin type, and we want to map back to the enum.
-  // TODO: The order won't matter once TryMapBuiltinType stops looking through
-  // adapters.
-  clang::QualType mapped_type = TryMapClassType(context, type_id);
-  if (mapped_type.isNull()) {
-    mapped_type = TryMapBuiltinType(context, inst_id, type_id);
-  }
-  return mapped_type;
-}
-
 // TODO: unify this with the C++ to Carbon type mapping function.
 auto MapToCppType(Context& context, SemIR::InstId inst_id) -> clang::QualType {
   auto type_id = context.insts().Get(inst_id).type_id();

+ 21 - 20
toolchain/check/testdata/interop/cpp/stdlib/string_view.carbon

@@ -10,8 +10,6 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/stdlib/string_view.carbon
 
-// TODO: Tests marked as `fail_todo_5891_` to fixed as a follow-up of https://github.com/carbon-language/carbon-lang/pull/5891.
-
 // --- string_view.h
 
 namespace std {
@@ -38,7 +36,7 @@ namespace std {
 auto Consume(std::string_view sv) -> void;
 auto Produce() -> std::string_view;
 
-// --- fail_todo_5891_import_multiple.carbon
+// --- import_multiple.carbon
 
 library "[[@TEST_NAME]]";
 
@@ -46,13 +44,6 @@ import Cpp library "string_view.h";
 
 //@dump-sem-ir-begin
 fn F() {
-  // CHECK:STDERR: fail_todo_5891_import_multiple.carbon:[[@LINE+7]]:15: error: call argument of type `str` is not supported [CppCallArgTypeNotSupported]
-  // CHECK:STDERR:   Cpp.Consume("hello");
-  // CHECK:STDERR:               ^~~~~~~
-  // CHECK:STDERR: fail_todo_5891_import_multiple.carbon:[[@LINE+4]]:3: note: in call to Cpp function here [InCallToCppFunction]
-  // CHECK:STDERR:   Cpp.Consume("hello");
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR:
   Cpp.Consume("hello");
 }
 //@dump-sem-ir-end
@@ -63,7 +54,7 @@ fn G() -> str {
 }
 //@dump-sem-ir-end
 
-// CHECK:STDOUT: --- fail_todo_5891_import_multiple.carbon
+// CHECK:STDOUT: --- import_multiple.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
@@ -75,17 +66,19 @@ fn G() -> str {
 // CHECK:STDOUT:   %int_8: Core.IntLiteral = int_value 8 [concrete]
 // CHECK:STDOUT:   %u8: type = class_type @UInt, @UInt(%int_8) [concrete]
 // CHECK:STDOUT:   %ptr.3e8: type = ptr_type %u8 [concrete]
-// CHECK:STDOUT:   %.fd2: type = cpp_overload_set_type @Produce [concrete]
+// CHECK:STDOUT:   %.fd2: type = cpp_overload_set_type @Consume__carbon_thunk [concrete]
 // CHECK:STDOUT:   %empty_struct.c28: %.fd2 = struct_value () [concrete]
 // CHECK:STDOUT:   %str.3b1: %ptr.3e8 = string_literal "hello" [concrete]
 // CHECK:STDOUT:   %int_5: %u64 = int_value 5 [concrete]
 // CHECK:STDOUT:   %String.val: %str.ee0 = struct_value (%str.3b1, %int_5) [concrete]
 // CHECK:STDOUT:   %pattern_type.461: type = pattern_type %str.ee0 [concrete]
+// CHECK:STDOUT:   %ptr.85f: type = ptr_type %str.ee0 [concrete]
+// CHECK:STDOUT:   %Consume__carbon_thunk.type: type = fn_type @Consume__carbon_thunk [concrete]
+// CHECK:STDOUT:   %Consume__carbon_thunk: %Consume__carbon_thunk.type = struct_value () [concrete]
 // CHECK:STDOUT:   %G.type: type = fn_type @G [concrete]
 // CHECK:STDOUT:   %G: %G.type = struct_value () [concrete]
-// CHECK:STDOUT:   %.a47: type = cpp_overload_set_type @Produce__carbon_thunk [concrete]
+// CHECK:STDOUT:   %.a47: type = cpp_overload_set_type @G [concrete]
 // CHECK:STDOUT:   %empty_struct.ab9: %.a47 = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.85f: type = ptr_type %str.ee0 [concrete]
 // CHECK:STDOUT:   %Produce__carbon_thunk.type: type = fn_type @Produce__carbon_thunk [concrete]
 // CHECK:STDOUT:   %Produce__carbon_thunk: %Produce__carbon_thunk.type = struct_value () [concrete]
 // CHECK:STDOUT: }
@@ -96,8 +89,13 @@ fn G() -> str {
 // CHECK:STDOUT:     .Produce = %.5d1
 // CHECK:STDOUT:     import Cpp//...
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %.f17: %.fd2 = cpp_overload_set_value @Produce [concrete = constants.%empty_struct.c28]
-// CHECK:STDOUT:   %.5d1: %.a47 = cpp_overload_set_value @Produce__carbon_thunk [concrete = constants.%empty_struct.ab9]
+// CHECK:STDOUT:   %.f17: %.fd2 = cpp_overload_set_value @Consume__carbon_thunk [concrete = constants.%empty_struct.c28]
+// CHECK:STDOUT:   %Consume__carbon_thunk.decl: %Consume__carbon_thunk.type = fn_decl @Consume__carbon_thunk [concrete = constants.%Consume__carbon_thunk] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.5d1: %.a47 = cpp_overload_set_value @G [concrete = constants.%empty_struct.ab9]
 // CHECK:STDOUT:   %Produce__carbon_thunk.decl: %Produce__carbon_thunk.type = fn_decl @Produce__carbon_thunk [concrete = constants.%Produce__carbon_thunk] {
 // CHECK:STDOUT:     <elided>
 // CHECK:STDOUT:   } {
@@ -123,6 +121,9 @@ fn G() -> str {
 // CHECK:STDOUT:   %str: %ptr.3e8 = string_literal "hello" [concrete = constants.%str.3b1]
 // CHECK:STDOUT:   %int_5: %u64 = int_value 5 [concrete = constants.%int_5]
 // CHECK:STDOUT:   %String.val: %str.ee0 = struct_value (%str, %int_5) [concrete = constants.%String.val]
+// CHECK:STDOUT:   %.loc8: ref %str.ee0 = value_as_ref %String.val
+// CHECK:STDOUT:   %addr: %ptr.85f = addr_of %.loc8
+// CHECK:STDOUT:   %Consume__carbon_thunk.call: init %empty_tuple.type = call imports.%Consume__carbon_thunk.decl(%addr)
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
@@ -130,10 +131,10 @@ fn G() -> str {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
 // CHECK:STDOUT:   %Produce.ref: %.a47 = name_ref Produce, imports.%.5d1 [concrete = constants.%empty_struct.ab9]
-// CHECK:STDOUT:   %.loc20: ref %str.ee0 = splice_block %return {}
-// CHECK:STDOUT:   %addr: %ptr.85f = addr_of %.loc20
+// CHECK:STDOUT:   %.loc13: ref %str.ee0 = splice_block %return {}
+// CHECK:STDOUT:   %addr: %ptr.85f = addr_of %.loc13
 // CHECK:STDOUT:   %Produce__carbon_thunk.call: init %empty_tuple.type = call imports.%Produce__carbon_thunk.decl(%addr)
-// CHECK:STDOUT:   %.loc21: init %str.ee0 = in_place_init %Produce__carbon_thunk.call, %.loc20
-// CHECK:STDOUT:   return %.loc21 to %return
+// CHECK:STDOUT:   %.loc14: init %str.ee0 = in_place_init %Produce__carbon_thunk.call, %.loc13
+// CHECK:STDOUT:   return %.loc14 to %return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 1 - 1
toolchain/sem_ir/name_scope.h

@@ -357,7 +357,7 @@ class NameScopeStore {
 
   // Returns whether the provided scope ID is valid and is directly contained
   // within the Core package.
-  auto IsInCorePackage(NameScopeId scope_id) const -> bool {
+  auto IsInCorePackageRoot(NameScopeId scope_id) const -> bool {
     return scope_id.has_value() &&
            IsCorePackage(Get(scope_id).parent_scope_id());
   }

+ 2 - 3
toolchain/sem_ir/type_info.cpp

@@ -85,7 +85,7 @@ auto NumericTypeLiteralInfo::ForType(const File& file, ClassType class_type)
 
   // The class must be declared in the `Core` package.
   const auto& class_info = file.classes().Get(class_type.class_id);
-  if (!file.name_scopes().IsInCorePackage(class_info.scope_id)) {
+  if (!file.name_scopes().IsInCorePackageRoot(class_info.scope_id)) {
     return NumericTypeLiteralInfo::Invalid;
   }
 
@@ -145,7 +145,7 @@ auto TypeLiteralInfo::ForType(const File& file, ClassType class_type)
 
   // The class must be declared in the `Core` package.
   const auto& class_info = file.classes().Get(class_type.class_id);
-  if (!file.name_scopes().IsInCorePackage(class_info.scope_id)) {
+  if (!file.name_scopes().IsInCorePackageRoot(class_info.scope_id)) {
     return {.kind = None};
   }
 
@@ -164,7 +164,6 @@ auto TypeLiteralInfo::ForType(const File& file, ClassType class_type)
 
 auto TypeLiteralInfo::PrintLiteral(const File& file,
                                    llvm::raw_ostream& out) const -> void {
-  CARBON_CHECK(is_valid());
   switch (kind) {
     case None:
       CARBON_FATAL("Printing invalid type literal");