diff options
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/SemaCUDA.cpp | 28 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 8 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 26 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 752 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 11 |
5 files changed, 499 insertions, 326 deletions
diff --git a/clang/lib/Sema/SemaCUDA.cpp b/clang/lib/Sema/SemaCUDA.cpp index d6c0606674e..827eb02bcd6 100644 --- a/clang/lib/Sema/SemaCUDA.cpp +++ b/clang/lib/Sema/SemaCUDA.cpp @@ -158,6 +158,34 @@ Sema::IdentifyCUDAPreference(const FunctionDecl *Caller, llvm_unreachable("All cases should've been handled by now."); } +void Sema::EraseUnwantedCUDAMatches(const FunctionDecl *Caller, + LookupResult &R) { + if (R.isSingleResult()) + return; + + // Gets the CUDA function preference for a call from Caller to Match. + auto GetCFP = [&](const NamedDecl *D) { + if (auto *Callee = dyn_cast<FunctionDecl>(D->getUnderlyingDecl())) + return IdentifyCUDAPreference(Caller, Callee); + return CFP_Never; + }; + + // Find the best call preference among the functions in R. + CUDAFunctionPreference BestCFP = GetCFP(*std::max_element( + R.begin(), R.end(), [&](const NamedDecl *D1, const NamedDecl *D2) { + return GetCFP(D1) < GetCFP(D2); + })); + + // Erase all functions with lower priority. + auto Filter = R.makeFilter(); + while (Filter.hasNext()) { + auto *Callee = dyn_cast<FunctionDecl>(Filter.next()->getUnderlyingDecl()); + if (Callee && GetCFP(Callee) < BestCFP) + Filter.erase(); + } + Filter.done(); +} + template <typename T> static void EraseUnwantedCUDAMatchesImpl( Sema &S, const FunctionDecl *Caller, llvm::SmallVectorImpl<T> &Matches, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 32201c99d3d..7baa85a6464 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14368,6 +14368,14 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, if (!Completed) Record->completeDefinition(); + // We may have deferred checking for a deleted destructor. Check now. + if (CXXRecordDecl *CXXRecord = dyn_cast<CXXRecordDecl>(Record)) { + auto *Dtor = CXXRecord->getDestructor(); + if (Dtor && Dtor->isImplicit() && + ShouldDeleteSpecialMember(Dtor, CXXDestructor)) + SetDeclDeleted(Dtor, CXXRecord->getLocation()); + } + if (Record->hasAttrs()) { CheckAlignasUnderalignment(Record); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 0f7f0ffa75a..36a0338d4ae 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -6755,7 +6755,7 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM, DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Delete); if (FindDeallocationFunction(MD->getLocation(), MD->getParent(), Name, - OperatorDelete, false)) { + OperatorDelete, /*Diagnose*/false)) { if (Diagnose) Diag(RD->getLocation(), diag::note_deleted_dtor_no_operator_delete); return true; @@ -7695,19 +7695,11 @@ bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) { Loc = RD->getLocation(); // If we have a virtual destructor, look up the deallocation function - FunctionDecl *OperatorDelete = nullptr; - DeclarationName Name = - Context.DeclarationNames.getCXXOperatorName(OO_Delete); - if (FindDeallocationFunction(Loc, RD, Name, OperatorDelete)) - return true; - // If there's no class-specific operator delete, look up the global - // non-array delete. - if (!OperatorDelete) - OperatorDelete = FindUsualDeallocationFunction(Loc, true, Name); - - MarkFunctionReferenced(Loc, OperatorDelete); - - Destructor->setOperatorDelete(OperatorDelete); + if (FunctionDecl *OperatorDelete = + FindDeallocationFunctionForDestructor(Loc, RD)) { + MarkFunctionReferenced(Loc, OperatorDelete); + Destructor->setOperatorDelete(OperatorDelete); + } } return false; @@ -10280,7 +10272,11 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { Scope *S = getScopeForContext(ClassDecl); CheckImplicitSpecialMemberDeclaration(S, Destructor); - if (ShouldDeleteSpecialMember(Destructor, CXXDestructor)) + // We can't check whether an implicit destructor is deleted before we complete + // the definition of the class, because its validity depends on the alignment + // of the class. We'll check this from ActOnFields once the class is complete. + if (ClassDecl->isCompleteDefinition() && + ShouldDeleteSpecialMember(Destructor, CXXDestructor)) SetDeclDeleted(Destructor, ClassLoc); // Introduce this destructor into its scope. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index ad102f8d203..9c5dba5a38a 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1321,8 +1321,126 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, return Result; } -/// doesUsualArrayDeleteWantSize - Answers whether the usual -/// operator delete[] for the given type has a size_t parameter. +/// \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). static bool doesUsualArrayDeleteWantSize(Sema &S, SourceLocation loc, QualType allocType) { const RecordType *record = @@ -1346,35 +1464,13 @@ 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; - 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); + // 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; } /// \brief Parsed a C++ 'new' expression (C++ 5.3.4). @@ -1730,21 +1826,26 @@ 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, PlacementArgs, - OperatorNew, OperatorDelete)) + UseGlobal, AllocType, ArraySize, PassAlignment, + 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) { @@ -1755,9 +1856,11 @@ 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. - if (GatherArgumentsForCall(PlacementLParen, OperatorNew, Proto, 1, - PlacementArgs, AllPlaceArgs, CallType)) + // 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)) return ExprError(); if (!AllPlaceArgs.empty()) @@ -1767,21 +1870,18 @@ 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 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) + // 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) Diag(StartLoc, diag::warn_overaligned_type) << AllocType - << unsigned(Align / Context.getCharWidth()) - << unsigned(SuitableAlign / Context.getCharWidth()); + << unsigned(Alignment / Context.getCharWidth()) + << unsigned(NewAlignment / Context.getCharWidth()); } } @@ -1880,7 +1980,7 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, } return new (Context) - CXXNewExpr(Context, UseGlobal, OperatorNew, OperatorDelete, + CXXNewExpr(Context, UseGlobal, OperatorNew, OperatorDelete, PassAlignment, UsualArrayDeleteWantsSize, PlacementArgs, TypeIdParens, ArraySize, initStyle, Initializer, ResultType, AllocTypeInfo, Range, DirectInitRange); @@ -1923,32 +2023,128 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc, return false; } -/// \brief Determine whether the given function is a non-placement -/// deallocation function. -static bool isNonPlacementDeallocationFunction(Sema &S, FunctionDecl *FD) { - if (FD->isInvalidDecl()) - 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 (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FD)) - return Method->isUsualDeallocationFunction(); + if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) { + S.AddTemplateOverloadCandidate(FnTemplate, Alloc.getPair(), + /*ExplicitTemplateArgs=*/nullptr, Args, + Candidates, + /*SuppressUserConversions=*/false); + continue; + } - if (FD->getOverloadedOperator() != OO_Delete && - FD->getOverloadedOperator() != OO_Array_Delete) + 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; return false; + } - if (FD->getNumParams() == 1) + 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); + } + + 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; - return S.getLangOpts().SizedDeallocation && FD->getNumParams() == 2 && - S.Context.hasSameUnqualifiedType(FD->getParamDecl(1)->getType(), - S.Context.getSizeType()); + 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"); } + /// 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, MultiExprArg PlaceArgs, + bool IsArray, bool &PassAlignment, + MultiExprArg PlaceArgs, FunctionDecl *&OperatorNew, FunctionDecl *&OperatorDelete) { // --- Choosing an allocation function --- @@ -1960,16 +2156,29 @@ 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(1 + PlaceArgs.size()); - // We don't care about the actual value of this argument. + SmallVector<Expr*, 8> AllocArgs; + AllocArgs.reserve((PassAlignment ? 2 : 1) + PlaceArgs.size()); + + // We don't care about the actual value of these arguments. // 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[0] = &Size; - std::copy(PlaceArgs.begin(), PlaceArgs.end(), AllocArgs.begin() + 1); + 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()); // C++ [expr.new]p8: // If the allocated type is a non-array type, the allocation @@ -1978,50 +2187,57 @@ 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); - DeclarationName DeleteName = Context.DeclarationNames.getCXXOperatorName( - IsArray ? OO_Array_Delete : OO_Delete); + IsArray ? OO_Array_New : OO_New); QualType AllocElemType = Context.getBaseElementType(AllocType); - if (AllocElemType->isRecordType() && !UseGlobal) { - CXXRecordDecl *Record - = cast<CXXRecordDecl>(AllocElemType->getAs<RecordType>()->getDecl()); - if (FindAllocationOverload(StartLoc, Range, NewName, AllocArgs, Record, - /*AllowMissing=*/true, OperatorNew)) + // 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()) return true; - } - 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 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"); - // 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)) + // We do our own custom access checks below. + R.suppressDiagnostics(); + + if (resolveAllocationOverload(*this, R, Range, AllocArgs, PassAlignment, + 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 @@ -2040,6 +2256,7 @@ 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()); @@ -2054,7 +2271,16 @@ 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() - bool isPlacementNew = (!PlaceArgs.empty() || OperatorNew->param_size() != 1); + // + // 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(); if (isPlacementNew) { // C++ [expr.new]p20: @@ -2080,7 +2306,9 @@ 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); @@ -2104,35 +2332,29 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, if (Context.hasSameType(Fn->getType(), ExpectedFunctionType)) Matches.push_back(std::make_pair(D.getPair(), Fn)); } - } 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)); - } + if (getLangOpts().CUDA) + EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(CurContext), Matches); + } else { // C++1y [expr.new]p22: // For a non-placement allocation function, the normal deallocation // function lookup is used - // 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"); + // + // 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)); } } @@ -2143,130 +2365,58 @@ bool Sema::FindAllocationFunctions(SourceLocation StartLoc, SourceRange Range, if (Matches.size() == 1) { OperatorDelete = Matches[0].second; - // 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 + // 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 // as a placement deallocation function, would have been // selected as a match for the allocation function, the program // is ill-formed. - if (!PlaceArgs.empty() && getLangOpts().CPlusPlus11 && + if (getLangOpts().CPlusPlus11 && isPlacementNew && isNonPlacementDeallocationFunction(*this, OperatorDelete)) { - 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); - } - } - - 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(); + 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; + } - if (FunctionTemplateDecl *FnTemplate = dyn_cast<FunctionTemplateDecl>(D)) { - AddTemplateOverloadCandidate(FnTemplate, Alloc.getPair(), - /*ExplicitTemplateArgs=*/nullptr, - Args, Candidates, - /*SuppressUserConversions=*/false); - continue; + 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; + } } - FunctionDecl *Fn = cast<FunctionDecl>(D); - AddOverloadCandidate(Fn, Alloc.getPair(), Args, Candidates, - /*SuppressUserConversions=*/false); - } - - // 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; + 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; - Operator = FnDecl; - return false; + for (auto &Match : Matches) + Diag(Match.second->getLocation(), + diag::note_member_declared_here) << DeleteName; } - 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"); + return false; } - /// DeclareGlobalNewDelete - Declare the global forms of operator new and /// delete. These are: /// @code @@ -2460,52 +2610,43 @@ 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()); - // 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"); - } + // 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; +} - if (getLangOpts().CUDA) - EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(CurContext), Matches); +FunctionDecl *Sema::FindDeallocationFunctionForDestructor(SourceLocation Loc, + CXXRecordDecl *RD) { + DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Delete); - assert(Matches.size() == 1 && - "unexpectedly have multiple usual deallocation functions"); - return Matches.front(); + 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); } 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); @@ -2515,27 +2656,20 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, Found.suppressDiagnostics(); - SmallVector<DeclAccessPair,4> Matches; - for (LookupResult::iterator F = Found.begin(), FEnd = Found.end(); - F != FEnd; ++F) { - NamedDecl *ND = (*F)->getUnderlyingDecl(); + bool Overaligned = hasNewExtendedAlignment(*this, Context.getRecordType(RD)); - // 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()); - } + // 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); - if (getLangOpts().CUDA) - EraseUnwantedCUDAMatches(dyn_cast<FunctionDecl>(CurContext), Matches); - - // There's exactly one suitable operator; pick it. + // If we could find an overload, use it. if (Matches.size() == 1) { - Operator = cast<CXXMethodDecl>(Matches[0]->getUnderlyingDecl()); + Operator = cast<CXXMethodDecl>(Matches[0].FD); + // FIXME: DiagnoseUseOfDecl? if (Operator->isDeleted()) { if (Diagnose) { Diag(StartLoc, diag::err_deleted_function_use); @@ -2545,21 +2679,21 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, } if (CheckAllocationAccess(StartLoc, SourceRange(), Found.getNamingClass(), - Matches[0], Diagnose) == AR_inaccessible) + Matches[0].Found, Diagnose) == AR_inaccessible) return true; return false; + } - // We found multiple suitable operators; complain about the ambiguity. - } else if (!Matches.empty()) { + // 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()) { if (Diagnose) { Diag(StartLoc, diag::err_ambiguous_suitable_delete_member_function_found) << Name << RD; - - for (SmallVectorImpl<DeclAccessPair>::iterator - F = Matches.begin(), FEnd = Matches.end(); F != FEnd; ++F) - Diag((*F)->getUnderlyingDecl()->getLocation(), - diag::note_member_declared_here) << Name; + for (auto &Match : Matches) + Diag(Match.FD->getLocation(), diag::note_member_declared_here) << Name; } return true; } @@ -2571,9 +2705,8 @@ bool Sema::FindDeallocationFunction(SourceLocation StartLoc, CXXRecordDecl *RD, Diag(StartLoc, diag::err_no_suitable_delete_member_function_found) << Name << RD; - for (LookupResult::iterator F = Found.begin(), FEnd = Found.end(); - F != FEnd; ++F) - Diag((*F)->getUnderlyingDecl()->getLocation(), + for (NamedDecl *D : Found) + Diag(D->getUnderlyingDecl()->getLocation(), diag::note_member_declared_here) << Name; } return true; @@ -2984,7 +3117,10 @@ 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 = (OperatorDelete->getNumParams() == 2); + UsualArrayDeleteWantsSize = + UsualDeallocFnInfo( + DeclAccessPair::make(OperatorDelete, AS_public)) + .HasSizeT; } if (!PointeeRD->hasIrrelevantDestructor()) @@ -3001,13 +3137,17 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, SourceLocation()); } - if (!OperatorDelete) + if (!OperatorDelete) { + bool IsComplete = isCompleteType(StartLoc, Pointee); + bool CanProvideSize = + IsComplete && (!ArrayForm || UsualArrayDeleteWantsSize || + Pointee.isDestructedType()); + bool Overaligned = hasNewExtendedAlignment(*this, Pointee); + // Look for a global declaration. - OperatorDelete = FindUsualDeallocationFunction( - StartLoc, isCompleteType(StartLoc, Pointee) && - (!ArrayForm || UsualArrayDeleteWantsSize || - Pointee.isDestructedType()), - DeleteName); + OperatorDelete = FindUsualDeallocationFunction(StartLoc, CanProvideSize, + Overaligned, DeleteName); + } MarkFunctionReferenced(StartLoc, OperatorDelete); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 7ca85a907ea..082a6f98ef2 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -10142,16 +10142,17 @@ static void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand, /// PrintOverloadCandidates - When overload resolution fails, prints /// diagnostic messages containing the candidates in the candidate /// set. -void OverloadCandidateSet::NoteCandidates(Sema &S, - OverloadCandidateDisplayKind OCD, - ArrayRef<Expr *> Args, - StringRef Opc, - SourceLocation OpLoc) { +void OverloadCandidateSet::NoteCandidates( + Sema &S, OverloadCandidateDisplayKind OCD, ArrayRef<Expr *> Args, + StringRef Opc, SourceLocation OpLoc, + llvm::function_ref<bool(OverloadCandidate &)> Filter) { // Sort the candidates by viability and position. Sorting directly would // be prohibitive, so we make a set of pointers and sort those. SmallVector<OverloadCandidate*, 32> Cands; if (OCD == OCD_AllCandidates) Cands.reserve(size()); for (iterator Cand = begin(), LastCand = end(); Cand != LastCand; ++Cand) { + if (!Filter(*Cand)) + continue; if (Cand->Viable) Cands.push_back(Cand); else if (OCD == OCD_AllCandidates) { |