mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 21:41:27 +01:00
This reverts commit afd679129d.
The commit introduced ASAN errors:
https://github.com/carbon-language/carbon-lang/actions/runs/27436787960/job/81100665405
This commit is contained in:
@@ -177,6 +177,39 @@ auto ExportClassToCpp(Context& context, SemIR::LocId loc_id,
|
||||
return record_decl;
|
||||
}
|
||||
|
||||
// Get the `StructTypeField`s from a class's object repr.
|
||||
static auto GetStructTypeFields(Context& context,
|
||||
const SemIR::Class& class_info)
|
||||
-> llvm::ArrayRef<SemIR::StructTypeField> {
|
||||
if (class_info.adapt_id.has_value()) {
|
||||
// The representation of an adapter won't necessarily be a
|
||||
// struct. Return an empty array since adapters can't declare
|
||||
// fields.
|
||||
return {};
|
||||
}
|
||||
|
||||
auto object_repr_type_id =
|
||||
class_info.GetObjectRepr(context.sem_ir(), SemIR::SpecificId::None);
|
||||
if (object_repr_type_id == SemIR::ErrorInst::TypeId) {
|
||||
return {};
|
||||
}
|
||||
auto struct_type =
|
||||
context.types().GetAs<SemIR::StructType>(object_repr_type_id);
|
||||
return context.struct_type_fields().Get(struct_type.fields_id);
|
||||
}
|
||||
|
||||
static auto LookupClassFieldByStructField(
|
||||
const Context& context, const SemIR::NameScope& class_scope,
|
||||
const SemIR::StructTypeField& struct_field)
|
||||
-> std::optional<SemIR::InstStore::GetAsWithIdResult<SemIR::FieldDecl>> {
|
||||
if (auto entry_id = class_scope.Lookup(struct_field.name_id)) {
|
||||
auto field_inst_id =
|
||||
class_scope.GetEntry(*entry_id).result.target_inst_id();
|
||||
return context.insts().TryGetAsWithId<SemIR::FieldDecl>(field_inst_id);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static auto SetCppClassMemberAccess(const SemIR::NameScope& class_scope,
|
||||
SemIR::NameId member_name_id,
|
||||
clang::Decl* member) -> void {
|
||||
@@ -232,10 +265,9 @@ auto ExportAllFieldsToCpp(Context& context, SemIR::Class& class_info) -> void {
|
||||
|
||||
const auto& class_scope = context.name_scopes().Get(class_info.scope_id);
|
||||
|
||||
for (const auto& struct_field :
|
||||
class_info.GetStructTypeFields(context.sem_ir())) {
|
||||
auto class_field = LookupClassFieldByStructField(context.sem_ir(),
|
||||
class_scope, struct_field);
|
||||
for (const auto& struct_field : GetStructTypeFields(context, class_info)) {
|
||||
auto class_field =
|
||||
LookupClassFieldByStructField(context, class_scope, struct_field);
|
||||
if (!class_field) {
|
||||
continue;
|
||||
}
|
||||
@@ -282,6 +314,42 @@ auto ExportFieldToCpp(Context& context, SemIR::InstId field_inst_id,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto CalculateCppFieldOffsets(
|
||||
Context& context, SemIR::ClassId class_id,
|
||||
llvm::DenseMap<const clang::FieldDecl*, uint64_t>& field_offsets) -> bool {
|
||||
auto class_info = context.classes().Get(class_id);
|
||||
const auto& class_scope = context.name_scopes().Get(class_info.scope_id);
|
||||
|
||||
auto class_layout = SemIR::ObjectLayout::Empty();
|
||||
for (const auto& struct_field : GetStructTypeFields(context, class_info)) {
|
||||
auto field_type_id = context.sem_ir().types().GetTypeIdForTypeInstId(
|
||||
struct_field.type_inst_id);
|
||||
auto field_layout = context.sem_ir()
|
||||
.types()
|
||||
.GetCompleteTypeInfo(field_type_id)
|
||||
.object_layout;
|
||||
|
||||
// Use the field's name to look up the corresponding entry in the
|
||||
// class. If it's a `FieldDecl`, write out the offset of the
|
||||
// corresponding `clang::FieldDecl`.
|
||||
auto class_field =
|
||||
LookupClassFieldByStructField(context, class_scope, struct_field);
|
||||
if (class_field) {
|
||||
auto* cpp_field_decl =
|
||||
ExportFieldToCpp(context, class_field->inst_id, class_field->inst);
|
||||
if (!cpp_field_decl) {
|
||||
return false;
|
||||
}
|
||||
field_offsets.insert(
|
||||
{cpp_field_decl, class_layout.FieldOffset(field_layout).bits()});
|
||||
}
|
||||
|
||||
class_layout.AppendField(field_layout);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct FunctionInfo {
|
||||
struct Param {
|
||||
|
||||
@@ -51,6 +51,13 @@ auto ExportAllFieldsToCpp(Context& context, SemIR::Class& class_info) -> void;
|
||||
auto ExportFieldToCpp(Context& context, SemIR::InstId field_inst_id,
|
||||
SemIR::FieldDecl field_decl) -> clang::FieldDecl*;
|
||||
|
||||
// Get the field offset for each field in a class.
|
||||
//
|
||||
// Returns true on success, false if any error occurs.
|
||||
auto CalculateCppFieldOffsets(
|
||||
Context& context, SemIR::ClassId class_id,
|
||||
llvm::DenseMap<const clang::FieldDecl*, uint64_t>& field_offsets) -> bool;
|
||||
|
||||
// Get a `clang::FunctionDecl` that can be used to call a Carbon function.
|
||||
auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::FunctionId function_id) -> clang::FunctionDecl*;
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
#include "toolchain/diagnostics/format_providers.h"
|
||||
#include "toolchain/parse/node_ids.h"
|
||||
#include "toolchain/sem_ir/cpp_file.h"
|
||||
#include "toolchain/sem_ir/read_only_ast_source.h"
|
||||
#include "toolchain/sem_ir/typed_insts.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
@@ -324,10 +323,9 @@ class ShallowCopyCompilerInvocation : public clang::CompilerInvocation {
|
||||
};
|
||||
|
||||
// Provides clang AST nodes representing Carbon SemIR entities.
|
||||
class CarbonExternalASTSource : public SemIR::ReadOnlyASTSource {
|
||||
class CarbonExternalASTSource : public clang::ExternalSemaSource {
|
||||
public:
|
||||
explicit CarbonExternalASTSource(Context* context)
|
||||
: ReadOnlyASTSource(context->sem_ir()), context_(context) {}
|
||||
explicit CarbonExternalASTSource(Context* context) : context_(context) {}
|
||||
|
||||
auto StartTranslationUnit(clang::ASTConsumer* consumer) -> void override;
|
||||
|
||||
@@ -577,6 +575,54 @@ auto CarbonExternalASTSource::FindExternalVisibleDeclsByName(
|
||||
}
|
||||
}
|
||||
|
||||
// If this declaration declares a class type that is "owned" by Carbon, and not
|
||||
// imported from C++, returns the corresponding type ID and `ClassType`.
|
||||
// Otherwise returns `nullopt`.
|
||||
static auto GetAsCarbonOwnedClass(Context& context,
|
||||
const clang::TagDecl* tag_decl)
|
||||
-> std::optional<std::pair<SemIR::TypeId, SemIR::ClassType>> {
|
||||
// Quickly check whether we could possibly own this class.
|
||||
// TODO: Once we multiplex with the ASTReader, handle
|
||||
// ASTReader::completeVisibleDeclsMap setting this to `false`.
|
||||
if (!tag_decl->hasExternalVisibleStorage()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(
|
||||
const_cast<clang::TagDecl*>(tag_decl->getFirstDecl()));
|
||||
auto clang_decl_id = context.clang_decls().LookupId(key);
|
||||
if (!clang_decl_id.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto inst_id = context.clang_decls().Get(clang_decl_id).inst_id;
|
||||
auto const_id = context.constant_values().Get(inst_id);
|
||||
if (!const_id.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto class_type =
|
||||
context.constant_values().TryGetInstAs<SemIR::ClassType>(const_id);
|
||||
if (!class_type) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Determine whether this class was imported from C++.
|
||||
// TODO: This currently can't happen, because only Carbon classes have
|
||||
// external lexical storage, but will happen once we support importing C++
|
||||
// classes from AST files. Add a test once that is supported.
|
||||
// TODO: Consider setting `extern_library_id` on classes imported from C++ to
|
||||
// indicate the current file does not own them.
|
||||
const auto& class_info = context.classes().Get(class_type->class_id);
|
||||
if (class_info.parent_scope_id.has_value() &&
|
||||
context.name_scopes().Get(class_info.parent_scope_id).is_cpp_scope()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto class_type_id = context.types().GetTypeIdForTypeConstantId(const_id);
|
||||
return std::make_pair(class_type_id, *class_type);
|
||||
}
|
||||
|
||||
auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void {
|
||||
auto* class_decl = dyn_cast<clang::CXXRecordDecl>(tag_decl);
|
||||
if (!class_decl) {
|
||||
@@ -585,8 +631,7 @@ auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void {
|
||||
return;
|
||||
}
|
||||
|
||||
auto carbon_class_info =
|
||||
SemIR::GetAsCarbonOwnedClass(context_->sem_ir(), tag_decl);
|
||||
auto carbon_class_info = GetAsCarbonOwnedClass(*context_, tag_decl);
|
||||
if (!carbon_class_info) {
|
||||
return;
|
||||
}
|
||||
@@ -691,8 +736,7 @@ auto CarbonExternalASTSource::layoutRecordType(
|
||||
llvm::DenseMap<const clang::CXXRecordDecl*, clang::CharUnits>& base_offsets,
|
||||
llvm::DenseMap<const clang::CXXRecordDecl*, clang::CharUnits>&
|
||||
vbase_offsets) -> bool {
|
||||
auto carbon_class_info =
|
||||
SemIR::GetAsCarbonOwnedClass(context_->sem_ir(), record_decl);
|
||||
auto carbon_class_info = GetAsCarbonOwnedClass(*context_, record_decl);
|
||||
if (!carbon_class_info) {
|
||||
return false;
|
||||
}
|
||||
@@ -704,11 +748,37 @@ auto CarbonExternalASTSource::layoutRecordType(
|
||||
// general.
|
||||
CompleteTypeOrCheckFail(*context_, class_type_id);
|
||||
|
||||
auto& class_info = context_->classes().Get(class_type.class_id);
|
||||
ExportAllFieldsToCpp(*context_, class_info);
|
||||
// Set the overall size and alignment. We round up the size to an integer
|
||||
// number of bytes in order to avoid surprising Clang too much.
|
||||
auto layout = context_->sem_ir()
|
||||
.types()
|
||||
.GetCompleteTypeInfo(class_type_id)
|
||||
.object_layout;
|
||||
size = layout.size.bytes() * 8;
|
||||
alignment = layout.alignment.bits();
|
||||
|
||||
return ReadOnlyASTSource::layoutRecordType(
|
||||
record_decl, size, alignment, field_offsets, base_offsets, vbase_offsets);
|
||||
// Fill in `field_offsets`.
|
||||
CalculateCppFieldOffsets(*context_, class_type.class_id, field_offsets);
|
||||
|
||||
// Add offset for base class, if any.
|
||||
if (const auto* class_decl = dyn_cast<clang::CXXRecordDecl>(record_decl);
|
||||
class_decl && !class_decl->bases().empty()) {
|
||||
CARBON_CHECK(class_decl->getNumBases() == 1,
|
||||
"Carbon class with multiple bases");
|
||||
const auto& base = *class_decl->bases_begin();
|
||||
// TODO: If this class introduced a vptr, the base will be at an offset of
|
||||
// `sizeof(void*)`, not 0.
|
||||
base_offsets.insert(
|
||||
{base.getType()->getAsCXXRecordDecl()->getCanonicalDecl(),
|
||||
clang::CharUnits::Zero()});
|
||||
|
||||
// TODO: Support deriving from a C++ class with virtual bases.
|
||||
CARBON_CHECK(class_decl->getNumVBases() == 0,
|
||||
"Carbon class with multiple bases");
|
||||
static_cast<void>(vbase_offsets);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parses a sequence of top-level declarations and forms a corresponding
|
||||
@@ -875,26 +945,17 @@ auto GenerateAst(Context& context,
|
||||
context.sem_ir().cpp_file()->CreateMangleContext();
|
||||
|
||||
auto& ast = clang_instance.getASTContext();
|
||||
|
||||
// Always build a multiplex source, even if there's only one child
|
||||
// source. During lowering, the `CarbonExternalASTSource` can no longer be
|
||||
// used (because it uses `Check::Context`), so a `ReadOnlyASTSource` is
|
||||
// installed instead. However, clang internally keeps pointers to the
|
||||
// top-level `ExternalASTSource` installed via `setExternalSource`, and
|
||||
// those pointers aren't updated if `setExternalSource` is called again. By
|
||||
// using `MultiplexExternalSemaSource`, we can keep the top-level
|
||||
// `ExternalASTSource` pointer the same, and only update its children.
|
||||
auto multiplex_source_ref_cnt_ptr =
|
||||
llvm::makeIntrusiveRefCnt<clang::MultiplexExternalSemaSource>();
|
||||
auto* multiplex_source = cast<clang::MultiplexExternalSemaSource>(
|
||||
multiplex_source_ref_cnt_ptr.get());
|
||||
llvm::IntrusiveRefCntPtr<clang::ExternalSemaSource> carbon_source =
|
||||
llvm::makeIntrusiveRefCnt<CarbonExternalASTSource>(&context);
|
||||
if (auto* existing_source = llvm::cast_or_null<clang::ExternalSemaSource>(
|
||||
ast.getExternalSource())) {
|
||||
multiplex_source->AddSource(existing_source);
|
||||
auto multiplex_source =
|
||||
llvm::makeIntrusiveRefCnt<clang::MultiplexExternalSemaSource>(
|
||||
existing_source, std::move(carbon_source));
|
||||
ast.setExternalSource(std::move(multiplex_source));
|
||||
} else {
|
||||
ast.setExternalSource(std::move(carbon_source));
|
||||
}
|
||||
multiplex_source->AddSource(
|
||||
llvm::makeIntrusiveRefCnt<CarbonExternalASTSource>(&context));
|
||||
ast.setExternalSource(std::move(multiplex_source_ref_cnt_ptr));
|
||||
|
||||
if (llvm::Error error = action.Execute()) {
|
||||
// `Execute` currently never fails, but its contract allows it to.
|
||||
|
||||
Reference in New Issue
Block a user