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

Create a placement `operator new` directly. (#6941)

Instead of injecting code to declare an `operator new`, generate AST for
it directly. In order to use this, directly generate a `CXXNewExpr`
rather than asking Clang to build one.

This is less of a hack, and doesn't visibly leak an `operator new`
declaration that inline C++ code or template instantiations might see.
It also avoids generating a warning in C++26 and later that the
`constexpr` declaration of `operator new` is used but not defined.

Assisted-by: Gemini 3.1 Pro via Antigravity
Richard Smith 1 месяц назад
Родитель
Сommit
e06eb8f532

+ 10 - 0
toolchain/check/cpp/context.h

@@ -37,6 +37,13 @@ class CppContext {
     return carbon_file_locations_;
     return carbon_file_locations_;
   }
   }
 
 
+  auto placement_new_decl() const -> clang::FunctionDecl* {
+    return placement_new_decl_;
+  }
+  void set_placement_new_decl(clang::FunctionDecl* decl) {
+    placement_new_decl_ = decl;
+  }
+
  private:
  private:
   // The Clang AST context.
   // The Clang AST context.
   clang::ASTContext* ast_context_;
   clang::ASTContext* ast_context_;
@@ -53,6 +60,9 @@ class CppContext {
 
 
   // The Clang mangle context for the target in the ASTContext.
   // The Clang mangle context for the target in the ASTContext.
   std::unique_ptr<clang::MangleContext> clang_mangle_context_;
   std::unique_ptr<clang::MangleContext> clang_mangle_context_;
+
+  // The cached placement new function declaration.
+  clang::FunctionDecl* placement_new_decl_ = nullptr;
 };
 };
 
 
 }  // namespace Carbon::Check
 }  // namespace Carbon::Check

+ 0 - 28
toolchain/check/cpp/generate_ast.cpp

@@ -86,34 +86,6 @@ static auto GenerateCppIncludesHeaderCode(
       }
       }
     }
     }
   }
   }
-
-  // Inject a declaration of placement operator new, because the code we
-  // generate in thunks depends on it for placement new expressions. Clang has
-  // special-case logic for lowering a new-expression using this, so a
-  // definition is not required.
-  // TODO: This is a hack. We should be able to directly generate Clang AST to
-  // construct objects in-place without this.
-  // TODO: Once we can rely on libc++ being available, consider including
-  // `<__new/placement_new_delete.h>` instead.
-  code_stream << R"(# 1 "<carbon-internal>"
-#undef constexpr
-#if __cplusplus > 202302L
-constexpr
-#endif
-#undef void
-#undef operator
-#undef new
-void* operator new(__SIZE_TYPE__, void*)
-#if __cplusplus < 201103L
-#undef throw
-throw()
-#else
-#undef noexcept
-noexcept
-#endif
-;
-)";
-
   return code;
   return code;
 }
 }
 
 

+ 56 - 7
toolchain/check/cpp/thunk.cpp

@@ -14,6 +14,7 @@
 #include "toolchain/check/context.h"
 #include "toolchain/check/context.h"
 #include "toolchain/check/control_flow.h"
 #include "toolchain/check/control_flow.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/convert.h"
+#include "toolchain/check/cpp/context.h"
 #include "toolchain/check/literal.h"
 #include "toolchain/check/literal.h"
 #include "toolchain/check/type.h"
 #include "toolchain/check/type.h"
 #include "toolchain/check/type_completion.h"
 #include "toolchain/check/type_completion.h"
@@ -23,6 +24,39 @@
 
 
 namespace Carbon::Check {
 namespace Carbon::Check {
 
 
+// Generate and return a function:
+// `void* operator new(__SIZE_TYPE__, void*) noexcept`.
+static auto GeneratePlacementNewFunctionDecl(clang::ASTContext& context)
+    -> clang::FunctionDecl* {
+  clang::QualType size_type = context.getSizeType();
+  clang::QualType void_ptr_type = context.VoidPtrTy;
+
+  auto ext_info = clang::FunctionProtoType::ExtProtoInfo();
+  ext_info.ExceptionSpec.Type = clang::EST_BasicNoexcept;
+
+  clang::QualType function_type = context.getFunctionType(
+      void_ptr_type, {size_type, void_ptr_type}, ext_info);
+
+  clang::DeclarationName name =
+      context.DeclarationNames.getCXXOperatorName(clang::OO_New);
+
+  clang::FunctionDecl* function_decl = clang::FunctionDecl::Create(
+      context, context.getTranslationUnitDecl(), clang::SourceLocation(),
+      clang::SourceLocation(), name, function_type,
+      /*TInfo=*/nullptr, clang::SC_None);
+
+  clang::ParmVarDecl* size_param = clang::ParmVarDecl::Create(
+      context, function_decl, clang::SourceLocation(), clang::SourceLocation(),
+      nullptr, size_type, nullptr, clang::SC_None, nullptr);
+  clang::ParmVarDecl* ptr_param = clang::ParmVarDecl::Create(
+      context, function_decl, clang::SourceLocation(), clang::SourceLocation(),
+      nullptr, void_ptr_type, nullptr, clang::SC_None, nullptr);
+
+  function_decl->setParams({size_param, ptr_param});
+  CARBON_CHECK(function_decl->isReservedGlobalPlacementOperator());
+  return function_decl;
+}
+
 // Returns the GlobalDecl to use to represent the given function declaration.
 // Returns the GlobalDecl to use to represent the given function declaration.
 // TODO: Refactor with `Lower::CreateGlobalDecl`.
 // TODO: Refactor with `Lower::CreateGlobalDecl`.
 static auto GetGlobalDecl(const clang::FunctionDecl* decl)
 static auto GetGlobalDecl(const clang::FunctionDecl* decl)
@@ -487,7 +521,7 @@ static auto BuildCalleeArgs(clang::Sema& sema,
 // Builds the thunk function body which calls the callee function using the call
 // Builds the thunk function body which calls the callee function using the call
 // args and returns the callee function return value. Returns nullptr on
 // args and returns the callee function return value. Returns nullptr on
 // failure.
 // failure.
-static auto BuildThunkBody(clang::Sema& sema,
+static auto BuildThunkBody(CppContext& cpp_context, clang::Sema& sema,
                            clang::FunctionDecl* thunk_function_decl,
                            clang::FunctionDecl* thunk_function_decl,
                            CalleeFunctionInfo callee_info)
                            CalleeFunctionInfo callee_info)
     -> clang::StmtResult {
     -> clang::StmtResult {
@@ -566,10 +600,25 @@ static auto BuildThunkBody(clang::Sema& sema,
   auto return_type = callee_info.effective_return_type.getNonReferenceType();
   auto return_type = callee_info.effective_return_type.getNonReferenceType();
   auto* return_type_info =
   auto* return_type_info =
       sema.Context.getTrivialTypeSourceInfo(return_type, clang_loc);
       sema.Context.getTrivialTypeSourceInfo(return_type, clang_loc);
-  auto placement_new = sema.BuildCXXNew(
-      clang_loc, /*UseGlobal=*/true, clang_loc, {return_object_addr}, clang_loc,
-      /*TypeIdParens=*/clang::SourceRange(), return_type, return_type_info,
-      /*ArraySize=*/std::nullopt, clang_loc, call.get());
+
+  auto* placement_new_decl = cpp_context.placement_new_decl();
+  if (!placement_new_decl) {
+    placement_new_decl = GeneratePlacementNewFunctionDecl(sema.getASTContext());
+    cpp_context.set_placement_new_decl(placement_new_decl);
+  }
+  sema.MarkFunctionReferenced(clang_loc, placement_new_decl);
+  clang::ImplicitAllocationParameters params(return_type,
+                                             clang::TypeAwareAllocationMode::No,
+                                             clang::AlignedAllocationMode::No);
+  clang::SourceRange range(clang_loc, clang_loc);
+  auto* placement_new = clang::CXXNewExpr::Create(
+      sema.getASTContext(), /*IsGlobalNew*/ true, placement_new_decl,
+      /*OperatorDelete*/ nullptr, params, /*UsualArrayDeleteWantsSize*/ false,
+      {return_object_addr},
+      /*TypeIdParens=*/clang::SourceRange(), /*ArraySize=*/std::nullopt,
+      clang::CXXNewInitializationStyle::Parens, call.get(),
+      sema.getASTContext().getPointerType(return_type), return_type_info, range,
+      range);
   return sema.ActOnExprStmt(placement_new, /*DiscardedValue=*/true);
   return sema.ActOnExprStmt(placement_new, /*DiscardedValue=*/true);
 }
 }
 
 
@@ -598,8 +647,8 @@ auto BuildCppThunk(Context& context, const SemIR::Function& callee_function)
   clang::Sema& sema = context.clang_sema();
   clang::Sema& sema = context.clang_sema();
   clang::Sema::ContextRAII context_raii(sema, thunk_function_decl);
   clang::Sema::ContextRAII context_raii(sema, thunk_function_decl);
   sema.ActOnStartOfFunctionDef(nullptr, thunk_function_decl);
   sema.ActOnStartOfFunctionDef(nullptr, thunk_function_decl);
-  clang::StmtResult body =
-      BuildThunkBody(sema, thunk_function_decl, callee_info);
+  clang::StmtResult body = BuildThunkBody(*context.cpp_context(), sema,
+                                          thunk_function_decl, callee_info);
   sema.ActOnFinishFunctionBody(thunk_function_decl, body.get());
   sema.ActOnFinishFunctionBody(thunk_function_decl, body.get());
   if (body.isInvalid()) {
   if (body.isInvalid()) {
     return nullptr;
     return nullptr;

+ 26 - 49
toolchain/check/testdata/interop/cpp/function/thunk_ast.carbon

@@ -18,55 +18,7 @@
 auto foo(short a) -> void;
 auto foo(short a) -> void;
 // CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <./thunk_required.h:[[@LINE-1]]:1, col:22> col:6 used foo 'auto (short) -> void'
 // CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <./thunk_required.h:[[@LINE-1]]:1, col:22> col:6 used foo 'auto (short) -> void'
 // CHECK:STDOUT: | `-ParmVarDecl {{0x[a-f0-9]+}} <col:10, col:16> col:16 a 'short'
 // CHECK:STDOUT: | `-ParmVarDecl {{0x[a-f0-9]+}} <col:10, col:16> col:16 a 'short'
-// CHECK:STDOUT: |-NamespaceDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit std
-// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit operator new 'void *(__size_t)'
-// CHECK:STDOUT: | |-VisibilityAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit Default
-// CHECK:STDOUT: | |-ReturnsNonNullAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit
-// CHECK:STDOUT: | `-AllocSizeAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit 1
-// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit operator new 'void *(__size_t, std::align_val_t)'
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit 'std::align_val_t'
-// CHECK:STDOUT: | |-VisibilityAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit Default
-// CHECK:STDOUT: | |-ReturnsNonNullAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit
-// CHECK:STDOUT: | |-AllocSizeAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit 1
-// CHECK:STDOUT: | `-AllocAlignAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit 2
-// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit operator new[] 'void *(__size_t)'
-// CHECK:STDOUT: | |-VisibilityAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit Default
-// CHECK:STDOUT: | |-ReturnsNonNullAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit
-// CHECK:STDOUT: | `-AllocSizeAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit 1
-// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit operator new[] 'void *(__size_t, std::align_val_t)'
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit 'std::align_val_t'
-// CHECK:STDOUT: | |-VisibilityAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit Default
-// CHECK:STDOUT: | |-ReturnsNonNullAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit
-// CHECK:STDOUT: | |-AllocSizeAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit 1
-// CHECK:STDOUT: | `-AllocAlignAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit 2
-// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit operator delete 'void (void *) noexcept'
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit 'void *'
-// CHECK:STDOUT: | `-VisibilityAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit Default
-// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit operator delete 'void (void *, std::align_val_t) noexcept'
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit 'void *'
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit 'std::align_val_t'
-// CHECK:STDOUT: | `-VisibilityAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit Default
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit 'void *'
-// CHECK:STDOUT: | `-VisibilityAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit Default
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit 'void *'
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit 'std::align_val_t'
-// CHECK:STDOUT: | `-VisibilityAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit Default
-// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit operator delete[] 'void (void *) noexcept'
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit 'void *'
-// CHECK:STDOUT: | `-VisibilityAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit Default
-// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit operator delete[] 'void (void *, std::align_val_t) noexcept'
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit 'void *'
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit 'std::align_val_t'
-// CHECK:STDOUT: | `-VisibilityAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit Default
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit 'void *'
-// CHECK:STDOUT: | `-VisibilityAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit Default
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit 'void *'
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> implicit 'std::align_val_t'
-// CHECK:STDOUT: | `-VisibilityAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit Default
-// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <<carbon-internal>:8:1, line:14:1> line:8:7 operator new 'void *(unsigned long, void *) noexcept'
-// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <<built-in>:176:23, col:37> <carbon-internal>:8:33 'unsigned long'
-// CHECK:STDOUT: | `-ParmVarDecl {{0x[a-f0-9]+}} <col:35, col:39> col:40 'void *'
-// CHECK:STDOUT: `-FunctionDecl {{0x[a-f0-9]+}} <./thunk_required.h:[[@LINE-51]]:6> col:6 foo__carbon_thunk 'void (short * _Nonnull)' extern
+// CHECK:STDOUT: `-FunctionDecl {{0x[a-f0-9]+}} <col:6> col:6 foo__carbon_thunk 'void (short * _Nonnull)' extern
 // CHECK:STDOUT:   |-ParmVarDecl {{0x[a-f0-9]+}} <col:6> col:6 used a 'short * _Nonnull':'short *'
 // CHECK:STDOUT:   |-ParmVarDecl {{0x[a-f0-9]+}} <col:6> col:6 used a 'short * _Nonnull':'short *'
 // CHECK:STDOUT:   |-ReturnStmt {{0x[a-f0-9]+}} <col:6>
 // CHECK:STDOUT:   |-ReturnStmt {{0x[a-f0-9]+}} <col:6>
 // CHECK:STDOUT:   | `-CallExpr {{0x[a-f0-9]+}} <col:6> 'void'
 // CHECK:STDOUT:   | `-CallExpr {{0x[a-f0-9]+}} <col:6> 'void'
@@ -78,6 +30,7 @@ auto foo(short a) -> void;
 // CHECK:STDOUT:   |         `-DeclRefExpr {{0x[a-f0-9]+}} <col:6> 'short * _Nonnull':'short *' lvalue ParmVar {{0x[a-f0-9]+}} 'a' 'short * _Nonnull':'short *'
 // CHECK:STDOUT:   |         `-DeclRefExpr {{0x[a-f0-9]+}} <col:6> 'short * _Nonnull':'short *' lvalue ParmVar {{0x[a-f0-9]+}} 'a' 'short * _Nonnull':'short *'
 // CHECK:STDOUT:   |-AlwaysInlineAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit always_inline
 // CHECK:STDOUT:   |-AlwaysInlineAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit always_inline
 // CHECK:STDOUT:   `-AsmLabelAttr {{0x[a-f0-9]+}} <col:6> Implicit "_Z3foos.carbon_thunk"
 // CHECK:STDOUT:   `-AsmLabelAttr {{0x[a-f0-9]+}} <col:6> Implicit "_Z3foos.carbon_thunk"
+// CHECK:STDOUT: TranslationUnitDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc>
 
 
 // --- import_thunk_required.carbon
 // --- import_thunk_required.carbon
 
 
@@ -88,3 +41,27 @@ import Cpp library "thunk_required.h";
 fn F() {
 fn F() {
   Cpp.foo(1 as i16);
   Cpp.foo(1 as i16);
 }
 }
+
+// --- return_thunk_required.h
+
+auto foo() -> short;
+// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <./return_thunk_required.h:[[@LINE-1]]:1, col:15> col:6 used foo 'auto () -> short'
+// CHECK:STDOUT: `-FunctionDecl {{0x[a-f0-9]+}} <col:6> col:6 foo__carbon_thunk 'void (short * _Nonnull)' extern
+// CHECK:STDOUT:   |-ParmVarDecl {{0x[a-f0-9]+}} <col:6> col:6 used return 'short * _Nonnull':'short *'
+// CHECK:STDOUT:   |-CXXNewExpr {{0x[a-f0-9]+}} <col:6> 'short *' global Function {{0x[a-f0-9]+}} 'operator new' 'void *(__size_t, void *) noexcept'
+// CHECK:STDOUT:   | |-CallExpr {{0x[a-f0-9]+}} <col:6> 'short'
+// CHECK:STDOUT:   | | `-ImplicitCastExpr {{0x[a-f0-9]+}} <col:6> 'auto (*)() -> short' <FunctionToPointerDecay>
+// CHECK:STDOUT:   | |   `-DeclRefExpr {{0x[a-f0-9]+}} <col:6> 'auto () -> short' Function {{0x[a-f0-9]+}} 'foo' 'auto () -> short'
+// CHECK:STDOUT:   | `-DeclRefExpr {{0x[a-f0-9]+}} <col:6> 'short * _Nonnull':'short *' lvalue ParmVar {{0x[a-f0-9]+}} 'return' 'short * _Nonnull':'short *'
+// CHECK:STDOUT:   |-AlwaysInlineAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit always_inline
+// CHECK:STDOUT:   `-AsmLabelAttr {{0x[a-f0-9]+}} <col:6> Implicit "_Z3foov.carbon_thunk"
+
+// --- import_return_thunk_required.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "return_thunk_required.h";
+
+fn F() -> i16 {
+  return Cpp.foo();
+}

+ 28 - 0
toolchain/check/testdata/interop/cpp/function/thunk_cpp26.carbon

@@ -0,0 +1,28 @@
+// 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/int.carbon
+// EXTRA-ARGS: --clang-arg=--std=c++26
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/function/thunk_cpp26.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/thunk_cpp26.carbon
+
+// --- thunk_required.h
+
+auto foo(short a) -> short;
+
+// --- import_thunk_required.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "thunk_required.h";
+
+fn F() {
+  // Placement operator new is `constexpr` in C++26. But that shouldn't cause
+  // problems for us because we declare our own operator new.
+  Cpp.foo(1 as i16);
+}