EricWF updated this revision to Diff 133365.
EricWF added a comment.

@rsmith Is this the direction you were thinking?

I haven't updated the tests yet, but I wanted to make the other changes visible 
earlier.


https://reviews.llvm.org/D43047

Files:
  include/clang/Basic/Builtins.def
  include/clang/Sema/Sema.h
  lib/CodeGen/CGBuiltin.cpp
  lib/CodeGen/CGExprCXX.cpp
  lib/CodeGen/CodeGenFunction.h
  lib/Lex/PPMacroExpansion.cpp
  lib/Sema/SemaChecking.cpp
  lib/Sema/SemaExprCXX.cpp
  test/CodeGenCXX/new.cpp
  test/Preprocessor/builtin_operator_new_delete.cpp
  test/SemaCXX/builtin-operator-new-delete-alignment-disabled.cpp
  test/SemaCXX/builtin-operator-new-delete.cpp

Index: test/SemaCXX/builtin-operator-new-delete.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/builtin-operator-new-delete.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -std=c++1z -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++03 -faligned-allocation -fsyntax-only -verify %s
+
+void *NP = 0;
+
+void test_signature() {
+  __builtin_operator_new();           // expected-error {{too few arguments to function call, expected 1, have 0}}
+  __builtin_operator_new(0, 0, 0);    // expected-error {{too many arguments to function call, expected 2, have 3}}
+  __builtin_operator_delete();        // expected-error {{too few arguments to function call, expected 1, have 0}}
+  __builtin_operator_delete(0, 0, 0); // expected-error {{too many arguments to function call, expected 2, have 3}}
+}
+
+void test_typo_in_args() {
+  __builtin_operator_new(DNE);          // expected-error {{undeclared identifier 'DNE'}}
+  __builtin_operator_new(DNE, DNE2);    // expected-error {{undeclared identifier 'DNE'}} expected-error {{'DNE2'}}
+  __builtin_operator_delete(DNE);       // expected-error {{'DNE'}}
+  __builtin_operator_delete(DNE, DNE2); // expected-error {{'DNE'}} expected-error {{'DNE2'}}
+}
+
+void test_arg_types() {
+  __builtin_operator_new(NP);    // expected-error {{cannot initialize a parameter of type 'unsigned long' with an lvalue of type 'void *'}}
+  __builtin_operator_new(NP, 0); // expected-error {{cannot initialize a parameter of type 'unsigned long' with an lvalue of type 'void *'}}
+}
+
+void test_return_type() {
+  int w = __builtin_operator_new(42);        // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'void *'}}
+  int x = __builtin_operator_new(42, 42);    // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'void *'}}
+  int y = __builtin_operator_delete(NP);     // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'void'}}
+  int z = __builtin_operator_delete(NP, 42); // expected-error {{cannot initialize a variable of type 'int' with an rvalue of type 'void'}}
+}
Index: test/SemaCXX/builtin-operator-new-delete-alignment-disabled.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/builtin-operator-new-delete-alignment-disabled.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -std=c++03 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++03 -faligned-allocation -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++1z -fno-aligned-allocation -fsyntax-only -verify %s
+
+void test_disabled() {
+#ifdef __cpp_aligned_new
+  // expected-no-diagnostics
+#else
+  // expected-error@+3 {{__builtin_operator_new with alignment requires aligned allocation support; use -faligned-allocation to enable}}
+  // expected-error@+3 {{__builtin_operator_delete with alignment requires aligned allocation support; use -faligned-allocation to enable}}
+#endif
+  (void)__builtin_operator_new(1, 2);
+  __builtin_operator_delete((void *)0, 2);
+}
Index: test/Preprocessor/builtin_operator_new_delete.cpp
===================================================================
--- /dev/null
+++ test/Preprocessor/builtin_operator_new_delete.cpp
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++98 %s -fsyntax-only -verify
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++11 %s -fsyntax-only -verify
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 %s -fsyntax-only -verify
+
+// expected-no-diagnostics
+
+#if __has_builtin(__builtin_operator_new) != __has_builtin(__builtin_operator_delete)
+#error builtins should report same value
+#endif
+
+#if !__has_builtin(__builtin_operator_new) || !__has_builtin(__builtin_operator_delete)
+#error builtins should always be available
+#endif
+
+#if __has_builtin(__builtin_operator_new) != 201802L || \
+    __has_builtin(__builtin_operator_delete) != 201802L
+#error builtin should report updated value
+#endif
+
Index: test/CodeGenCXX/new.cpp
===================================================================
--- test/CodeGenCXX/new.cpp
+++ test/CodeGenCXX/new.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown %s -verify -faligned-allocation \
+// RUN:    -emit-llvm -o - | FileCheck %s
 
 typedef __typeof__(sizeof(0)) size_t;
 
@@ -363,6 +364,12 @@
     // CHECK: call void @_ZdlPv({{.*}}) [[ATTR_BUILTIN_DELETE]]
     __builtin_operator_delete(__builtin_operator_new(4));
   }
+  // CHECK-LABEL: define void @_ZN8builtins1gEv
+  void g() {
+    // CHECK: call i8* @_ZnwmSt11align_val_t(i64 4, i64 4) [[ATTR_BUILTIN_NEW]]
+    // CHECK: call void @_ZdlPvSt11align_val_t({{.*}}, i64 4) [[ATTR_BUILTIN_DELETE]]
+    __builtin_operator_delete(__builtin_operator_new(4, 4), 4);
+  }
 }
 
 // CHECK-DAG: attributes [[ATTR_NOBUILTIN]] = {{[{].*}} nobuiltin {{.*[}]}}
Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -1442,7 +1442,7 @@
           CUDAPref = S.IdentifyCUDAPreference(Caller, FD);
     }
 
-    operator bool() const { return FD; }
+    explicit operator bool() const { return FD; }
 
     bool isBetterThan(const UsualDeallocFnInfo &Other, bool WantSize,
                       bool WantAlign) const {
@@ -2266,7 +2266,6 @@
   llvm_unreachable("Unreachable, bad result from BestViableFunction");
 }
 
-
 /// FindAllocationFunctions - Finds the overloads of operator new and delete
 /// that are appropriate for the allocation.
 bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range,
@@ -3336,6 +3335,142 @@
   return Result;
 }
 
+static bool resolveBuiltinNewDeleteOverload(Sema &S, LookupResult &R,
+                                            SourceRange Range,
+                                            SmallVectorImpl<Expr *> &Args,
+                                            FunctionDecl *&Operator) {
+  OverloadCandidateSet Candidates(R.getNameLoc(),
+                                  OverloadCandidateSet::CSK_Normal);
+  for (LookupResult::iterator FnOvl = R.begin(), FnOvlEnd = R.end();
+       FnOvl != FnOvlEnd; ++FnOvl) {
+    // Even member operator new/delete are implicitly treated as
+    // static, so don't use AddMemberCandidate.
+    NamedDecl *D = (*FnOvl)->getUnderlyingDecl();
+
+    if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) {
+      S.AddTemplateOverloadCandidate(FnTemplate, FnOvl.getPair(),
+                                     /*ExplicitTemplateArgs=*/nullptr, Args,
+                                     Candidates,
+                                     /*SuppressUserConversions=*/false);
+      continue;
+    }
+
+    FunctionDecl *Fn = cast<FunctionDecl>(D);
+    S.AddOverloadCandidate(Fn, FnOvl.getPair(), Args, Candidates,
+                           /*SuppressUserConversions=*/false);
+  }
+
+  // Do the resolution.
+  OverloadCandidateSet::iterator Best;
+  switch (Candidates.BestViableFunction(S, R.getNameLoc(), Best)) {
+  case OR_Success: {
+    // Got one!
+    FunctionDecl *FnDecl = Best->Function;
+    if (S.CheckAllocationAccess(R.getNameLoc(), Range, R.getNamingClass(),
+                                Best->FoundDecl) == Sema::AR_inaccessible)
+      return true;
+
+    if (!FnDecl->isReplaceableGlobalAllocationFunction()) {
+      S.Diag(R.getNameLoc(), diag::err_ovl_no_viable_function_in_call)
+          << R.getLookupName() << Range;
+      return true;
+    }
+
+    Operator = FnDecl;
+    return false;
+  }
+
+  case OR_No_Viable_Function:
+    S.Diag(R.getNameLoc(), diag::err_ovl_no_viable_function_in_call)
+        << R.getLookupName() << Range;
+    Candidates.NoteCandidates(S, OCD_AllCandidates, Args);
+    return true;
+
+  case OR_Ambiguous:
+    S.Diag(R.getNameLoc(), diag::err_ovl_ambiguous_call)
+        << R.getLookupName() << Range;
+    Candidates.NoteCandidates(S, OCD_ViableCandidates, Args);
+    return true;
+
+  case OR_Deleted: {
+    S.Diag(R.getNameLoc(), diag::err_ovl_deleted_call)
+        << Best->Function->isDeleted() << R.getLookupName()
+        << S.getDeletedOrUnavailableSuffix(Best->Function) << Range;
+    Candidates.NoteCandidates(S, OCD_AllCandidates, Args);
+    return true;
+  }
+  }
+  llvm_unreachable("Unreachable, bad result from BestViableFunction");
+}
+
+ExprResult
+Sema::SemaBuiltinOperatorNewDeleteOverloaded(ExprResult TheCallResult,
+                                             bool IsDelete) {
+  CallExpr *TheCall = cast<CallExpr>(TheCallResult.get());
+  if (!getLangOpts().CPlusPlus) {
+    Diag(TheCall->getExprLoc(), diag::err_builtin_requires_language)
+        << (IsDelete ? "__builtin_operator_delete" : "__builtin_operator_new")
+        << "C++";
+    return ExprError();
+  }
+  // CodeGen assumes it can find the global new and delete to call,
+  // so ensure that they are declared.
+  DeclareGlobalNewDelete();
+
+  TheCall->setType(IsDelete ? Context.VoidTy : Context.VoidPtrTy);
+
+  if (TheCall->getNumArgs() < 1) {
+    Diag(TheCall->getLocEnd(), diag::err_typecheck_call_too_few_args)
+        << 0 << 1 << TheCall->getNumArgs()
+        << TheCall->getCallee()->getSourceRange();
+    return ExprError();
+  } else if (TheCall->getNumArgs() > 2) {
+    Diag(TheCall->getArg(2)->getLocStart(),
+         diag::err_typecheck_call_too_many_args)
+        << 0 << 2 << TheCall->getNumArgs()
+        << TheCall->getCallee()->getSourceRange();
+    return ExprError();
+  }
+
+  llvm::SmallVector<QualType, 2> ArgTypes;
+  ArgTypes.push_back(IsDelete ? Context.VoidPtrTy : Context.getSizeType());
+
+
+  DeclarationName NewName = Context.DeclarationNames.getCXXOperatorName(
+      IsDelete ? OO_Delete : OO_New);
+
+  FunctionDecl *OperatorNewOrDelete = nullptr;
+  {
+    LookupResult R(*this, NewName, TheCall->getLocStart(), LookupOrdinaryName);
+    LookupQualifiedName(R, Context.getTranslationUnitDecl());
+    assert(!R.empty() && "implicitly declared allocation functions not found");
+    assert(!R.isAmbiguous() && "global allocation functions are ambiguous");
+
+    // We do our own custom access checks below.
+    R.suppressDiagnostics();
+
+    SmallVector<Expr *, 8> Args(TheCall->arg_begin(), TheCall->arg_end());
+    if (resolveBuiltinNewDeleteOverload(*this, R, TheCall->getSourceRange(),
+                                        Args, OperatorNewOrDelete))
+      return ExprError();
+    assert(OperatorNewOrDelete && "should be found");
+
+    TheCall->getCallee()->setType(OperatorNewOrDelete->getType());
+    for (unsigned i = 0; i != TheCall->getNumArgs(); ++i) {
+      QualType ParamTy = OperatorNewOrDelete->getParamDecl(i)->getType();
+      InitializedEntity Entity =
+          InitializedEntity::InitializeParameter(Context, ParamTy, false);
+      ExprResult Arg = PerformCopyInitialization(Entity, SourceLocation(),
+                                                 TheCall->getArg(i));
+      if (Arg.isInvalid())
+        return ExprError();
+      TheCall->setArg(i, Arg.get());
+    }
+  }
+
+  return TheCallResult;
+}
+
 void Sema::CheckVirtualDtorCall(CXXDestructorDecl *dtor, SourceLocation Loc,
                                 bool IsDelete, bool CallCanBeVirtual,
                                 bool WarnOnNonAbstractTypes,
Index: lib/Sema/SemaChecking.cpp
===================================================================
--- lib/Sema/SemaChecking.cpp
+++ lib/Sema/SemaChecking.cpp
@@ -1097,20 +1097,14 @@
       return ExprError();
     break;
   case Builtin::BI__builtin_operator_new:
-  case Builtin::BI__builtin_operator_delete:
-    if (!getLangOpts().CPlusPlus) {
-      Diag(TheCall->getExprLoc(), diag::err_builtin_requires_language)
-        << (BuiltinID == Builtin::BI__builtin_operator_new
-                ? "__builtin_operator_new"
-                : "__builtin_operator_delete")
-        << "C++";
-      return ExprError();
-    }
-    // CodeGen assumes it can find the global new and delete to call,
-    // so ensure that they are declared.
-    DeclareGlobalNewDelete();
-    break;
-
+  case Builtin::BI__builtin_operator_delete: {
+    bool IsDelete = BuiltinID == Builtin::BI__builtin_operator_delete;
+    ExprResult Res =
+        SemaBuiltinOperatorNewDeleteOverloaded(TheCallResult, IsDelete);
+    if (Res.isInvalid())
+      CorrectDelayedTyposInExpr(TheCallResult.get());
+    return Res;
+  }
   // check secure string manipulation functions where overflows
   // are detectable at compile time
   case Builtin::BI__builtin___memcpy_chk:
Index: lib/Lex/PPMacroExpansion.cpp
===================================================================
--- lib/Lex/PPMacroExpansion.cpp
+++ lib/Lex/PPMacroExpansion.cpp
@@ -1800,12 +1800,20 @@
       [this](Token &Tok, bool &HasLexedNextToken) -> int {
         IdentifierInfo *II = ExpectFeatureIdentifierInfo(Tok, *this,
                                            diag::err_feature_check_malformed);
+        const LangOptions &LangOpts = getLangOpts();
         if (!II)
           return false;
-        else if (II->getBuiltinID() != 0)
+        else if (II->getBuiltinID() != 0) {
+          switch (II->getBuiltinID()) {
+          case Builtin::BI__builtin_operator_new:
+          case Builtin::BI__builtin_operator_delete: {
+            return 201802;
+          }
+          default:
+            return true;
+          }
           return true;
-        else {
-          const LangOptions &LangOpts = getLangOpts();
+        } else {
           return llvm::StringSwitch<bool>(II->getName())
                       .Case("__make_integer_seq", LangOpts.CPlusPlus)
                       .Case("__type_pack_element", LangOpts.CPlusPlus)
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -2346,7 +2346,7 @@
                       CharUnits CookieSize = CharUnits());
 
   RValue EmitBuiltinNewDeleteCall(const FunctionProtoType *Type,
-                                  const Expr *Arg, bool IsDelete);
+                                  const CallExpr *TheCallExpr, bool IsDelete);
 
   llvm::Value *EmitCXXTypeidExpr(const CXXTypeidExpr *E);
   llvm::Value *EmitDynamicCast(Address V, const CXXDynamicCastExpr *DCE);
Index: lib/CodeGen/CGExprCXX.cpp
===================================================================
--- lib/CodeGen/CGExprCXX.cpp
+++ lib/CodeGen/CGExprCXX.cpp
@@ -1307,19 +1307,19 @@
 }
 
 RValue CodeGenFunction::EmitBuiltinNewDeleteCall(const FunctionProtoType *Type,
-                                                 const Expr *Arg,
+                                                 const CallExpr *TheCall,
                                                  bool IsDelete) {
   CallArgList Args;
-  const Stmt *ArgS = Arg;
-  EmitCallArgs(Args, *Type->param_type_begin(), llvm::makeArrayRef(ArgS));
+  EmitCallArgs(Args, Type->getParamTypes(), TheCall->arguments());
   // Find the allocation or deallocation function that we're calling.
   ASTContext &Ctx = getContext();
   DeclarationName Name = Ctx.DeclarationNames
       .getCXXOperatorName(IsDelete ? OO_Delete : OO_New);
+
   for (auto *Decl : Ctx.getTranslationUnitDecl()->lookup(Name))
     if (auto *FD = dyn_cast<FunctionDecl>(Decl))
       if (Ctx.hasSameType(FD->getType(), QualType(Type, 0)))
-        return EmitNewDeleteCall(*this, cast<FunctionDecl>(Decl), Type, Args);
+        return EmitNewDeleteCall(*this, FD, Type, Args);
   llvm_unreachable("predeclared global operator new/delete is missing");
 }
 
Index: lib/CodeGen/CGBuiltin.cpp
===================================================================
--- lib/CodeGen/CGBuiltin.cpp
+++ lib/CodeGen/CGBuiltin.cpp
@@ -2611,11 +2611,12 @@
   case Builtin::BI__builtin_addressof:
     return RValue::get(EmitLValue(E->getArg(0)).getPointer());
   case Builtin::BI__builtin_operator_new:
-    return EmitBuiltinNewDeleteCall(FD->getType()->castAs<FunctionProtoType>(),
-                                    E->getArg(0), false);
+    return EmitBuiltinNewDeleteCall(
+        E->getCallee()->getType()->castAs<FunctionProtoType>(), E, false);
   case Builtin::BI__builtin_operator_delete:
-    return EmitBuiltinNewDeleteCall(FD->getType()->castAs<FunctionProtoType>(),
-                                    E->getArg(0), true);
+    return EmitBuiltinNewDeleteCall(
+        E->getCallee()->getType()->castAs<FunctionProtoType>(), E, true);
+
   case Builtin::BI__noop:
     // __noop always evaluates to an integer literal zero.
     return RValue::get(ConstantInt::get(IntTy, 0));
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -10363,6 +10363,8 @@
   ExprResult SemaBuiltinNontemporalOverloaded(ExprResult TheCallResult);
   ExprResult SemaAtomicOpsOverloaded(ExprResult TheCallResult,
                                      AtomicExpr::AtomicOp Op);
+  ExprResult SemaBuiltinOperatorNewDeleteOverloaded(ExprResult TheCallResult,
+                                                    bool IsDelete);
   bool SemaBuiltinConstantArg(CallExpr *TheCall, int ArgNum,
                               llvm::APSInt &Result);
   bool SemaBuiltinConstantArgRange(CallExpr *TheCall, int ArgNum,
Index: include/clang/Basic/Builtins.def
===================================================================
--- include/clang/Basic/Builtins.def
+++ include/clang/Basic/Builtins.def
@@ -1371,8 +1371,9 @@
 
 // Clang builtins (not available in GCC).
 BUILTIN(__builtin_addressof, "v*v&", "nct")
-BUILTIN(__builtin_operator_new, "v*z", "c")
-BUILTIN(__builtin_operator_delete, "vv*", "n")
+BUILTIN(__builtin_operator_new, "v*z", "tc")
+BUILTIN(__builtin_operator_delete, "vv*", "tn")
+
 BUILTIN(__builtin_char_memchr, "c*cC*iz", "n")
 
 // Safestack builtins
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to