diff options
author | Daniel Jasper <djasper@google.com> | 2016-10-10 14:13:55 +0000 |
---|---|---|
committer | Daniel Jasper <djasper@google.com> | 2016-10-10 14:13:55 +0000 |
commit | e9abe648166a95a85105f4138ea41b345a66035e (patch) | |
tree | 551aed9b3cc942c9f7f0d0e95b5b02f2a0f8db04 /clang/lib/Sema/SemaExprCXX.cpp | |
parent | 610ad3a5985c1eb40917eb948f5a09266270112c (diff) | |
download | bcm5719-llvm-e9abe648166a95a85105f4138ea41b345a66035e.tar.gz bcm5719-llvm-e9abe648166a95a85105f4138ea41b345a66035e.zip |
Revert "P0035R4: Semantic analysis and code generation for C++17 overaligned allocation."
This reverts commit r283722. Breaks:
Clang.SemaCUDA.device-var-init.cu
Clang.CodeGenCUDA.device-var-init.cu
http://lab.llvm.org:8080/green/job/clang-stage1-cmake-RA-expensive/884/
llvm-svn: 283750
Diffstat (limited to 'clang/lib/Sema/SemaExprCXX.cpp')
-rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 752 |
1 files changed, 306 insertions, 446 deletions
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 9c5dba5a38a..ad102f8d203 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1321,126 +1321,8 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, return Result; } -/// \brief Determine whether the given function is a non-placement -/// deallocation function. -static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) { - if (FD->isInvalidDecl()) - return false; - - if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FD)) - return Method->isUsualDeallocationFunction(); - - if (FD->getOverloadedOperator() != OO_Delete && - FD->getOverloadedOperator() != OO_Array_Delete) - return false; - - unsigned UsualParams = 1; - - if (S.getLangOpts().SizedDeallocation && UsualParams < FD->getNumParams() && - S.Context.hasSameUnqualifiedType( - FD->getParamDecl(UsualParams)->getType(), - S.Context.getSizeType())) - ++UsualParams; - - if (S.getLangOpts().AlignedAllocation && UsualParams < FD->getNumParams() && - S.Context.hasSameUnqualifiedType( - FD->getParamDecl(UsualParams)->getType(), - S.Context.getTypeDeclType(S.getStdAlignValT()))) - ++UsualParams; - - return UsualParams == FD->getNumParams(); -} - -namespace { - struct UsualDeallocFnInfo { - UsualDeallocFnInfo() : Found(), FD(nullptr) {} - UsualDeallocFnInfo(DeclAccessPair Found) - : Found(Found), FD(dyn_cast<FunctionDecl>(Found->getUnderlyingDecl())), - HasSizeT(false), HasAlignValT(false) { - // A function template declaration is never a usual deallocation function. - if (!FD) - return; - if (FD->getNumParams() == 3) - HasAlignValT = HasSizeT = true; - else if (FD->getNumParams() == 2) { - HasSizeT = FD->getParamDecl(1)->getType()->isIntegerType(); - HasAlignValT = !HasSizeT; - } - } - - operator bool() const { return FD; } - - DeclAccessPair Found; - FunctionDecl *FD; - bool HasSizeT, HasAlignValT; - }; -} - -/// Determine whether a type has new-extended alignment. This may be called when -/// the type is incomplete (for a delete-expression with an incomplete pointee -/// type), in which case it will conservatively return false if the alignment is -/// not known. -static bool hasNewExtendedAlignment(Sema &S, QualType AllocType) { - return S.getLangOpts().AlignedAllocation && - S.getASTContext().getTypeAlignIfKnown(AllocType) > - S.getASTContext().getTargetInfo().getNewAlign(); -} - -/// Select the correct "usual" deallocation function to use from a selection of -/// deallocation functions (either global or class-scope). -static UsualDeallocFnInfo resolveDeallocationOverload( - Sema &S, LookupResult &R, bool WantSize, bool WantAlign, - llvm::SmallVectorImpl<UsualDeallocFnInfo> *BestFns = nullptr) { - UsualDeallocFnInfo Best; - - // For CUDA, rank callability above anything else when ordering usual - // deallocation functions. - // FIXME: We should probably instead rank this between alignment (which - // affects correctness) and size (which is just an optimization). - if (S.getLangOpts().CUDA) - S.EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(S.CurContext), R); - - for (auto I = R.begin(), E = R.end(); I != E; ++I) { - UsualDeallocFnInfo Info(I.getPair()); - if (!Info || !isNonPlacementDeallocationFunction(S, Info.FD)) - continue; - - if (!Best) { - Best = Info; - if (BestFns) - BestFns->push_back(Info); - continue; - } - - // C++17 [expr.delete]p10: - // If the type has new-extended alignment, a function with a parameter of - // type std::align_val_t is preferred; otherwise a function without such a - // parameter is preferred - if (Best.HasAlignValT == WantAlign && Info.HasAlignValT != WantAlign) - continue; - - if (Best.HasAlignValT == Info.HasAlignValT && - Best.HasSizeT == WantSize && Info.HasSizeT != WantSize) - continue; - - // If more than one preferred function is found, all non-preferred - // functions are eliminated from further consideration. - if (BestFns && (Best.HasAlignValT != Info.HasAlignValT || - Best.HasSizeT != Info.HasSizeT)) - BestFns->clear(); - - Best = Info; - if (BestFns) - BestFns->push_back(Info); - } - - return Best; -} - -/// Determine whether a given type is a class for which 'delete[]' would call -/// a member 'operator delete[]' with a 'size_t' parameter. This implies that -/// we need to store the array size (even if the type is -/// trivially-destructible). +/// doesUsualArrayDeleteWantSize - Answers whether the usual +/// operator delete[] for the given type has a size_t parameter. static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc, QualType allocType) { const RecordType *record = @@ -1464,13 +1346,35 @@ static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc, // on this thing, so it doesn't matter if we allocate extra space or not. if (ops.isAmbiguous()) return false; - // C++17 [expr.delete]p10: - // If the deallocation functions have class scope, the one without a - // parameter of type std::size_t is selected. - auto Best = resolveDeallocationOverload( - S, ops, /*WantSize*/false, - /*WantAlign*/hasNewExtendedAlignment(S, allocType)); - return Best && Best.HasSizeT; + LookupResult::Filter filter = ops.makeFilter(); + while (filter.hasNext()) { + NamedDecl *del = filter.next()->getUnderlyingDecl(); + + // C++0x [basic.stc.dynamic.deallocation]p2: + // A template instance is never a usual deallocation function, + // regardless of its signature. + if (isa<FunctionTemplateDecl>(del)) { + filter.erase(); + continue; + } + + // C++0x [basic.stc.dynamic.deallocation]p2: + // If class T does not declare [an operator delete[] with one + // parameter] but does declare a member deallocation function + // named operator delete[] with exactly two parameters, the + // second of which has type std::size_t, then this function + // is a usual deallocation function. + if (!cast<CXXMethodDecl>(del)->isUsualDeallocationFunction()) { + filter.erase(); + continue; + } + } + filter.done(); + + if (!ops.isSingleResult()) return false; + + const FunctionDecl *del = cast<FunctionDecl>(ops.getFoundDecl()); + return (del->getNumParams() == 2); } /// \brief Parsed a C++ 'new' expression (C++ 5.3.4). @@ -1826,26 +1730,21 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, FunctionDecl *OperatorNew = nullptr; FunctionDecl *OperatorDelete = nullptr; - unsigned Alignment = - AllocType->isDependentType() ? 0 : Context.getTypeAlign(AllocType); - unsigned NewAlignment = Context.getTargetInfo().getNewAlign(); - bool PassAlignment = getLangOpts().AlignedAllocation && - Alignment > NewAlignment; if (!AllocType->isDependentType() && !Expr::hasAnyTypeDependentArguments(PlacementArgs) && FindAllocationFunctions(StartLoc, SourceRange(PlacementLParen, PlacementRParen), - UseGlobal, AllocType, ArraySize, PassAlignment, - PlacementArgs, OperatorNew, OperatorDelete)) + UseGlobal, AllocType, ArraySize, PlacementArgs, + OperatorNew, OperatorDelete)) return ExprError(); // If this is an array allocation, compute whether the usual array // deallocation function for the type has a size_t parameter. bool UsualArrayDeleteWantsSize = false; if (ArraySize && !AllocType->isDependentType()) - UsualArrayDeleteWantsSize = - doesUsualArrayDeleteWantSize(*this, StartLoc, AllocType); + UsualArrayDeleteWantsSize + = doesUsualArrayDeleteWantSize(*this, StartLoc, AllocType); SmallVector<Expr *, 8> AllPlaceArgs; if (OperatorNew) { @@ -1856,11 +1755,9 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, // We've already converted the placement args, just fill in any default // arguments. Skip the first parameter because we don't have a corresponding - // argument. Skip the second parameter too if we're passing in the - // alignment; we've already filled it in. - if (GatherArgumentsForCall(PlacementLParen, OperatorNew, Proto, - PassAlignment ? 2 : 1, PlacementArgs, - AllPlaceArgs, CallType)) + // argument. + if (GatherArgumentsForCall(PlacementLParen, OperatorNew, Proto, 1, + PlacementArgs, AllPlaceArgs, CallType)) return ExprError(); if (!AllPlaceArgs.empty()) @@ -1870,18 +1767,21 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, DiagnoseSentinelCalls(OperatorNew, PlacementLParen, PlacementArgs); // FIXME: Missing call to CheckFunctionCall or equivalent + } - // Warn if the type is over-aligned and is being allocated by (unaligned) - // global operator new. - if (PlacementArgs.empty() && !PassAlignment && - (OperatorNew->isImplicit() || - (OperatorNew->getLocStart().isValid() && - getSourceManager().isInSystemHeader(OperatorNew->getLocStart())))) { - if (Alignment > NewAlignment) + // Warn if the type is over-aligned and is being allocated by global operator + // new. + if (PlacementArgs.empty() && OperatorNew && + (OperatorNew->isImplicit() || + (OperatorNew->getLocStart().isValid() && + getSourceManager().isInSystemHeader(OperatorNew->getLocStart())))) { + if (unsigned Align = Context.getPreferredTypeAlign(AllocType.getTypePtr())){ + unsigned SuitableAlign = Context.getTargetInfo().getSuitableAlign(); + if (Align > SuitableAlign) Diag(StartLoc, diag::warn_overaligned_type) << AllocType - << unsigned(Alignment / Context.getCharWidth()) - << unsigned(NewAlignment / Context.getCharWidth()); + << unsigned(Align / Context.getCharWidth()) + << unsigned(SuitableAlign / Context.getCharWidth()); } } @@ -1980,7 +1880,7 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, } return new (Context) - CXXNewExpr(Context, UseGlobal, OperatorNew, OperatorDelete, PassAlignment, + CXXNewExpr(Context, UseGlobal, OperatorNew, OperatorDelete, UsualArrayDeleteWantsSize, PlacementArgs, TypeIdParens, ArraySize, initStyle, Initializer, ResultType, AllocTypeInfo, Range, DirectInitRange); @@ -2023,128 +1923,32 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc, return false; } -static bool -resolveAllocationOverload(Sema &S, LookupResult &R, SourceRange Range, - SmallVectorImpl<Expr *> &Args, bool &PassAlignment, - FunctionDecl *&Operator, - OverloadCandidateSet *AlignedCandidates = nullptr, - Expr *AlignArg = nullptr) { - OverloadCandidateSet Candidates(R.getNameLoc(), - OverloadCandidateSet::CSK_Normal); - for (LookupResult::iterator Alloc = R.begin(), AllocEnd = R.end(); - Alloc != AllocEnd; ++Alloc) { - // Even member operator new/delete are implicitly treated as - // static, so don't use AddMemberCandidate. - NamedDecl *D = (*Alloc)->getUnderlyingDecl(); - - if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) { - S.AddTemplateOverloadCandidate(FnTemplate, Alloc.getPair(), - /*ExplicitTemplateArgs=*/nullptr, Args, - Candidates, - /*SuppressUserConversions=*/false); - continue; - } - - FunctionDecl *Fn = cast<FunctionDecl>(D); - S.AddOverloadCandidate(Fn, Alloc.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; - - Operator = FnDecl; +/// \brief Determine whether the given function is a non-placement +/// deallocation function. +static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) { + if (FD->isInvalidDecl()) return false; - } - case OR_No_Viable_Function: - // C++17 [expr.new]p13: - // If no matching function is found and the allocated object type has - // new-extended alignment, the alignment argument is removed from the - // argument list, and overload resolution is performed again. - if (PassAlignment) { - PassAlignment = false; - AlignArg = Args[1]; - Args.erase(Args.begin() + 1); - return resolveAllocationOverload(S, R, Range, Args, PassAlignment, - Operator, &Candidates, AlignArg); - } - - // MSVC will fall back on trying to find a matching global operator new - // if operator new[] cannot be found. Also, MSVC will leak by not - // generating a call to operator delete or operator delete[], but we - // will not replicate that bug. - // FIXME: Find out how this interacts with the std::align_val_t fallback - // once MSVC implements it. - if (R.getLookupName().getCXXOverloadedOperator() == OO_Array_New && - S.Context.getLangOpts().MSVCCompat) { - R.clear(); - R.setLookupName(S.Context.DeclarationNames.getCXXOperatorName(OO_New)); - S.LookupQualifiedName(R, S.Context.getTranslationUnitDecl()); - // FIXME: This will give bad diagnostics pointing at the wrong functions. - return resolveAllocationOverload(S, R, Range, Args, PassAlignment, - Operator, nullptr); - } + if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FD)) + return Method->isUsualDeallocationFunction(); - S.Diag(R.getNameLoc(), diag::err_ovl_no_viable_function_in_call) - << R.getLookupName() << Range; - - // If we have aligned candidates, only note the align_val_t candidates - // from AlignedCandidates and the non-align_val_t candidates from - // Candidates. - if (AlignedCandidates) { - auto IsAligned = [](OverloadCandidate &C) { - return C.Function->getNumParams() > 1 && - C.Function->getParamDecl(1)->getType()->isAlignValT(); - }; - auto IsUnaligned = [&](OverloadCandidate &C) { return !IsAligned(C); }; - - // This was an overaligned allocation, so list the aligned candidates - // first. - Args.insert(Args.begin() + 1, AlignArg); - AlignedCandidates->NoteCandidates(S, OCD_AllCandidates, Args, "", - R.getNameLoc(), IsAligned); - Args.erase(Args.begin() + 1); - Candidates.NoteCandidates(S, OCD_AllCandidates, Args, "", R.getNameLoc(), - IsUnaligned); - } else { - Candidates.NoteCandidates(S, OCD_AllCandidates, Args); - } - return true; + if (FD->getOverloadedOperator() != OO_Delete && + FD->getOverloadedOperator() != OO_Array_Delete) + return false; - case OR_Ambiguous: - S.Diag(R.getNameLoc(), diag::err_ovl_ambiguous_call) - << R.getLookupName() << Range; - Candidates.NoteCandidates(S, OCD_ViableCandidates, Args); + if (FD->getNumParams() == 1) 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"); + return S.getLangOpts().SizedDeallocation && FD->getNumParams() == 2 && + S.Context.hasSameUnqualifiedType(FD->getParamDecl(1)->getType(), + S.Context.getSizeType()); } - /// 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, + bool IsArray, MultiExprArg PlaceArgs, FunctionDecl *&OperatorNew, FunctionDecl *&OperatorDelete) { // --- Choosing an allocation function --- @@ -2156,29 +1960,16 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, // 3) The first argument is always size_t. Append the arguments from the // placement form. - SmallVector<Expr*, 8> AllocArgs; - AllocArgs.reserve((PassAlignment ? 2 : 1) + PlaceArgs.size()); - - // We don't care about the actual value of these arguments. + SmallVector<Expr*, 8> AllocArgs(1 + PlaceArgs.size()); + // We don't care about the actual value of this argument. // FIXME: Should the Sema create the expression and embed it in the syntax // tree? Or should the consumer just recalculate the value? - // FIXME: Using a dummy value will interact poorly with attribute enable_if. IntegerLiteral Size(Context, llvm::APInt::getNullValue( Context.getTargetInfo().getPointerWidth(0)), Context.getSizeType(), SourceLocation()); - AllocArgs.push_back(&Size); - - QualType AlignValT = Context.VoidTy; - if (PassAlignment) { - DeclareGlobalNewDelete(); - AlignValT = Context.getTypeDeclType(getStdAlignValT()); - } - CXXScalarValueInitExpr Align(AlignValT, nullptr, SourceLocation()); - if (PassAlignment) - AllocArgs.push_back(&Align); - - AllocArgs.insert(AllocArgs.end(), PlaceArgs.begin(), PlaceArgs.end()); + AllocArgs[0] = &Size; + std::copy(PlaceArgs.begin(), PlaceArgs.end(), AllocArgs.begin() + 1); // C++ [expr.new]p8: // If the allocated type is a non-array type, the allocation @@ -2187,57 +1978,50 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, // type, the allocation function's name is operator new[] and the // deallocation function's name is operator delete[]. DeclarationName NewName = Context.DeclarationNames.getCXXOperatorName( - IsArray ? OO_Array_New : OO_New); + IsArray ? OO_Array_New : OO_New); + DeclarationName DeleteName = Context.DeclarationNames.getCXXOperatorName( + IsArray ? OO_Array_Delete : OO_Delete); QualType AllocElemType = Context.getBaseElementType(AllocType); - // Find the allocation function. - { - LookupResult R(*this, NewName, StartLoc, LookupOrdinaryName); - - // C++1z [expr.new]p9: - // If the new-expression begins with a unary :: operator, the allocation - // 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) - LookupQualifiedName(R, AllocElemType->getAsCXXRecordDecl()); - - // We can see ambiguity here if the allocation function is found in - // multiple base classes. - if (R.isAmbiguous()) + if (AllocElemType->isRecordType() && !UseGlobal) { + CXXRecordDecl *Record + = cast<CXXRecordDecl>(AllocElemType->getAs<RecordType>()->getDecl()); + if (FindAllocationOverload(StartLoc, Range, NewName, AllocArgs, Record, + /*AllowMissing=*/true, OperatorNew)) return true; + } - // 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()) - 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(); + if (!OperatorNew) { + // Didn't find a member overload. Look for a global one. + DeclareGlobalNewDelete(); + DeclContext *TUDecl = Context.getTranslationUnitDecl(); + bool FallbackEnabled = IsArray && Context.getLangOpts().MSVCCompat; + if (FindAllocationOverload(StartLoc, Range, NewName, AllocArgs, TUDecl, + /*AllowMissing=*/FallbackEnabled, OperatorNew, + /*Diagnose=*/!FallbackEnabled)) { + if (!FallbackEnabled) + return true; - if (resolveAllocationOverload(*this, R, Range, AllocArgs, PassAlignment, - OperatorNew)) + // MSVC will fall back on trying to find a matching global operator new + // if operator new[] cannot be found. Also, MSVC will leak by not + // generating a call to operator delete or operator delete[], but we + // will not replicate that bug. + NewName = Context.DeclarationNames.getCXXOperatorName(OO_New); + DeleteName = Context.DeclarationNames.getCXXOperatorName(OO_Delete); + if (FindAllocationOverload(StartLoc, Range, NewName, AllocArgs, TUDecl, + /*AllowMissing=*/false, OperatorNew)) return true; + } } - // We don't need an operator delete if we're running under -fno-exceptions. + // We don't need an operator delete if we're running under + // -fno-exceptions. if (!getLangOpts().Exceptions) { OperatorDelete = nullptr; return false; } - // Note, the name of OperatorNew might have been changed from array to - // non-array by resolveAllocationOverload. - DeclarationName DeleteName = Context.DeclarationNames.getCXXOperatorName( - OperatorNew->getDeclName().getCXXOverloadedOperator() == OO_Array_New - ? OO_Array_Delete - : OO_Delete); - // C++ [expr.new]p19: // // If the new-expression begins with a unary :: operator, the @@ -2256,7 +2040,6 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, if (FoundDelete.isAmbiguous()) return true; // FIXME: clean up expressions? - bool FoundGlobalDelete = FoundDelete.empty(); if (FoundDelete.empty()) { DeclareGlobalNewDelete(); LookupQualifiedName(FoundDelete, Context.getTranslationUnitDecl()); @@ -2271,16 +2054,7 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, // we had explicit placement arguments. This matters for things like // struct A { void *operator new(size_t, int = 0); ... }; // A *a = new A() - // - // We don't have any definition for what a "placement allocation function" - // is, but we assume it's any allocation function whose - // parameter-declaration-clause is anything other than (size_t). - // - // FIXME: Should (size_t, std::align_val_t) also be considered non-placement? - // This affects whether an exception from the constructor of an overaligned - // type uses the sized or non-sized form of aligned operator delete. - bool isPlacementNew = !PlaceArgs.empty() || OperatorNew->param_size() != 1 || - OperatorNew->isVariadic(); + bool isPlacementNew = (!PlaceArgs.empty() || OperatorNew->param_size() != 1); if (isPlacementNew) { // C++ [expr.new]p20: @@ -2306,9 +2080,7 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, ArgTypes.push_back(Proto->getParamType(I)); FunctionProtoType::ExtProtoInfo EPI; - // FIXME: This is not part of the standard's rule. EPI.Variadic = Proto->isVariadic(); - EPI.ExceptionSpec.Type = EST_BasicNoexcept; ExpectedFunctionType = Context.getFunctionType(Context.VoidTy, ArgTypes, EPI); @@ -2332,29 +2104,35 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, if (Context.hasSameType(Fn->getType(), ExpectedFunctionType)) Matches.push_back(std::make_pair(D.getPair(), Fn)); } - - if (getLangOpts().CUDA) - EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(CurContext), Matches); } else { + // C++ [expr.new]p20: + // [...] Any non-placement deallocation function matches a + // non-placement allocation function. [...] + for (LookupResult::iterator D = FoundDelete.begin(), + DEnd = FoundDelete.end(); + D != DEnd; ++D) { + if (FunctionDecl *Fn = dyn_cast<FunctionDecl>((*D)->getUnderlyingDecl())) + if (isNonPlacementDeallocationFunction(*this, Fn)) + Matches.push_back(std::make_pair(D.getPair(), Fn)); + } + // C++1y [expr.new]p22: // For a non-placement allocation function, the normal deallocation // function lookup is used - // - // Per [expr.delete]p10, this lookup prefers a member operator delete - // without a size_t argument, but prefers a non-member operator delete - // with a size_t where possible (which it always is in this case). - llvm::SmallVector<UsualDeallocFnInfo, 4> BestDeallocFns; - UsualDeallocFnInfo Selected = resolveDeallocationOverload( - *this, FoundDelete, /*WantSize*/ FoundGlobalDelete, - /*WantAlign*/ hasNewExtendedAlignment(*this, AllocElemType), - &BestDeallocFns); - if (Selected) - Matches.push_back(std::make_pair(Selected.Found, Selected.FD)); - else { - // If we failed to select an operator, all remaining functions are viable - // but ambiguous. - for (auto Fn : BestDeallocFns) - Matches.push_back(std::make_pair(Fn.Found, Fn.FD)); + // C++1y [expr.delete]p?: + // If [...] deallocation function lookup finds both a usual deallocation + // function with only a pointer parameter and a usual deallocation + // function with both a pointer parameter and a size parameter, then the + // selected deallocation function shall be the one with two parameters. + // Otherwise, the selected deallocation function shall be the function + // with one parameter. + if (getLangOpts().SizedDeallocation && Matches.size() == 2) { + if (Matches[0].second->getNumParams() == 1) + Matches.erase(Matches.begin()); + else + Matches.erase(Matches.begin() + 1); + assert(Matches[0].second->getNumParams() == 2 && + "found an unexpected usual deallocation function"); } } @@ -2365,58 +2143,130 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, if (Matches.size() == 1) { OperatorDelete = Matches[0].second; - // C++1z [expr.new]p23: - // If the lookup finds a usual deallocation function (3.7.4.2) - // with a parameter of type std::size_t and that function, considered + // C++0x [expr.new]p20: + // If the lookup finds the two-parameter form of a usual + // deallocation function (3.7.4.2) and that function, considered // as a placement deallocation function, would have been // selected as a match for the allocation function, the program // is ill-formed. - if (getLangOpts().CPlusPlus11 && isPlacementNew && + if (!PlaceArgs.empty() && getLangOpts().CPlusPlus11 && isNonPlacementDeallocationFunction(*this, OperatorDelete)) { - UsualDeallocFnInfo Info(DeclAccessPair::make(OperatorDelete, AS_public)); - // Core issue, per mail to core reflector, 2016-10-09: - // If this is a member operator delete, and there is a corresponding - // non-sized member operator delete, this isn't /really/ a sized - // deallocation function, it just happens to have a size_t parameter. - bool IsSizedDelete = Info.HasSizeT; - if (IsSizedDelete && !FoundGlobalDelete) { - auto NonSizedDelete = - resolveDeallocationOverload(*this, FoundDelete, /*WantSize*/false, - /*WantAlign*/Info.HasAlignValT); - if (NonSizedDelete && !NonSizedDelete.HasSizeT && - NonSizedDelete.HasAlignValT == Info.HasAlignValT) - IsSizedDelete = false; - } + Diag(StartLoc, diag::err_placement_new_non_placement_delete) + << SourceRange(PlaceArgs.front()->getLocStart(), + PlaceArgs.back()->getLocEnd()); + if (!OperatorDelete->isImplicit()) + Diag(OperatorDelete->getLocation(), diag::note_previous_decl) + << DeleteName; + } else { + CheckAllocationAccess(StartLoc, Range, FoundDelete.getNamingClass(), + Matches[0].first); + } + } - if (IsSizedDelete) { - SourceRange R = PlaceArgs.empty() - ? SourceRange() - : SourceRange(PlaceArgs.front()->getLocStart(), - PlaceArgs.back()->getLocEnd()); - Diag(StartLoc, diag::err_placement_new_non_placement_delete) << R; - if (!OperatorDelete->isImplicit()) - Diag(OperatorDelete->getLocation(), diag::note_previous_decl) - << DeleteName; - } + return false; +} + +/// \brief Find an fitting overload for the allocation function +/// in the specified scope. +/// +/// \param StartLoc The location of the 'new' token. +/// \param Range The range of the placement arguments. +/// \param Name The name of the function ('operator new' or 'operator new[]'). +/// \param Args The placement arguments specified. +/// \param Ctx The scope in which we should search; either a class scope or the +/// translation unit. +/// \param AllowMissing If \c true, report an error if we can't find any +/// allocation functions. Otherwise, succeed but don't fill in \p +/// Operator. +/// \param Operator Filled in with the found allocation function. Unchanged if +/// no allocation function was found. +/// \param Diagnose If \c true, issue errors if the allocation function is not +/// usable. +bool Sema::FindAllocationOverload(SourceLocation StartLoc, SourceRange Range, + DeclarationName Name, MultiExprArg Args, + DeclContext *Ctx, + bool AllowMissing, FunctionDecl *&Operator, + bool Diagnose) { + LookupResult R(*this, Name, StartLoc, LookupOrdinaryName); + LookupQualifiedName(R, Ctx); + if (R.empty()) { + if (AllowMissing || !Diagnose) + return false; + return Diag(StartLoc, diag::err_ovl_no_viable_function_in_call) + << Name << Range; + } + + if (R.isAmbiguous()) + return true; + + R.suppressDiagnostics(); + + OverloadCandidateSet Candidates(StartLoc, OverloadCandidateSet::CSK_Normal); + for (LookupResult::iterator Alloc = R.begin(), AllocEnd = R.end(); + Alloc != AllocEnd; ++Alloc) { + // Even member operator new/delete are implicitly treated as + // static, so don't use AddMemberCandidate. + NamedDecl *D = (*Alloc)->getUnderlyingDecl(); + + if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) { + AddTemplateOverloadCandidate(FnTemplate, Alloc.getPair(), + /*ExplicitTemplateArgs=*/nullptr, + Args, Candidates, + /*SuppressUserConversions=*/false); + continue; } - CheckAllocationAccess(StartLoc, Range, FoundDelete.getNamingClass(), - Matches[0].first); - } else if (!Matches.empty()) { - // We found multiple suitable operators. Per [expr.new]p20, that means we - // call no 'operator delete' function, but we should at least warn the user. - // FIXME: Suppress this warning if the construction cannot throw. - Diag(StartLoc, diag::warn_ambiguous_suitable_delete_function_found) - << DeleteName << AllocElemType; + FunctionDecl *Fn = cast<FunctionDecl>(D); + AddOverloadCandidate(Fn, Alloc.getPair(), Args, Candidates, + /*SuppressUserConversions=*/false); + } - for (auto &Match : Matches) - Diag(Match.second->getLocation(), - diag::note_member_declared_here) << DeleteName; + // Do the resolution. + OverloadCandidateSet::iterator Best; + switch (Candidates.BestViableFunction(*this, StartLoc, Best)) { + case OR_Success: { + // Got one! + FunctionDecl *FnDecl = Best->Function; + if (CheckAllocationAccess(StartLoc, Range, R.getNamingClass(), + Best->FoundDecl, Diagnose) == AR_inaccessible) + return true; + + Operator = FnDecl; + return false; } - return false; + case OR_No_Viable_Function: + if (Diagnose) { + Diag(StartLoc, diag::err_ovl_no_viable_function_in_call) + << Name << Range; + Candidates.NoteCandidates(*this, OCD_AllCandidates, Args); + } + return true; + + case OR_Ambiguous: + if (Diagnose) { + Diag(StartLoc, diag::err_ovl_ambiguous_call) + << Name << Range; + Candidates.NoteCandidates(*this, OCD_ViableCandidates, Args); + } + return true; + + case OR_Deleted: { + if (Diagnose) { + Diag(StartLoc, diag::err_ovl_deleted_call) + << Best->Function->isDeleted() + << Name + << getDeletedOrUnavailableSuffix(Best->Function) + << Range; + Candidates.NoteCandidates(*this, OCD_AllCandidates, Args); + } + return true; + } + } + llvm_unreachable("Unreachable, bad result from BestViableFunction"); } + /// DeclareGlobalNewDelete - Declare the global forms of operator new and /// delete. These are: /// @code @@ -2610,43 +2460,52 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name, FunctionDecl *Sema::FindUsualDeallocationFunction(SourceLocation StartLoc, bool CanProvideSize, - bool Overaligned, DeclarationName Name) { DeclareGlobalNewDelete(); LookupResult FoundDelete(*this, Name, StartLoc, LookupOrdinaryName); LookupQualifiedName(FoundDelete, Context.getTranslationUnitDecl()); - // FIXME: It's possible for this to result in ambiguity, through a - // user-declared variadic operator delete or the enable_if attribute. We - // should probably not consider those cases to be usual deallocation - // functions. But for now we just make an arbitrary choice in that case. - auto Result = resolveDeallocationOverload(*this, FoundDelete, CanProvideSize, - Overaligned); - assert(Result.FD && "operator delete missing from global scope?"); - return Result.FD; -} + // C++ [expr.new]p20: + // [...] Any non-placement deallocation function matches a + // non-placement allocation function. [...] + llvm::SmallVector<FunctionDecl*, 2> Matches; + for (LookupResult::iterator D = FoundDelete.begin(), + DEnd = FoundDelete.end(); + D != DEnd; ++D) { + if (FunctionDecl *Fn = dyn_cast<FunctionDecl>(*D)) + if (isNonPlacementDeallocationFunction(*this, Fn)) + Matches.push_back(Fn); + } + + // C++1y [expr.delete]p?: + // If the type is complete and deallocation function lookup finds both a + // usual deallocation function with only a pointer parameter and a usual + // deallocation function with both a pointer parameter and a size + // parameter, then the selected deallocation function shall be the one + // with two parameters. Otherwise, the selected deallocation function + // shall be the function with one parameter. + if (getLangOpts().SizedDeallocation && Matches.size() == 2) { + unsigned NumArgs = CanProvideSize ? 2 : 1; + if (Matches[0]->getNumParams() != NumArgs) + Matches.erase(Matches.begin()); + else + Matches.erase(Matches.begin() + 1); + assert(Matches[0]->getNumParams() == NumArgs && + "found an unexpected usual deallocation function"); + } -FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc, - CXXRecordDecl *RD) { - DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Delete); + if (getLangOpts().CUDA) + EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(CurContext), Matches); - FunctionDecl *OperatorDelete = nullptr; - if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete)) - return nullptr; - if (OperatorDelete) - return OperatorDelete; - - // If there's no class-specific operator delete, look up the global - // non-array delete. - return FindUsualDeallocationFunction( - Loc, true, hasNewExtendedAlignment(*this, Context.getRecordType(RD)), - Name); + assert(Matches.size() == 1 && + "unexpectedly have multiple usual deallocation functions"); + return Matches.front(); } bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, DeclarationName Name, - FunctionDecl *&Operator, bool Diagnose) { + FunctionDecl* &Operator, bool Diagnose) { LookupResult Found(*this, Name, StartLoc, LookupOrdinaryName); // Try to find operator delete/operator delete[] in class scope. LookupQualifiedName(Found, RD); @@ -2656,20 +2515,27 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, Found.suppressDiagnostics(); - bool Overaligned = hasNewExtendedAlignment(*this, Context.getRecordType(RD)); + SmallVector<DeclAccessPair,4> Matches; + for (LookupResult::iterator F = Found.begin(), FEnd = Found.end(); + F != FEnd; ++F) { + NamedDecl *ND = (*F)->getUnderlyingDecl(); - // C++17 [expr.delete]p10: - // If the deallocation functions have class scope, the one without a - // parameter of type std::size_t is selected. - llvm::SmallVector<UsualDeallocFnInfo, 4> Matches; - resolveDeallocationOverload(*this, Found, /*WantSize*/ false, - /*WantAlign*/ Overaligned, &Matches); + // Ignore template operator delete members from the check for a usual + // deallocation function. + if (isa<FunctionTemplateDecl>(ND)) + continue; + + if (cast<CXXMethodDecl>(ND)->isUsualDeallocationFunction()) + Matches.push_back(F.getPair()); + } - // If we could find an overload, use it. + if (getLangOpts().CUDA) + EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(CurContext), Matches); + + // There's exactly one suitable operator; pick it. if (Matches.size() == 1) { - Operator = cast<CXXMethodDecl>(Matches[0].FD); + Operator = cast<CXXMethodDecl>(Matches[0]->getUnderlyingDecl()); - // FIXME: DiagnoseUseOfDecl? if (Operator->isDeleted()) { if (Diagnose) { Diag(StartLoc, diag::err_deleted_function_use); @@ -2679,21 +2545,21 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, } if (CheckAllocationAccess(StartLoc, SourceRange(), Found.getNamingClass(), - Matches[0].Found, Diagnose) == AR_inaccessible) + Matches[0], Diagnose) == AR_inaccessible) return true; return false; - } - // We found multiple suitable operators; complain about the ambiguity. - // FIXME: The standard doesn't say to do this; it appears that the intent - // is that this should never happen. - if (!Matches.empty()) { + // We found multiple suitable operators; complain about the ambiguity. + } else if (!Matches.empty()) { if (Diagnose) { Diag(StartLoc, diag::err_ambiguous_suitable_delete_member_function_found) << Name << RD; - for (auto &Match : Matches) - Diag(Match.FD->getLocation(), diag::note_member_declared_here) << Name; + + for (SmallVectorImpl<DeclAccessPair>::iterator + F = Matches.begin(), FEnd = Matches.end(); F != FEnd; ++F) + Diag((*F)->getUnderlyingDecl()->getLocation(), + diag::note_member_declared_here) << Name; } return true; } @@ -2705,8 +2571,9 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, Diag(StartLoc, diag::err_no_suitable_delete_member_function_found) << Name << RD; - for (NamedDecl *D : Found) - Diag(D->getUnderlyingDecl()->getLocation(), + for (LookupResult::iterator F = Found.begin(), FEnd = Found.end(); + F != FEnd; ++F) + Diag((*F)->getUnderlyingDecl()->getLocation(), diag::note_member_declared_here) << Name; } return true; @@ -3117,10 +2984,7 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, // Otherwise, the usual operator delete[] should be the // function we just found. else if (OperatorDelete && isa<CXXMethodDecl>(OperatorDelete)) - UsualArrayDeleteWantsSize = - UsualDeallocFnInfo( - DeclAccessPair::make(OperatorDelete, AS_public)) - .HasSizeT; + UsualArrayDeleteWantsSize = (OperatorDelete->getNumParams() == 2); } if (!PointeeRD->hasIrrelevantDestructor()) @@ -3137,17 +3001,13 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, SourceLocation()); } - if (!OperatorDelete) { - bool IsComplete = isCompleteType(StartLoc, Pointee); - bool CanProvideSize = - IsComplete && (!ArrayForm || UsualArrayDeleteWantsSize || - Pointee.isDestructedType()); - bool Overaligned = hasNewExtendedAlignment(*this, Pointee); - + if (!OperatorDelete) // Look for a global declaration. - OperatorDelete = FindUsualDeallocationFunction(StartLoc, CanProvideSize, - Overaligned, DeleteName); - } + OperatorDelete = FindUsualDeallocationFunction( + StartLoc, isCompleteType(StartLoc, Pointee) && + (!ArrayForm || UsualArrayDeleteWantsSize || + Pointee.isDestructedType()), + DeleteName); MarkFunctionReferenced(StartLoc, OperatorDelete); |