Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -5140,8 +5140,25 @@ bool CheckAllocatedType(QualType AllocType, SourceLocation Loc, SourceRange R); + + /// \brief The scope in which to find allocation functions. + enum AllocationFunctionScope { + /// \brief Only look for allocation functions in the global scope. + AFS_Global, + /// \brief Only look for allocation functions in the scope of the + /// allocated class. + AFS_Class, + /// \brief Look for allocation functions in both the global scope + /// and in the scope of the allocated class. + AFS_Both + }; + + /// \brief Finds the overloads of operator new and delete that are appropriate + /// for the allocation. bool FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, - bool UseGlobal, QualType AllocType, bool IsArray, + AllocationFunctionScope NewScope, + AllocationFunctionScope DeleteScope, + QualType AllocType, bool IsArray, bool &PassAlignment, MultiExprArg PlaceArgs, FunctionDecl *&OperatorNew, FunctionDecl *&OperatorDelete, Index: lib/Sema/SemaCoroutine.cpp =================================================================== --- lib/Sema/SemaCoroutine.cpp +++ lib/Sema/SemaCoroutine.cpp @@ -1110,8 +1110,8 @@ PlacementArgs.push_back(PDRefExpr.get()); } - S.FindAllocationFunctions(Loc, SourceRange(), - /*UseGlobal*/ false, PromiseType, + S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Class, + /*DeleteScope*/ Sema::AFS_Both, PromiseType, /*isArray*/ false, PassAlignment, PlacementArgs, OperatorNew, UnusedResult, /*Diagnose*/ false); @@ -1121,10 +1121,21 @@ // an argument of type std::size_t." if (!OperatorNew && !PlacementArgs.empty()) { PlacementArgs.clear(); - S.FindAllocationFunctions(Loc, SourceRange(), - /*UseGlobal*/ false, PromiseType, - /*isArray*/ false, PassAlignment, - PlacementArgs, OperatorNew, UnusedResult); + S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Class, + /*DeleteScope*/ Sema::AFS_Both, PromiseType, + /*isArray*/ false, PassAlignment, PlacementArgs, + OperatorNew, UnusedResult, /*Diagnose*/ false); + } + + // [dcl.fct.def.coroutine]/7 + // "The allocation function’s name is looked up in the scope of P. If this + // lookup fails, the allocation function’s name is looked up in the global + // scope." + if (!OperatorNew) { + S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Global, + /*DeleteScope*/ Sema::AFS_Both, PromiseType, + /*isArray*/ false, PassAlignment, PlacementArgs, + OperatorNew, UnusedResult); } bool IsGlobalOverload = @@ -1138,8 +1149,8 @@ return false; PlacementArgs = {StdNoThrow}; OperatorNew = nullptr; - S.FindAllocationFunctions(Loc, SourceRange(), - /*UseGlobal*/ true, PromiseType, + S.FindAllocationFunctions(Loc, SourceRange(), /*NewScope*/ Sema::AFS_Both, + /*DeleteScope*/ Sema::AFS_Both, PromiseType, /*isArray*/ false, PassAlignment, PlacementArgs, OperatorNew, UnusedResult); } Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -1975,11 +1975,12 @@ bool PassAlignment = getLangOpts().AlignedAllocation && Alignment > NewAlignment; + AllocationFunctionScope Scope = UseGlobal ? AFS_Global : AFS_Both; if (!AllocType->isDependentType() && !Expr::hasAnyTypeDependentArguments(PlacementArgs) && FindAllocationFunctions(StartLoc, SourceRange(PlacementLParen, PlacementRParen), - UseGlobal, AllocType, ArraySize, PassAlignment, + Scope, Scope, AllocType, ArraySize, PassAlignment, PlacementArgs, OperatorNew, OperatorDelete)) return ExprError(); @@ -2271,19 +2272,19 @@ 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, - bool UseGlobal, QualType AllocType, - bool IsArray, bool &PassAlignment, - MultiExprArg PlaceArgs, + AllocationFunctionScope NewScope, + AllocationFunctionScope DeleteScope, + QualType AllocType, bool IsArray, + bool &PassAlignment, MultiExprArg PlaceArgs, FunctionDecl *&OperatorNew, FunctionDecl *&OperatorDelete, bool Diagnose) { // --- Choosing an allocation function --- // C++ 5.3.4p8 - 14 & 18 - // 1) If UseGlobal is true, only look in the global scope. Else, also look - // in the scope of the allocated class. + // 1) If looking in AFS_Global scope for allocation functions, only look in + // the global scope. Else, if AFS_Class, only look in the scope of the + // allocated class. If AFS_Both, look in both. // 2) If an array size is given, look for operator new[], else look for // operator new. // 3) The first argument is always size_t. Append the arguments from the @@ -2333,7 +2334,7 @@ // function's name is looked up in the global scope. Otherwise, if the // allocated type is a class type T or array thereof, the allocation // function's name is looked up in the scope of T. - if (AllocElemType->isRecordType() && !UseGlobal) + if (AllocElemType->isRecordType() && NewScope != AFS_Global) LookupQualifiedName(R, AllocElemType->getAsCXXRecordDecl()); // We can see ambiguity here if the allocation function is found in @@ -2344,8 +2345,12 @@ // If this lookup fails to find the name, or if the allocated type is not // a class type, the allocation function's name is looked up in the // global scope. - if (R.empty()) + if (R.empty()) { + if (NewScope == AFS_Class) + return true; + LookupQualifiedName(R, Context.getTranslationUnitDecl()); + } assert(!R.empty() && "implicitly declared allocation functions not found"); assert(!R.isAmbiguous() && "global allocation functions are ambiguous"); @@ -2382,7 +2387,7 @@ // the allocated type is not a class type or array thereof, the // deallocation function's name is looked up in the global scope. LookupResult FoundDelete(*this, DeleteName, StartLoc, LookupOrdinaryName); - if (AllocElemType->isRecordType() && !UseGlobal) { + if (AllocElemType->isRecordType() && DeleteScope != AFS_Global) { CXXRecordDecl *RD = cast(AllocElemType->getAs()->getDecl()); LookupQualifiedName(FoundDelete, RD); @@ -2392,6 +2397,9 @@ bool FoundGlobalDelete = FoundDelete.empty(); if (FoundDelete.empty()) { + if (DeleteScope == AFS_Class) + return true; + DeclareGlobalNewDelete(); LookupQualifiedName(FoundDelete, Context.getTranslationUnitDecl()); } Index: test/CodeGenCoroutines/coro-alloc.cpp =================================================================== --- test/CodeGenCoroutines/coro-alloc.cpp +++ test/CodeGenCoroutines/coro-alloc.cpp @@ -134,6 +134,32 @@ co_return; } +// Declare a placement form operator new, such as the one described in +// C++ 18.6.1.3.1, which takes a void* argument. +void* operator new(SizeT __sz, void *__p) noexcept; + +struct promise_matching_global_placement_new_tag {}; +struct dummy {}; +template<> +struct std::experimental::coroutine_traits { + struct promise_type { + void get_return_object() {} + suspend_always initial_suspend() { return {}; } + suspend_always final_suspend() { return {}; } + void return_void() {} + }; +}; + +// A coroutine that takes a single pointer argument should not invoke this +// placement form operator. [dcl.fct.def.coroutine]/7 dictates that lookup for +// allocation functions matching the coroutine function's signature be done +// within the scope of the promise type's class. +// CHECK-LABEL: f1b( +extern "C" void f1b(promise_matching_global_placement_new_tag, dummy *) { + // CHECK: call i8* @_Znwm(i64 + co_return; +} + struct promise_delete_tag {}; template<> Index: test/SemaCXX/coroutines.cpp =================================================================== --- test/SemaCXX/coroutines.cpp +++ test/SemaCXX/coroutines.cpp @@ -804,62 +804,6 @@ void *operator new(SizeT, coroutine_nonstatic_member_struct &, double); }; -struct bad_promise_nonstatic_member_mismatched_custom_new_operator { - coro get_return_object(); - suspend_always initial_suspend(); - suspend_always final_suspend(); - void return_void(); - void unhandled_exception(); - // expected-note@+1 {{candidate function not viable: requires 2 arguments, but 1 was provided}} - void *operator new(SizeT, double); -}; - -struct coroutine_nonstatic_member_struct { - coro - good_coroutine_calls_nonstatic_member_custom_new_operator(double) { - co_return; - } - - coro - bad_coroutine_calls_nonstatic_member_mistmatched_custom_new_operator(double) { - // expected-error@-1 {{no matching function for call to 'operator new'}} - co_return; - } -}; - -struct bad_promise_mismatched_custom_new_operator { - coro get_return_object(); - suspend_always initial_suspend(); - suspend_always final_suspend(); - void return_void(); - void unhandled_exception(); - // expected-note@+1 {{candidate function not viable: requires 4 arguments, but 1 was provided}} - void *operator new(SizeT, double, float, int); -}; - -coro -bad_coroutine_calls_mismatched_custom_new_operator(double) { - // expected-error@-1 {{no matching function for call to 'operator new'}} - co_return; -} - -struct bad_promise_throwing_custom_new_operator { - static coro get_return_object_on_allocation_failure(); - coro get_return_object(); - suspend_always initial_suspend(); - suspend_always final_suspend(); - void return_void(); - void unhandled_exception(); - // expected-error@+1 {{'operator new' is required to have a non-throwing noexcept specification when the promise type declares 'get_return_object_on_allocation_failure()'}} - void *operator new(SizeT, double, float, int); -}; - -coro -bad_coroutine_calls_throwing_custom_new_operator(double, float, int) { - // expected-note@-1 {{call to 'operator new' implicitly required by coroutine function here}} - co_return; -} - struct good_promise_noexcept_custom_new_operator { static coro get_return_object_on_allocation_failure(); coro get_return_object();