From 40d2d8f68ca9ce27abecc427c90c65d17fe3d5ef Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Fri, 12 Jun 2026 19:04:50 -0400 Subject: [PATCH] Revert "Fix crash from accessing a Check::Context during lowering (#7335)" (#7353) This reverts commit afd679129de895fda74b990910b592c2cd9dc636. The commit introduced ASAN errors: https://github.com/carbon-language/carbon-lang/actions/runs/27436787960/job/81100665405 --- MODULE.bazel | 1 - ...uctor-and-GetSources-method-to-Multi.patch | 60 ------- toolchain/check/BUILD | 1 - toolchain/check/cpp/export.cpp | 76 ++++++++- toolchain/check/cpp/export.h | 7 + toolchain/check/cpp/generate_ast.cpp | 119 ++++++++++---- toolchain/lower/BUILD | 2 - toolchain/lower/context.cpp | 19 --- .../testdata/interop/cpp/issue7142.carbon | 146 ------------------ toolchain/sem_ir/BUILD | 10 -- toolchain/sem_ir/class.cpp | 75 --------- toolchain/sem_ir/class.h | 26 ---- toolchain/sem_ir/read_only_ast_source.cpp | 91 ----------- toolchain/sem_ir/read_only_ast_source.h | 31 ---- 14 files changed, 169 insertions(+), 495 deletions(-) delete mode 100644 bazel/llvm_project/0011-Add-empty-constructor-and-GetSources-method-to-Multi.patch delete mode 100644 toolchain/lower/testdata/interop/cpp/issue7142.carbon delete mode 100644 toolchain/sem_ir/read_only_ast_source.cpp delete mode 100644 toolchain/sem_ir/read_only_ast_source.h diff --git a/MODULE.bazel b/MODULE.bazel index fdfb0dbf2776..b5ef04c47d30 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -93,7 +93,6 @@ git_override( "//bazel/llvm_project:0004_Introduce_basic_sources_exporting_for_libunwind.patch", "//bazel/llvm_project:0005_Introduce_basic_sources_exporting_for_libcxx_and_libcxxabi.patch", "//bazel/llvm_project:0009_Introduce_starlark_exporting_compiler-rt_build_information.patch", - "//bazel/llvm_project:0011-Add-empty-constructor-and-GetSources-method-to-Multi.patch", ], remote = "https://github.com/llvm/llvm-project.git", ) diff --git a/bazel/llvm_project/0011-Add-empty-constructor-and-GetSources-method-to-Multi.patch b/bazel/llvm_project/0011-Add-empty-constructor-and-GetSources-method-to-Multi.patch deleted file mode 100644 index e98a01b355a4..000000000000 --- a/bazel/llvm_project/0011-Add-empty-constructor-and-GetSources-method-to-Multi.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 2c7bb43e8800ef29d815f37422a2e56ee0e724b1 Mon Sep 17 00:00:00 2001 -From: Nicholas Bishop -Date: Wed, 10 Jun 2026 20:19:57 -0400 -Subject: [PATCH] Add empty constructor and GetSources method to - MultiplexExternalSemaSource - ---- - .../include/clang/Sema/MultiplexExternalSemaSource.h | 11 ++++++++++- - clang/lib/Sema/MultiplexExternalSemaSource.cpp | 3 +++ - 2 files changed, 13 insertions(+), 1 deletion(-) - -diff --git a/clang/include/clang/Sema/MultiplexExternalSemaSource.h b/clang/include/clang/Sema/MultiplexExternalSemaSource.h -index 12015724b39f..9230d843e923 100644 ---- a/clang/include/clang/Sema/MultiplexExternalSemaSource.h -+++ b/clang/include/clang/Sema/MultiplexExternalSemaSource.h -@@ -39,10 +39,16 @@ class MultiplexExternalSemaSource : public ExternalSemaSource { - /// LLVM-style RTTI. - static char ID; - -+public: -+ using SourceVector = SmallVector, 2>; -+ - private: -- SmallVector, 2> Sources; -+ SourceVector Sources; - - public: -+ /// Constructs an empty multiplexing external sema source. -+ MultiplexExternalSemaSource(); -+ - /// Constructs a new multiplexing external sema source and appends the - /// given element to it. - /// -@@ -58,6 +64,9 @@ public: - /// - void AddSource(llvm::IntrusiveRefCntPtr Source); - -+ /// Returns a reference to the sources vector. -+ SourceVector& GetSources() { return Sources; } -+ - //===--------------------------------------------------------------------===// - // ExternalASTSource. - //===--------------------------------------------------------------------===// -diff --git a/clang/lib/Sema/MultiplexExternalSemaSource.cpp b/clang/lib/Sema/MultiplexExternalSemaSource.cpp -index be9582ce501f..4306143025c8 100644 ---- a/clang/lib/Sema/MultiplexExternalSemaSource.cpp -+++ b/clang/lib/Sema/MultiplexExternalSemaSource.cpp -@@ -16,6 +16,9 @@ using namespace clang; - - char MultiplexExternalSemaSource::ID; - -+/// Constructs an empty multiplexing external sema source. -+MultiplexExternalSemaSource::MultiplexExternalSemaSource() {} -+ - /// Constructs a new multiplexing external sema source and appends the - /// given element to it. - /// --- -2.54.0.1136.gdb2ca164c4-goog - diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index da2c44385a62..e4e16ed4b2ff 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -188,7 +188,6 @@ cc_library( "//toolchain/sem_ir:file", "//toolchain/sem_ir:formatter", "//toolchain/sem_ir:mangler", - "//toolchain/sem_ir:read_only_ast_source", "//toolchain/sem_ir:typed_insts", "@llvm-project//clang:ast", "@llvm-project//clang:basic", diff --git a/toolchain/check/cpp/export.cpp b/toolchain/check/cpp/export.cpp index 7d34eeaa453f..533548e62e56 100644 --- a/toolchain/check/cpp/export.cpp +++ b/toolchain/check/cpp/export.cpp @@ -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 { + 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(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> { + 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(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& 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 { diff --git a/toolchain/check/cpp/export.h b/toolchain/check/cpp/export.h index 1311d7d2c7e7..0e6e95b9b8a2 100644 --- a/toolchain/check/cpp/export.h +++ b/toolchain/check/cpp/export.h @@ -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& 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*; diff --git a/toolchain/check/cpp/generate_ast.cpp b/toolchain/check/cpp/generate_ast.cpp index 2975bb1b29c9..5737d203ffa9 100644 --- a/toolchain/check/cpp/generate_ast.cpp +++ b/toolchain/check/cpp/generate_ast.cpp @@ -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> { + // 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(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(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(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& base_offsets, llvm::DenseMap& 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(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(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(); - auto* multiplex_source = cast( - multiplex_source_ref_cnt_ptr.get()); + llvm::IntrusiveRefCntPtr carbon_source = + llvm::makeIntrusiveRefCnt(&context); if (auto* existing_source = llvm::cast_or_null( ast.getExternalSource())) { - multiplex_source->AddSource(existing_source); + auto multiplex_source = + llvm::makeIntrusiveRefCnt( + existing_source, std::move(carbon_source)); + ast.setExternalSource(std::move(multiplex_source)); + } else { + ast.setExternalSource(std::move(carbon_source)); } - multiplex_source->AddSource( - llvm::makeIntrusiveRefCnt(&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. diff --git a/toolchain/lower/BUILD b/toolchain/lower/BUILD index 4a0084059841..e4537d4763c4 100644 --- a/toolchain/lower/BUILD +++ b/toolchain/lower/BUILD @@ -81,14 +81,12 @@ cc_library( "//toolchain/sem_ir:file", "//toolchain/sem_ir:inst_namer", "//toolchain/sem_ir:mangler", - "//toolchain/sem_ir:read_only_ast_source", "//toolchain/sem_ir:stringify", "//toolchain/sem_ir:typed_insts", "@llvm-project//clang:ast", "@llvm-project//clang:basic", "@llvm-project//clang:codegen", "@llvm-project//clang:lex", - "@llvm-project//clang:sema", "@llvm-project//llvm:Core", "@llvm-project//llvm:Linker", "@llvm-project//llvm:Passes", diff --git a/toolchain/lower/context.cpp b/toolchain/lower/context.cpp index a8fd65003147..bc805a0b5ab5 100644 --- a/toolchain/lower/context.cpp +++ b/toolchain/lower/context.cpp @@ -5,7 +5,6 @@ #include "toolchain/lower/context.h" #include "clang/Basic/SourceManager.h" -#include "clang/Sema/MultiplexExternalSemaSource.h" #include "common/check.h" #include "common/growing_range.h" #include "common/raw_string_ostream.h" @@ -13,7 +12,6 @@ #include "llvm/Transforms/Utils/ModuleUtils.h" #include "toolchain/lower/file_context.h" #include "toolchain/sem_ir/inst_namer.h" -#include "toolchain/sem_ir/read_only_ast_source.h" namespace Carbon::Lower { @@ -72,23 +70,6 @@ auto Context::Finalize() && -> std::unique_ptr { for (auto& file_context : file_contexts_.values()) { if (file_context) { - if (file_context->cpp_file()) { - // Remove the `CarbonExternalASTSource` installed during check - // (always the last child of the multiplex source) and replace - // it with a `ReadOnlyASTSource`. This is necessary because the - // original source has a now-invalid pointer to a - // `Check::Context`. - auto& ast = const_cast( - file_context->cpp_file()->ast_context()); - auto* multiplex_source = - cast(ast.getExternalSource()); - auto& child_sources = multiplex_source->GetSources(); - child_sources.pop_back(); - multiplex_source->AddSource( - llvm::makeIntrusiveRefCnt( - file_context->sem_ir())); - } - file_context->Finalize(); } } diff --git a/toolchain/lower/testdata/interop/cpp/issue7142.carbon b/toolchain/lower/testdata/interop/cpp/issue7142.carbon deleted file mode 100644 index e8b40eeb8cf5..000000000000 --- a/toolchain/lower/testdata/interop/cpp/issue7142.carbon +++ /dev/null @@ -1,146 +0,0 @@ -// 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/full.carbon -// -// AUTOUPDATE -// TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/interop/cpp/issue7142.carbon -// TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/interop/cpp/issue7142.carbon - -import Cpp; - -class A { var price: i32; }; -fn Get() -> A*; - -inline Cpp ''' -struct S { - static int Vend() { - return Carbon::Get()->price; - } -}; - -void f() { - S::Vend(); -} -'''; - -// CHECK:STDOUT: ; ModuleID = 'issue7142.carbon' -// CHECK:STDOUT: source_filename = "issue7142.carbon" -// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" -// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu" -// CHECK:STDOUT: -// CHECK:STDOUT: %"class.Carbon::A" = type { i32 } -// CHECK:STDOUT: -// CHECK:STDOUT: $_ZN1S4VendEv = comdat any -// CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: mustprogress uwtable -// CHECK:STDOUT: define dso_local void @_Z1fv() #0 { -// CHECK:STDOUT: entry: -// CHECK:STDOUT: %call = call noundef i32 @_ZN1S4VendEv() -// CHECK:STDOUT: ret void -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: mustprogress uwtable -// CHECK:STDOUT: define linkonce_odr dso_local noundef i32 @_ZN1S4VendEv() #0 comdat align 2 { -// CHECK:STDOUT: entry: -// CHECK:STDOUT: %call = call noundef ptr @_ZN6CarbonL3GetEv() -// CHECK:STDOUT: %price = getelementptr inbounds nuw %"class.Carbon::A", ptr %call, i32 0, i32 0 -// CHECK:STDOUT: %0 = load i32, ptr %price, align 4, !tbaa !11 -// CHECK:STDOUT: ret i32 %0 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: declare ptr @_CGet.Main() -// CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define void @_CGet__carbon_thunk.Main(ptr %_) #1 !dbg !13 { -// CHECK:STDOUT: entry: -// CHECK:STDOUT: %Get.call = call ptr @_CGet.Main(), !dbg !19 -// CHECK:STDOUT: store ptr %Get.call, ptr %_, align 8, !dbg !19 -// CHECK:STDOUT: ret void, !dbg !19 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind -// CHECK:STDOUT: define void @"_C__destroy_thunk:thunk.A.Main"(ptr %self) #2 !dbg !20 { -// CHECK:STDOUT: entry: -// CHECK:STDOUT: call void @"_COp.68011419f123494e:core.Destroy.Core"(ptr %self), !dbg !23 -// CHECK:STDOUT: ret void, !dbg !23 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define weak_odr void @"_COp.5e27612b9dd31a14:core.Destroy.Core"(ptr %self) #1 !dbg !24 { -// CHECK:STDOUT: entry: -// CHECK:STDOUT: ret void, !dbg !30 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define weak_odr void @"_COp.dd803598fd1f0f08:core.Destroy.Core"(ptr %self) #1 !dbg !31 { -// CHECK:STDOUT: entry: -// CHECK:STDOUT: ret void, !dbg !34 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define weak_odr void @"_COp.68011419f123494e:core.Destroy.Core"(ptr %self) #1 !dbg !35 { -// CHECK:STDOUT: entry: -// CHECK:STDOUT: ret void, !dbg !38 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable -// CHECK:STDOUT: define internal noundef ptr @_ZN6CarbonL3GetEv() #3 { -// CHECK:STDOUT: entry: -// CHECK:STDOUT: %retval = alloca ptr, align 8 -// CHECK:STDOUT: call void @_CGet__carbon_thunk.Main(ptr noundef nonnull align 8 dereferenceable(8) %retval) -// CHECK:STDOUT: %0 = load ptr, ptr %retval, align 8 -// CHECK:STDOUT: ret ptr %0 -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: attributes #0 = { mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } -// CHECK:STDOUT: attributes #1 = { nounwind } -// CHECK:STDOUT: attributes #2 = { alwaysinline nounwind } -// CHECK:STDOUT: attributes #3 = { alwaysinline mustprogress nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } -// CHECK:STDOUT: -// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} -// CHECK:STDOUT: !llvm.dbg.cu = !{!5} -// CHECK:STDOUT: !llvm.errno.tbaa = !{!7} -// CHECK:STDOUT: -// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} -// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} -// CHECK:STDOUT: !2 = !{i32 8, !"PIC Level", i32 2} -// CHECK:STDOUT: !3 = !{i32 7, !"PIE Level", i32 2} -// CHECK:STDOUT: !4 = !{i32 7, !"uwtable", i32 2} -// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) -// CHECK:STDOUT: !6 = !DIFile(filename: "issue7142.carbon", directory: "") -// CHECK:STDOUT: !7 = !{!8, !8, i64 0} -// CHECK:STDOUT: !8 = !{!"int", !9, i64 0} -// CHECK:STDOUT: !9 = !{!"omnipotent char", !10, i64 0} -// CHECK:STDOUT: !10 = !{!"Simple C++ TBAA"} -// CHECK:STDOUT: !11 = !{!12, !8, i64 0} -// CHECK:STDOUT: !12 = !{!"_ZTSN6Carbon1AE", !8, i64 0} -// CHECK:STDOUT: !13 = distinct !DISubprogram(name: "Get__carbon_thunk", linkageName: "_CGet__carbon_thunk.Main", scope: null, file: !6, line: 16, type: !14, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !17) -// CHECK:STDOUT: !14 = !DISubroutineType(types: !15) -// CHECK:STDOUT: !15 = !{null, !16} -// CHECK:STDOUT: !16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) -// CHECK:STDOUT: !17 = !{!18} -// CHECK:STDOUT: !18 = !DILocalVariable(arg: 1, scope: !13, type: !16) -// CHECK:STDOUT: !19 = !DILocation(line: 16, column: 1, scope: !13) -// CHECK:STDOUT: !20 = distinct !DISubprogram(name: "__destroy_thunk", linkageName: "_C__destroy_thunk:thunk.A.Main", scope: null, file: !6, line: 15, type: !14, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !21) -// CHECK:STDOUT: !21 = !{!22} -// CHECK:STDOUT: !22 = !DILocalVariable(arg: 1, scope: !20, type: !16) -// CHECK:STDOUT: !23 = !DILocation(line: 15, column: 1, scope: !20) -// CHECK:STDOUT: !24 = distinct !DISubprogram(name: "Op", linkageName: "_COp.5e27612b9dd31a14:core.Destroy.Core", scope: null, file: !6, line: 15, type: !25, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !28) -// CHECK:STDOUT: !25 = !DISubroutineType(types: !26) -// CHECK:STDOUT: !26 = !{null, !27} -// CHECK:STDOUT: !27 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) -// CHECK:STDOUT: !28 = !{!29} -// CHECK:STDOUT: !29 = !DILocalVariable(arg: 1, scope: !24, type: !27) -// CHECK:STDOUT: !30 = !DILocation(line: 15, column: 1, scope: !24) -// CHECK:STDOUT: !31 = distinct !DISubprogram(name: "Op", linkageName: "_COp.dd803598fd1f0f08:core.Destroy.Core", scope: null, file: !6, line: 15, type: !14, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !32) -// CHECK:STDOUT: !32 = !{!33} -// CHECK:STDOUT: !33 = !DILocalVariable(arg: 1, scope: !31, type: !16) -// CHECK:STDOUT: !34 = !DILocation(line: 15, column: 1, scope: !31) -// CHECK:STDOUT: !35 = distinct !DISubprogram(name: "Op", linkageName: "_COp.68011419f123494e:core.Destroy.Core", scope: null, file: !6, line: 15, type: !14, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !36) -// CHECK:STDOUT: !36 = !{!37} -// CHECK:STDOUT: !37 = !DILocalVariable(arg: 1, scope: !35, type: !16) -// CHECK:STDOUT: !38 = !DILocation(line: 15, column: 1, scope: !35) diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index aba7c5053ddc..378bdceee0d4 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -382,13 +382,3 @@ cc_library( "@llvm-project//llvm:Support", ], ) - -cc_library( - name = "read_only_ast_source", - srcs = ["read_only_ast_source.cpp"], - hdrs = ["read_only_ast_source.h"], - deps = [ - "//toolchain/sem_ir:file", - "@llvm-project//clang:sema", - ], -) diff --git a/toolchain/sem_ir/class.cpp b/toolchain/sem_ir/class.cpp index 0eb072dc9ffa..dfa0dc66bfd5 100644 --- a/toolchain/sem_ir/class.cpp +++ b/toolchain/sem_ir/class.cpp @@ -4,7 +4,6 @@ #include "toolchain/sem_ir/class.h" -#include "clang/AST/Decl.h" #include "toolchain/base/value_store_impl.h" #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/generic.h" @@ -52,80 +51,6 @@ auto Class::GetObjectRepr(const File& file, SpecificId specific_id) const .object_repr_type_inst_id); } -auto Class::GetStructTypeFields(const File& sem_ir) const - -> llvm::ArrayRef { - if (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 = GetObjectRepr(sem_ir, SemIR::SpecificId::None); - if (object_repr_type_id == SemIR::ErrorInst::TypeId) { - return {}; - } - auto struct_type = - sem_ir.types().GetAs(object_repr_type_id); - return sem_ir.struct_type_fields().Get(struct_type.fields_id); -} - -auto GetAsCarbonOwnedClass(const File& sem_ir, const clang::TagDecl* tag_decl) - -> std::optional> { - // 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(tag_decl->getFirstDecl())); - auto clang_decl_id = sem_ir.clang_decls().LookupId(key); - if (!clang_decl_id.has_value()) { - return std::nullopt; - } - - auto inst_id = sem_ir.clang_decls().Get(clang_decl_id).inst_id; - auto const_id = sem_ir.constant_values().Get(inst_id); - if (!const_id.has_value()) { - return std::nullopt; - } - - auto class_type = - sem_ir.constant_values().TryGetInstAs(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 = sem_ir.classes().Get(class_type->class_id); - if (class_info.parent_scope_id.has_value() && - sem_ir.name_scopes().Get(class_info.parent_scope_id).is_cpp_scope()) { - return std::nullopt; - } - - auto class_type_id = sem_ir.types().GetTypeIdForTypeConstantId(const_id); - return std::make_pair(class_type_id, *class_type); -} - -auto LookupClassFieldByStructField(const File& sem_ir, - const SemIR::NameScope& class_scope, - const SemIR::StructTypeField& struct_field) - -> std::optional> { - 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 sem_ir.insts().TryGetAsWithId(field_inst_id); - } - return std::nullopt; -} - } // namespace Carbon::SemIR namespace Carbon { diff --git a/toolchain/sem_ir/class.h b/toolchain/sem_ir/class.h index 476d884b861c..1c6b5b7fdb3e 100644 --- a/toolchain/sem_ir/class.h +++ b/toolchain/sem_ir/class.h @@ -5,24 +5,13 @@ #ifndef CARBON_TOOLCHAIN_SEM_IR_CLASS_H_ #define CARBON_TOOLCHAIN_SEM_IR_CLASS_H_ -#include - #include "common/map.h" #include "toolchain/base/value_store.h" #include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/ids.h" -#include "toolchain/sem_ir/inst.h" -#include "toolchain/sem_ir/struct_type_field.h" - -namespace clang { -class TagDecl; -} namespace Carbon::SemIR { -class File; -class NameScope; - // Class-specific fields. struct ClassFields { enum InheritanceKind : int8_t { @@ -140,25 +129,10 @@ struct Class : public EntityWithParamsBase, // Gets the object representation for this class. Returns `None` if the class // is not yet defined. auto GetObjectRepr(const File& file, SpecificId specific_id) const -> TypeId; - - // Get the `StructTypeField`s from a class's object repr. - auto GetStructTypeFields(const File& sem_ir) const - -> llvm::ArrayRef; }; using ClassStore = ValueStore>; -// 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`. -auto GetAsCarbonOwnedClass(const File& sem_ir, const clang::TagDecl* tag_decl) - -> std::optional>; - -auto LookupClassFieldByStructField(const File& sem_ir, - const NameScope& class_scope, - const StructTypeField& struct_field) - -> std::optional>; - } // namespace Carbon::SemIR namespace Carbon { diff --git a/toolchain/sem_ir/read_only_ast_source.cpp b/toolchain/sem_ir/read_only_ast_source.cpp deleted file mode 100644 index 72f7dc1b544a..000000000000 --- a/toolchain/sem_ir/read_only_ast_source.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// 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/sem_ir/read_only_ast_source.h" - -namespace Carbon::SemIR { - -// Get the field offset for each field in a class. -// -// Returns true on success, false if any error occurs. -static auto CalculateCppFieldOffsets( - const File& sem_ir, SemIR::ClassId class_id, - llvm::DenseMap& field_offsets) -> bool { - auto class_info = sem_ir.classes().Get(class_id); - const auto& class_scope = sem_ir.name_scopes().Get(class_info.scope_id); - - auto class_layout = SemIR::ObjectLayout::Empty(); - for (const auto& struct_field : class_info.GetStructTypeFields(sem_ir)) { - auto field_type_id = - sem_ir.types().GetTypeIdForTypeInstId(struct_field.type_inst_id); - auto field_layout = - 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(sem_ir, class_scope, struct_field); - if (class_field) { - const auto* clang_decl = - sem_ir.clang_decls().Lookup(class_field->inst_id); - if (!clang_decl) { - return false; - } - - auto* cpp_field_decl = cast(clang_decl->decl()); - field_offsets.insert( - {cpp_field_decl, class_layout.FieldOffset(field_layout).bits()}); - } - - class_layout.AppendField(field_layout); - } - - return true; -} - -auto ReadOnlyASTSource::layoutRecordType( - const clang::RecordDecl* record_decl, uint64_t& size, uint64_t& alignment, - llvm::DenseMap& field_offsets, - llvm::DenseMap& base_offsets, - llvm::DenseMap& - vbase_offsets) -> bool { - auto carbon_class_info = GetAsCarbonOwnedClass(sem_ir_, record_decl); - if (!carbon_class_info) { - return false; - } - auto& [class_type_id, class_type] = *carbon_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 = - sem_ir_.types().GetCompleteTypeInfo(class_type_id).object_layout; - size = layout.size.bytes() * 8; - alignment = layout.alignment.bits(); - - // Fill in `field_offsets`. - CalculateCppFieldOffsets(sem_ir_, class_type.class_id, field_offsets); - - // Add offset for base class, if any. - if (const auto* class_decl = dyn_cast(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(vbase_offsets); - } - - return true; -} - -} // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/read_only_ast_source.h b/toolchain/sem_ir/read_only_ast_source.h deleted file mode 100644 index 571746719a7b..000000000000 --- a/toolchain/sem_ir/read_only_ast_source.h +++ /dev/null @@ -1,31 +0,0 @@ -// 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_SEM_IR_READ_ONLY_AST_SOURCE_H_ -#define CARBON_TOOLCHAIN_SEM_IR_READ_ONLY_AST_SOURCE_H_ - -#include "clang/Sema/ExternalSemaSource.h" -#include "toolchain/sem_ir/file.h" - -namespace Carbon::SemIR { - -class ReadOnlyASTSource : public clang::ExternalSemaSource { - public: - explicit ReadOnlyASTSource(const File& sem_ir) : sem_ir_(sem_ir) {} - - auto layoutRecordType( - const clang::RecordDecl* record_decl, uint64_t& size, uint64_t& alignment, - llvm::DenseMap& field_offsets, - llvm::DenseMap& - base_offsets, - llvm::DenseMap& - vbase_offsets) -> bool override; - - private: - const File& sem_ir_; -}; - -} // namespace Carbon::SemIR - -#endif // CARBON_TOOLCHAIN_SEM_IR_READ_ONLY_AST_SOURCE_H_