mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 21:10:12 +01:00
Interop: map C++ std::string_view into Carbon str when importing. (#5985)
We assume these types have the same representation. For now, that will only be the case for libc++ on 64-bit targets, because libc++ puts the size field first, and `Core.String` always uses a 64-bit size field even on 32-bit targets. --------- Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
This commit is contained in:
co-authored by
Chandler Carruth
parent
82ba1a43a1
commit
bd90fe1d9b
@@ -21,6 +21,7 @@ cc_library(
|
||||
"context.cpp",
|
||||
"control_flow.cpp",
|
||||
"convert.cpp",
|
||||
"cpp_custom_type_mapping.cpp",
|
||||
"cpp_thunk.cpp",
|
||||
"decl_name_stack.cpp",
|
||||
"deduce.cpp",
|
||||
@@ -66,6 +67,7 @@ cc_library(
|
||||
"context.h",
|
||||
"control_flow.h",
|
||||
"convert.h",
|
||||
"cpp_custom_type_mapping.h",
|
||||
"cpp_thunk.h",
|
||||
"decl_introducer_state.h",
|
||||
"decl_name_stack.h",
|
||||
|
||||
@@ -124,12 +124,17 @@ auto ConvertCallArgs(Context& context, SemIR::LocId call_loc_id,
|
||||
|
||||
// A type that has been converted for use as a type expression.
|
||||
struct TypeExpr {
|
||||
static const TypeExpr None;
|
||||
|
||||
// The converted expression of type `type`, or `ErrorInst::InstId`.
|
||||
SemIR::TypeInstId inst_id;
|
||||
// The corresponding type, or `ErrorInst::TypeId`.
|
||||
SemIR::TypeId type_id;
|
||||
};
|
||||
|
||||
constexpr inline TypeExpr TypeExpr::None = {.inst_id = SemIR::TypeInstId::None,
|
||||
.type_id = SemIR::TypeId::None};
|
||||
|
||||
// Converts an expression for use as a type.
|
||||
//
|
||||
// If `diagnose` is true, errors are diagnosed to the user. Set it to false when
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
// 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 "toolchain/check/cpp_custom_type_mapping.h"
|
||||
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclTemplate.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
// A small, lightweight library of AST matchers. Unlike clang's ASTMatchers,
|
||||
// this avoids heap allocations and is suitable for one-off matching rather than
|
||||
// matching against a whole AST.
|
||||
namespace Matchers {
|
||||
// A matcher for a type T is just a function that takes a T and returns whether
|
||||
// it matched. Matchers should be invoked immediately, and are not expected to
|
||||
// outlive the arguments of the call that created them.
|
||||
// TODO: We could avoid the indirect calls by making the below functions be
|
||||
// templated on the inner matcher.
|
||||
template <typename T>
|
||||
using Matcher = llvm::function_ref<auto(T)->bool>;
|
||||
|
||||
// Returns a matcher for class declarations that determines whether the given
|
||||
// class is a class template specialization in namespace std with the specified
|
||||
// name and template arguments matching the given predicate.
|
||||
static auto StdClassTemplate(
|
||||
llvm::StringLiteral name,
|
||||
Matcher<const clang::TemplateArgumentList&> args_matcher
|
||||
[[clang::lifetimebound]]) -> auto {
|
||||
return [=](const clang::CXXRecordDecl* class_decl) -> bool {
|
||||
const auto* specialization =
|
||||
dyn_cast<clang::ClassTemplateSpecializationDecl>(class_decl);
|
||||
const auto* identifier = class_decl->getIdentifier();
|
||||
return specialization && identifier && identifier->isStr(name) &&
|
||||
specialization->isInStdNamespace() &&
|
||||
args_matcher(specialization->getTemplateArgs());
|
||||
};
|
||||
}
|
||||
|
||||
// Returns a matcher that matches types if they are class types whose class
|
||||
// matches the given matcher.
|
||||
static auto Class(Matcher<const clang::CXXRecordDecl*> class_matcher
|
||||
[[clang::lifetimebound]]) -> auto {
|
||||
return [=](clang::QualType type) -> bool {
|
||||
const auto* class_decl = type->getAsCXXRecordDecl();
|
||||
return !type.hasQualifiers() && class_decl && class_matcher(class_decl);
|
||||
};
|
||||
}
|
||||
|
||||
// Returns a matcher that determines whether the given template argument is a
|
||||
// type matching the given predicate.
|
||||
static auto TypeTemplateArgument(Matcher<clang::QualType> type_matcher
|
||||
[[clang::lifetimebound]]) -> auto {
|
||||
return [=](clang::TemplateArgument arg) -> bool {
|
||||
return arg.getKind() == clang::TemplateArgument::Type &&
|
||||
type_matcher(arg.getAsType());
|
||||
};
|
||||
}
|
||||
|
||||
// A matcher that determines whether the given type is `char`.
|
||||
static auto Char(clang::QualType type) -> bool {
|
||||
return !type.hasQualifiers() && type->isCharType();
|
||||
}
|
||||
|
||||
// Returns a matcher that determines whether the given template argument list
|
||||
// matches the given sequence of template argument matchers.
|
||||
static auto TemplateArgumentsAre(
|
||||
std::initializer_list<Matcher<clang::TemplateArgument>> arg_matchers
|
||||
[[clang::lifetimebound]]) -> auto {
|
||||
return [=](const clang::TemplateArgumentList& args) -> bool {
|
||||
if (args.size() != arg_matchers.size()) {
|
||||
return false;
|
||||
}
|
||||
for (auto [arg, matcher] : llvm::zip_equal(args.asArray(), arg_matchers)) {
|
||||
if (!matcher(arg)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
// A matcher for `std::char_traits<char>`.
|
||||
static auto StdCharTraitsChar(clang::QualType type) -> bool {
|
||||
return Class(StdClassTemplate(
|
||||
"char_traits", TemplateArgumentsAre({TypeTemplateArgument(Char)})))(type);
|
||||
}
|
||||
|
||||
// A matcher for `std::string_view`.
|
||||
static auto StdStringView(const clang::CXXRecordDecl* record_decl) -> bool {
|
||||
return StdClassTemplate(
|
||||
"basic_string_view",
|
||||
TemplateArgumentsAre({TypeTemplateArgument(Char),
|
||||
TypeTemplateArgument(StdCharTraitsChar)}))(
|
||||
record_decl);
|
||||
}
|
||||
} // end namespace Matchers
|
||||
|
||||
auto GetCustomCppTypeMapping(const clang::CXXRecordDecl* record_decl)
|
||||
-> CustomCppTypeMapping {
|
||||
if (Matchers::StdStringView(record_decl)) {
|
||||
return CustomCppTypeMapping::Str;
|
||||
}
|
||||
|
||||
return CustomCppTypeMapping::None;
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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
|
||||
|
||||
#ifndef CARBON_TOOLCHAIN_CHECK_CPP_CUSTOM_TYPE_MAPPING_H_
|
||||
#define CARBON_TOOLCHAIN_CHECK_CPP_CUSTOM_TYPE_MAPPING_H_
|
||||
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
// Carbon types that have a custom mapping from C++.
|
||||
enum class CustomCppTypeMapping : uint8_t {
|
||||
// None.
|
||||
None,
|
||||
|
||||
// The Carbon `Str` type, which maps to `std::string_view`.
|
||||
Str,
|
||||
};
|
||||
|
||||
// Determines whether record_decl is a C++ class that has a custom mapping into
|
||||
// Carbon, and if so, returns the corresponding Carbon type. Otherwise returns
|
||||
// None.
|
||||
auto GetCustomCppTypeMapping(const clang::CXXRecordDecl* record_decl)
|
||||
-> CustomCppTypeMapping;
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
#endif // CARBON_TOOLCHAIN_CHECK_CPP_CUSTOM_TYPE_MAPPING_H_
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "toolchain/check/class.h"
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/convert.h"
|
||||
#include "toolchain/check/cpp_custom_type_mapping.h"
|
||||
#include "toolchain/check/cpp_thunk.h"
|
||||
#include "toolchain/check/diagnostic_helpers.h"
|
||||
#include "toolchain/check/eval.h"
|
||||
@@ -1200,7 +1201,24 @@ static auto MapBuiltinType(Context& context, SemIR::LocId loc_id,
|
||||
// TODO: Handle floating-point types that map to named aliases.
|
||||
}
|
||||
|
||||
return {.inst_id = SemIR::TypeInstId::None, .type_id = SemIR::TypeId::None};
|
||||
return TypeExpr::None;
|
||||
}
|
||||
|
||||
// Determines whether record_decl is a C++ class that has a custom mapping into
|
||||
// Carbon, and if so, returns the corresponding Carbon type. Otherwise returns
|
||||
// None.
|
||||
static auto LookupCustomRecordType(Context& context,
|
||||
const clang::CXXRecordDecl* record_decl)
|
||||
-> TypeExpr {
|
||||
switch (GetCustomCppTypeMapping(record_decl)) {
|
||||
case CustomCppTypeMapping::None:
|
||||
return TypeExpr::None;
|
||||
|
||||
case CustomCppTypeMapping::Str:
|
||||
return MakeStringType(
|
||||
context,
|
||||
AddImportIRInst(context.sem_ir(), record_decl->getLocation()));
|
||||
}
|
||||
}
|
||||
|
||||
// Maps a C++ tag type (class, struct, union, enum) to a Carbon type.
|
||||
@@ -1210,12 +1228,21 @@ static auto MapTagType(Context& context, const clang::TagType& type)
|
||||
CARBON_CHECK(tag_decl);
|
||||
|
||||
// Check if the declaration is already mapped.
|
||||
SemIR::InstId record_inst_id = LookupClangDeclInstId(context, tag_decl);
|
||||
if (!record_inst_id.has_value()) {
|
||||
record_inst_id = ImportTagDecl(context, tag_decl);
|
||||
SemIR::InstId tag_inst_id = LookupClangDeclInstId(context, tag_decl);
|
||||
if (!tag_inst_id.has_value()) {
|
||||
if (auto* record_decl = dyn_cast<clang::CXXRecordDecl>(tag_decl)) {
|
||||
auto custom_type = LookupCustomRecordType(context, record_decl);
|
||||
if (custom_type.inst_id.has_value()) {
|
||||
context.sem_ir().clang_decls().Add(
|
||||
{.decl = record_decl, .inst_id = custom_type.inst_id});
|
||||
return custom_type;
|
||||
}
|
||||
}
|
||||
|
||||
tag_inst_id = ImportTagDecl(context, tag_decl);
|
||||
}
|
||||
SemIR::TypeInstId record_type_inst_id =
|
||||
context.types().GetAsTypeInstId(record_inst_id);
|
||||
context.types().GetAsTypeInstId(tag_inst_id);
|
||||
return {
|
||||
.inst_id = record_type_inst_id,
|
||||
.type_id = context.types().GetTypeIdForTypeInstId(record_type_inst_id)};
|
||||
@@ -1237,7 +1264,7 @@ static auto MapNonWrapperType(Context& context, SemIR::LocId loc_id,
|
||||
CARBON_CHECK(!type.hasQualifiers() && !type->isPointerType(),
|
||||
"Should not see wrapper types here");
|
||||
|
||||
return {.inst_id = SemIR::TypeInstId::None, .type_id = SemIR::TypeId::None};
|
||||
return TypeExpr::None;
|
||||
}
|
||||
|
||||
// Maps a qualified C++ type to a Carbon type.
|
||||
@@ -1254,7 +1281,7 @@ static auto MapQualifiedType(Context& context, clang::QualType type,
|
||||
|
||||
// TODO: Support other qualifiers.
|
||||
if (!quals.empty()) {
|
||||
return {.inst_id = SemIR::TypeInstId::None, .type_id = SemIR::TypeId::None};
|
||||
return TypeExpr::None;
|
||||
}
|
||||
|
||||
return type_expr;
|
||||
@@ -1269,7 +1296,7 @@ static auto MapPointerType(Context& context, clang::QualType type,
|
||||
!nullability.has_value() ||
|
||||
*nullability != clang::NullabilityKind::NonNull) {
|
||||
// TODO: Support nullable pointers.
|
||||
return {.inst_id = SemIR::TypeInstId::None, .type_id = SemIR::TypeId::None};
|
||||
return TypeExpr::None;
|
||||
}
|
||||
|
||||
SemIR::TypeId pointer_type_id =
|
||||
@@ -1467,7 +1494,7 @@ static auto GetReturnTypeExpr(Context& context, SemIR::LocId loc_id,
|
||||
|
||||
if (!isa<clang::CXXConstructorDecl>(clang_decl)) {
|
||||
// void.
|
||||
return {.inst_id = SemIR::TypeInstId::None, .type_id = SemIR::TypeId::None};
|
||||
return TypeExpr::None;
|
||||
}
|
||||
|
||||
// TODO: Make this a `PartialType`.
|
||||
|
||||
+17
-15
@@ -7,6 +7,7 @@
|
||||
#include "toolchain/check/call.h"
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/convert.h"
|
||||
#include "toolchain/check/diagnostic_helpers.h"
|
||||
#include "toolchain/check/name_lookup.h"
|
||||
#include "toolchain/check/type.h"
|
||||
#include "toolchain/check/type_completion.h"
|
||||
@@ -106,23 +107,23 @@ static auto GetStringLiteralRepr(Context& context, SemIR::LocId loc_id,
|
||||
|
||||
auto MakeStringLiteral(Context& context, Parse::StringLiteralId node_id,
|
||||
StringLiteralValueId value_id) -> SemIR::InstId {
|
||||
auto str_type_id = MakeStringType(context, node_id);
|
||||
if (!RequireCompleteType(context, str_type_id, node_id, [&] {
|
||||
auto str_type = MakeStringType(context, node_id);
|
||||
if (!RequireCompleteType(context, str_type.type_id, node_id, [&] {
|
||||
CARBON_DIAGNOSTIC(StringLiteralTypeIncomplete, Error,
|
||||
"type {0} is incomplete", SemIR::TypeId);
|
||||
"type {0} is incomplete", InstIdAsType);
|
||||
return context.emitter().Build(node_id, StringLiteralTypeIncomplete,
|
||||
str_type_id);
|
||||
str_type.inst_id);
|
||||
})) {
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
|
||||
auto repr = GetStringLiteralRepr(context, node_id, str_type_id);
|
||||
auto repr = GetStringLiteralRepr(context, node_id, str_type.type_id);
|
||||
if (!repr) {
|
||||
if (str_type_id != SemIR::ErrorInst::TypeId) {
|
||||
if (str_type.type_id != SemIR::ErrorInst::TypeId) {
|
||||
CARBON_DIAGNOSTIC(StringLiteralTypeUnexpected, Error,
|
||||
"unexpected representation for type {0}",
|
||||
SemIR::TypeId);
|
||||
context.emitter().Emit(node_id, StringLiteralTypeUnexpected, str_type_id);
|
||||
"unexpected representation for type {0}", InstIdAsType);
|
||||
context.emitter().Emit(node_id, StringLiteralTypeUnexpected,
|
||||
str_type.inst_id);
|
||||
}
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
@@ -157,17 +158,18 @@ auto MakeStringLiteral(Context& context, Parse::StringLiteralId node_id,
|
||||
// Build the representation struct.
|
||||
auto elements_id = context.inst_blocks().Add({ptr_value_id, size_value_id});
|
||||
return AddInst<SemIR::StructValue>(
|
||||
context, node_id, {.type_id = str_type_id, .elements_id = elements_id});
|
||||
context, node_id,
|
||||
{.type_id = str_type.type_id, .elements_id = elements_id});
|
||||
}
|
||||
|
||||
auto MakeStringTypeLiteral(Context& context, Parse::NodeId node_id)
|
||||
auto MakeStringTypeLiteral(Context& context, SemIR::LocId loc_id)
|
||||
-> SemIR::InstId {
|
||||
return LookupNameInCore(context, node_id, "String");
|
||||
return LookupNameInCore(context, loc_id, "String");
|
||||
}
|
||||
|
||||
auto MakeStringType(Context& context, Parse::NodeId node_id) -> SemIR::TypeId {
|
||||
auto type_inst_id = MakeStringTypeLiteral(context, node_id);
|
||||
return ExprAsType(context, node_id, type_inst_id).type_id;
|
||||
auto MakeStringType(Context& context, SemIR::LocId loc_id) -> TypeExpr {
|
||||
auto type_inst_id = MakeStringTypeLiteral(context, loc_id);
|
||||
return ExprAsType(context, loc_id, type_inst_id);
|
||||
}
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "toolchain/base/value_ids.h"
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/convert.h"
|
||||
#include "toolchain/lex/token_info.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
|
||||
@@ -35,11 +36,11 @@ auto MakeStringLiteral(Context& context, Parse::StringLiteralId node_id,
|
||||
StringLiteralValueId value_id) -> SemIR::InstId;
|
||||
|
||||
// Forms a string literal type expression for a `str` literal.
|
||||
auto MakeStringTypeLiteral(Context& context, Parse::NodeId node_id)
|
||||
auto MakeStringTypeLiteral(Context& context, SemIR::LocId loc_id)
|
||||
-> SemIR::InstId;
|
||||
|
||||
// Forms a string type.
|
||||
auto MakeStringType(Context& context, Parse::NodeId node_id) -> SemIR::TypeId;
|
||||
auto MakeStringType(Context& context, SemIR::LocId) -> TypeExpr;
|
||||
|
||||
} // namespace Carbon::Check
|
||||
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
// 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/primitives.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/stdlib/string_view.carbon
|
||||
// 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
|
||||
|
||||
// --- string_view.h
|
||||
|
||||
namespace std {
|
||||
using size_t = __SIZE_TYPE__;
|
||||
|
||||
inline namespace __1 {
|
||||
template<typename T> struct char_traits {};
|
||||
|
||||
template<typename CharT, typename Traits = char_traits<CharT>>
|
||||
class basic_string_view {
|
||||
public:
|
||||
basic_string_view() = default;
|
||||
size_t size() const { return size_; }
|
||||
|
||||
private:
|
||||
const CharT* data_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
using string_view = basic_string_view<char>;
|
||||
}
|
||||
}
|
||||
|
||||
auto Consume(std::string_view sv) -> void;
|
||||
auto Produce() -> std::string_view;
|
||||
|
||||
// --- import_multiple.carbon
|
||||
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
import Cpp library "string_view.h";
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn F() {
|
||||
Cpp.Consume("hello");
|
||||
}
|
||||
//@dump-sem-ir-end
|
||||
|
||||
//@dump-sem-ir-begin
|
||||
fn G() -> str {
|
||||
return Cpp.Produce();
|
||||
}
|
||||
//@dump-sem-ir-end
|
||||
|
||||
// CHECK:STDOUT: --- import_multiple.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
// CHECK:STDOUT: %F.type: type = fn_type @F [concrete]
|
||||
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
|
||||
// CHECK:STDOUT: %F: %F.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %String: type = class_type @String [concrete]
|
||||
// CHECK:STDOUT: %int_64: Core.IntLiteral = int_value 64 [concrete]
|
||||
// CHECK:STDOUT: %u64: type = class_type @UInt, @UInt(%int_64) [concrete]
|
||||
// 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: %pattern_type.461: type = pattern_type %String [concrete]
|
||||
// CHECK:STDOUT: %Consume.type: type = fn_type @Consume [concrete]
|
||||
// CHECK:STDOUT: %Consume: %Consume.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %ptr.85f: type = ptr_type %String [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: %str: %ptr.3e8 = string_literal "hello" [concrete]
|
||||
// CHECK:STDOUT: %int_5: %u64 = int_value 5 [concrete]
|
||||
// CHECK:STDOUT: %String.val: %String = struct_value (%str, %int_5) [concrete]
|
||||
// CHECK:STDOUT: %G.type: type = fn_type @G [concrete]
|
||||
// CHECK:STDOUT: %G: %G.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: %Produce.type: type = fn_type @Produce [concrete]
|
||||
// CHECK:STDOUT: %Produce: %Produce.type = struct_value () [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: %String.as.Destroy.impl.Op.type: type = fn_type @String.as.Destroy.impl.Op [concrete]
|
||||
// CHECK:STDOUT: %String.as.Destroy.impl.Op: %String.as.Destroy.impl.Op.type = struct_value () [concrete]
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: imports {
|
||||
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
|
||||
// CHECK:STDOUT: .Consume = %Consume.decl
|
||||
// CHECK:STDOUT: .Produce = %Produce.decl
|
||||
// CHECK:STDOUT: import Cpp//...
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: %Consume.decl: %Consume.type = fn_decl @Consume [concrete = constants.%Consume] {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// 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: %Produce.decl: %Produce.type = fn_decl @Produce [concrete = constants.%Produce] {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// 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: } {
|
||||
// CHECK:STDOUT: <elided>
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: file {
|
||||
// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {}
|
||||
// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] {
|
||||
// CHECK:STDOUT: %return.patt: %pattern_type.461 = return_slot_pattern [concrete]
|
||||
// CHECK:STDOUT: %return.param_patt: %pattern_type.461 = out_param_pattern %return.patt, call_param0 [concrete]
|
||||
// CHECK:STDOUT: } {
|
||||
// CHECK:STDOUT: %return.param: ref %String = out_param call_param0
|
||||
// CHECK:STDOUT: %return: ref %String = return_slot %return.param
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: fn @F() {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %Consume.ref: %Consume.type = name_ref Consume, imports.%Consume.decl [concrete = constants.%Consume]
|
||||
// CHECK:STDOUT: %str: %ptr.3e8 = string_literal "hello" [concrete = constants.%str]
|
||||
// CHECK:STDOUT: %int_5: %u64 = int_value 5 [concrete = constants.%int_5]
|
||||
// CHECK:STDOUT: %String.val: %String = struct_value (%str, %int_5) [concrete = constants.%String.val]
|
||||
// CHECK:STDOUT: %.loc8: ref %String = 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:
|
||||
// CHECK:STDOUT: fn @G() -> %return.param: %String {
|
||||
// CHECK:STDOUT: !entry:
|
||||
// CHECK:STDOUT: %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
|
||||
// CHECK:STDOUT: %Produce.ref: %Produce.type = name_ref Produce, imports.%Produce.decl [concrete = constants.%Produce]
|
||||
// CHECK:STDOUT: %.loc13: ref %String = splice_block %return {}
|
||||
// CHECK:STDOUT: %addr.loc14: %ptr.85f = addr_of %.loc13
|
||||
// CHECK:STDOUT: %Produce__carbon_thunk.call: init %empty_tuple.type = call imports.%Produce__carbon_thunk.decl(%addr.loc14)
|
||||
// CHECK:STDOUT: %.loc14: init %String = in_place_init %Produce__carbon_thunk.call, %.loc13
|
||||
// CHECK:STDOUT: %String.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc13, constants.%String.as.Destroy.impl.Op
|
||||
// CHECK:STDOUT: %addr.loc13: %ptr.85f = addr_of %.loc13
|
||||
// CHECK:STDOUT: %String.as.Destroy.impl.Op.call: init %empty_tuple.type = call %String.as.Destroy.impl.Op.bound(%addr.loc13)
|
||||
// CHECK:STDOUT: return %.loc14 to %return
|
||||
// CHECK:STDOUT: }
|
||||
// CHECK:STDOUT:
|
||||
Reference in New Issue
Block a user