Fix crash from accessing a Check::Context during lowering (#7335)

In generate_ast.cpp, an `CarbonExternalASTSource` is installed that has
a `Check::Context` pointer. During lowering, this `ExternalASTSource` is
still installed, and using it can cause a crash if the now-invalid
pointer is dereferenced.

Fix by adding a new `ReadOnlyASTSource` in sem_ir, and using that during
lowering.

`CarbonExternalASTSource` now inherits from `ReadOnlyASTSource` to avoid
some code duplication.

In generate_ast.cpp, we now always install a multiplex source, even if
there's only one child source. 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.

Using `MultiplexExternalSemaSource` this way requires a new constructor
and a method to modify its child sources; added a new LLVM patch adding
those.

https://github.com/carbon-language/carbon-lang/issues/7142
This commit is contained in:
Nicholas Bishop
2026-06-12 18:05:19 +00:00
committed by GitHub
parent f3b8e231ca
commit afd679129d
14 changed files with 495 additions and 169 deletions
+1
View File
@@ -93,6 +93,7 @@ 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",
)
@@ -0,0 +1,60 @@
From 2c7bb43e8800ef29d815f37422a2e56ee0e724b1 Mon Sep 17 00:00:00 2001
From: Nicholas Bishop <nicholsabishop@google.com>
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<llvm::IntrusiveRefCntPtr<ExternalSemaSource>, 2>;
+
private:
- SmallVector<llvm::IntrusiveRefCntPtr<ExternalSemaSource>, 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<ExternalSemaSource> 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
+1
View File
@@ -188,6 +188,7 @@ 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",
+4 -72
View File
@@ -177,39 +177,6 @@ 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 {
@@ -265,9 +232,10 @@ 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 : GetStructTypeFields(context, class_info)) {
auto class_field =
LookupClassFieldByStructField(context, class_scope, struct_field);
for (const auto& struct_field :
class_info.GetStructTypeFields(context.sem_ir())) {
auto class_field = LookupClassFieldByStructField(context.sem_ir(),
class_scope, struct_field);
if (!class_field) {
continue;
}
@@ -314,42 +282,6 @@ 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 {
-7
View File
@@ -51,13 +51,6 @@ 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*;
+29 -90
View File
@@ -41,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/read_only_ast_source.h"
#include "toolchain/sem_ir/typed_insts.h"
namespace Carbon::Check {
@@ -323,9 +324,10 @@ class ShallowCopyCompilerInvocation : public clang::CompilerInvocation {
};
// Provides clang AST nodes representing Carbon SemIR entities.
class CarbonExternalASTSource : public clang::ExternalSemaSource {
class CarbonExternalASTSource : public SemIR::ReadOnlyASTSource {
public:
explicit CarbonExternalASTSource(Context* context) : context_(context) {}
explicit CarbonExternalASTSource(Context* context)
: ReadOnlyASTSource(context->sem_ir()), context_(context) {}
auto StartTranslationUnit(clang::ASTConsumer* consumer) -> void override;
@@ -575,54 +577,6 @@ 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) {
@@ -631,7 +585,8 @@ auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void {
return;
}
auto carbon_class_info = GetAsCarbonOwnedClass(*context_, tag_decl);
auto carbon_class_info =
SemIR::GetAsCarbonOwnedClass(context_->sem_ir(), tag_decl);
if (!carbon_class_info) {
return;
}
@@ -736,7 +691,8 @@ 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 = GetAsCarbonOwnedClass(*context_, record_decl);
auto carbon_class_info =
SemIR::GetAsCarbonOwnedClass(context_->sem_ir(), record_decl);
if (!carbon_class_info) {
return false;
}
@@ -748,37 +704,11 @@ auto CarbonExternalASTSource::layoutRecordType(
// general.
CompleteTypeOrCheckFail(*context_, class_type_id);
// 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();
auto& class_info = context_->classes().Get(class_type.class_id);
ExportAllFieldsToCpp(*context_, class_info);
// 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;
return ReadOnlyASTSource::layoutRecordType(
record_decl, size, alignment, field_offsets, base_offsets, vbase_offsets);
}
// Parses a sequence of top-level declarations and forms a corresponding
@@ -945,17 +875,26 @@ auto GenerateAst(Context& context,
context.sem_ir().cpp_file()->CreateMangleContext();
auto& ast = clang_instance.getASTContext();
llvm::IntrusiveRefCntPtr<clang::ExternalSemaSource> carbon_source =
llvm::makeIntrusiveRefCnt<CarbonExternalASTSource>(&context);
// 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());
if (auto* existing_source = llvm::cast_or_null<clang::ExternalSemaSource>(
ast.getExternalSource())) {
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(existing_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.
+2
View File
@@ -81,12 +81,14 @@ 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",
+19
View File
@@ -5,6 +5,7 @@
#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"
@@ -12,6 +13,7 @@
#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 {
@@ -70,6 +72,23 @@ auto Context::Finalize() && -> std::unique_ptr<llvm::Module> {
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<clang::ASTContext&>(
file_context->cpp_file()->ast_context());
auto* multiplex_source =
cast<clang::MultiplexExternalSemaSource>(ast.getExternalSource());
auto& child_sources = multiplex_source->GetSources();
child_sources.pop_back();
multiplex_source->AddSource(
llvm::makeIntrusiveRefCnt<SemIR::ReadOnlyASTSource>(
file_context->sem_ir()));
}
file_context->Finalize();
}
}
+146
View File
@@ -0,0 +1,146 @@
// 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)
+10
View File
@@ -382,3 +382,13 @@ 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",
],
)
+75
View File
@@ -4,6 +4,7 @@
#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"
@@ -51,6 +52,80 @@ 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<SemIR::StructTypeField> {
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<SemIR::StructType>(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<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 = 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<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 = 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<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 sem_ir.insts().TryGetAsWithId<SemIR::FieldDecl>(field_inst_id);
}
return std::nullopt;
}
} // namespace Carbon::SemIR
namespace Carbon {
+26
View File
@@ -5,13 +5,24 @@
#ifndef CARBON_TOOLCHAIN_SEM_IR_CLASS_H_
#define CARBON_TOOLCHAIN_SEM_IR_CLASS_H_
#include <optional>
#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 {
@@ -129,10 +140,25 @@ 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<SemIR::StructTypeField>;
};
using ClassStore = ValueStore<ClassId, Class, Tag<CheckIRId>>;
// 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<std::pair<SemIR::TypeId, SemIR::ClassType>>;
auto LookupClassFieldByStructField(const File& sem_ir,
const NameScope& class_scope,
const StructTypeField& struct_field)
-> std::optional<InstStore::GetAsWithIdResult<SemIR::FieldDecl>>;
} // namespace Carbon::SemIR
namespace Carbon {
+91
View File
@@ -0,0 +1,91 @@
// 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<const clang::FieldDecl*, uint64_t>& 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::FieldDecl>(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<const clang::FieldDecl*, uint64_t>& field_offsets,
llvm::DenseMap<const clang::CXXRecordDecl*, clang::CharUnits>& base_offsets,
llvm::DenseMap<const clang::CXXRecordDecl*, clang::CharUnits>&
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<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;
}
} // namespace Carbon::SemIR
+31
View File
@@ -0,0 +1,31 @@
// 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<const clang::FieldDecl*, uint64_t>& field_offsets,
llvm::DenseMap<const clang::CXXRecordDecl*, clang::CharUnits>&
base_offsets,
llvm::DenseMap<const clang::CXXRecordDecl*, clang::CharUnits>&
vbase_offsets) -> bool override;
private:
const File& sem_ir_;
};
} // namespace Carbon::SemIR
#endif // CARBON_TOOLCHAIN_SEM_IR_READ_ONLY_AST_SOURCE_H_