diff options
author | George Burgess IV <george.burgess.iv@gmail.com> | 2015-12-02 21:58:08 +0000 |
---|---|---|
committer | George Burgess IV <george.burgess.iv@gmail.com> | 2015-12-02 21:58:08 +0000 |
commit | 3e3bb95b6951c313dd5dd1c099184c309a3952e2 (patch) | |
tree | 601f182e5509276b9319182864eac0e27a888b68 /clang/lib/Sema/SemaOverload.cpp | |
parent | ba904d4ecf66528be5dadd60665d5a979ce6b1a4 (diff) | |
download | bcm5719-llvm-3e3bb95b6951c313dd5dd1c099184c309a3952e2.tar.gz bcm5719-llvm-3e3bb95b6951c313dd5dd1c099184c309a3952e2.zip |
Add the `pass_object_size` attribute to clang.
`pass_object_size` is our way of enabling `__builtin_object_size` to
produce high quality results without requiring inlining to happen
everywhere.
A link to the design doc for this attribute is available at the
Differential review link below.
Differential Revision: http://reviews.llvm.org/D13263
llvm-svn: 254554
Diffstat (limited to 'clang/lib/Sema/SemaOverload.cpp')
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 192 |
1 files changed, 152 insertions, 40 deletions
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 4ac5a502819..645dbed21ba 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -38,6 +38,11 @@ using namespace clang; using namespace sema; +static bool functionHasPassObjectSizeParams(const FunctionDecl *FD) { + return std::any_of(FD->param_begin(), FD->param_end(), + std::mem_fn(&ParmVarDecl::hasAttr<PassObjectSizeAttr>)); +} + /// A convenience routine for creating a decayed reference to a function. static ExprResult CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl, @@ -60,12 +65,8 @@ CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl, DRE->setHadMultipleCandidates(true); S.MarkDeclRefReferenced(DRE); - - ExprResult E = DRE; - E = S.DefaultFunctionArrayConversion(E.get()); - if (E.isInvalid()) - return ExprError(); - return E; + return S.ImpCastExprToType(DRE, S.Context.getPointerType(DRE->getType()), + CK_FunctionToPointerDecay); } static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, @@ -1062,6 +1063,14 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old, return true; } + // Though pass_object_size is placed on parameters and takes an argument, we + // consider it to be a function-level modifier for the sake of function + // identity. Either the function has one or more parameters with + // pass_object_size or it doesn't. + if (functionHasPassObjectSizeParams(New) != + functionHasPassObjectSizeParams(Old)) + return true; + // enable_if attributes are an order-sensitive part of the signature. for (specific_attr_iterator<EnableIfAttr> NewI = New->specific_attr_begin<EnableIfAttr>(), @@ -1548,6 +1557,11 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, // Function-to-pointer conversion (C++ 4.3). SCS.First = ICK_Function_To_Pointer; + if (auto *DRE = dyn_cast<DeclRefExpr>(From->IgnoreParenCasts())) + if (auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl())) + if (!S.checkAddressOfFunctionIsAvailable(FD)) + return false; + // An lvalue of function type T can be converted to an rvalue of // type "pointer to T." The result is a pointer to the // function. (C++ 4.3p1). @@ -2508,10 +2522,21 @@ enum { ft_parameter_arity, ft_parameter_mismatch, ft_return_type, - ft_qualifer_mismatch, - ft_addr_enable_if + ft_qualifer_mismatch }; +/// Attempts to get the FunctionProtoType from a Type. Handles +/// MemberFunctionPointers properly. +static const FunctionProtoType *tryGetFunctionProtoType(QualType FromType) { + if (auto *FPT = FromType->getAs<FunctionProtoType>()) + return FPT; + + if (auto *MPT = FromType->getAs<MemberPointerType>()) + return MPT->getPointeeType()->getAs<FunctionProtoType>(); + + return nullptr; +} + /// HandleFunctionTypeMismatch - Gives diagnostic information for differeing /// function types. Catches different number of parameter, mismatch in /// parameter types, and different return types. @@ -2558,8 +2583,8 @@ void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag, return; } - const FunctionProtoType *FromFunction = FromType->getAs<FunctionProtoType>(), - *ToFunction = ToType->getAs<FunctionProtoType>(); + const FunctionProtoType *FromFunction = tryGetFunctionProtoType(FromType), + *ToFunction = tryGetFunctionProtoType(ToType); // Both types need to be function types. if (!FromFunction || !ToFunction) { @@ -8572,7 +8597,11 @@ bool clang::isBetterOverloadCandidate(Sema &S, const OverloadCandidate &Cand1, S.IdentifyCUDAPreference(Caller, Cand2.Function); } - return false; + bool HasPS1 = Cand1.Function != nullptr && + functionHasPassObjectSizeParams(Cand1.Function); + bool HasPS2 = Cand2.Function != nullptr && + functionHasPassObjectSizeParams(Cand2.Function); + return HasPS1 != HasPS2 && HasPS1; } /// Determine whether two declarations are "equivalent" for the purposes of @@ -8642,9 +8671,6 @@ void Sema::diagnoseEquivalentInternalLinkageDeclarations( } } -static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand, - unsigned NumArgs); - /// \brief Computes the best viable function (C++ 13.3.3) /// within an overload candidate set. /// @@ -8794,17 +8820,75 @@ static bool isFunctionAlwaysEnabled(const ASTContext &Ctx, return true; } +/// \brief Returns true if we can take the address of the function. +/// +/// \param Complain - If true, we'll emit a diagnostic +/// \param InOverloadResolution - For the purposes of emitting a diagnostic, are +/// we in overload resolution? +/// \param Loc - The location of the statement we're complaining about. Ignored +/// if we're not complaining, or if we're in overload resolution. +static bool checkAddressOfFunctionIsAvailable(Sema &S, const FunctionDecl *FD, + bool Complain, + bool InOverloadResolution, + SourceLocation Loc) { + if (!isFunctionAlwaysEnabled(S.Context, FD)) { + if (Complain) { + // FIXME(gbiv): Both diagnostics below lack tests. We should add tests. + if (InOverloadResolution) + S.Diag(FD->getLocStart(), + diag::note_addrof_ovl_candidate_disabled_by_enable_if_attr); + else + S.Diag(Loc, diag::err_addrof_function_disabled_by_enable_if_attr) << FD; + } + return false; + } + + auto I = std::find_if(FD->param_begin(), FD->param_end(), + std::mem_fn(&ParmVarDecl::hasAttr<PassObjectSizeAttr>)); + if (I == FD->param_end()) + return true; + + if (Complain) { + // Add one to ParamNo because it's user-facing + unsigned ParamNo = std::distance(FD->param_begin(), I) + 1; + if (InOverloadResolution) + S.Diag(FD->getLocation(), + diag::note_ovl_candidate_has_pass_object_size_params) + << ParamNo; + else + S.Diag(Loc, diag::err_address_of_function_with_pass_object_size_params) + << FD << ParamNo; + } + return false; +} + +static bool checkAddressOfCandidateIsAvailable(Sema &S, + const FunctionDecl *FD) { + return checkAddressOfFunctionIsAvailable(S, FD, /*Complain=*/true, + /*InOverloadResolution=*/true, + /*Loc=*/SourceLocation()); +} + +bool Sema::checkAddressOfFunctionIsAvailable(const FunctionDecl *Function, + bool Complain, + SourceLocation Loc) { + return ::checkAddressOfFunctionIsAvailable(*this, Function, Complain, + /*InOverloadResolution=*/false, + Loc); +} + // Notes the location of an overload candidate. void Sema::NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType, bool TakingAddress) { + if (TakingAddress && !checkAddressOfCandidateIsAvailable(*this, Fn)) + return; + std::string FnDesc; OverloadCandidateKind K = ClassifyOverloadCandidate(*this, Fn, FnDesc); PartialDiagnostic PD = PDiag(diag::note_ovl_candidate) << (unsigned) K << FnDesc; - if (TakingAddress && !isFunctionAlwaysEnabled(Context, Fn)) - PD << ft_addr_enable_if; - else - HandleFunctionTypeMismatch(PD, Fn->getType(), DestType); + + HandleFunctionTypeMismatch(PD, Fn->getType(), DestType); Diag(Fn->getLocation(), PD); MaybeEmitInheritedConstructorNote(*this, Fn); } @@ -8858,7 +8942,7 @@ void ImplicitConversionSequence::DiagnoseAmbiguousConversion( } static void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, - unsigned I) { + unsigned I, bool TakingCandidateAddress) { const ImplicitConversionSequence &Conv = Cand->Conversions[I]; assert(Conv.isBad()); assert(Cand->Function && "for now, candidate must be a function"); @@ -9056,7 +9140,11 @@ static void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, return; } } - + + if (TakingCandidateAddress && + !checkAddressOfCandidateIsAvailable(S, Cand->Function)) + return; + // Emit the generic diagnostic and, optionally, add the hints to it. PartialDiagnostic FDiag = S.PDiag(diag::note_ovl_candidate_bad_conv); FDiag << (unsigned) FnKind << FnDesc @@ -9167,7 +9255,8 @@ static TemplateDecl *getDescribedTemplate(Decl *Templated) { /// Diagnose a failed template-argument deduction. static void DiagnoseBadDeduction(Sema &S, Decl *Templated, DeductionFailureInfo &DeductionFailure, - unsigned NumArgs) { + unsigned NumArgs, + bool TakingCandidateAddress) { TemplateParameter Param = DeductionFailure.getTemplateParameter(); NamedDecl *ParamD; (ParamD = Param.dyn_cast<TemplateTypeParmDecl*>()) || @@ -9335,6 +9424,11 @@ static void DiagnoseBadDeduction(Sema &S, Decl *Templated, } } } + + if (TakingCandidateAddress && isa<FunctionDecl>(Templated) && + !checkAddressOfCandidateIsAvailable(S, cast<FunctionDecl>(Templated))) + return; + // FIXME: For generic lambda parameters, check if the function is a lambda // call operator, and if so, emit a prettier and more informative // diagnostic that mentions 'auto' and lambda in addition to @@ -9355,14 +9449,15 @@ static void DiagnoseBadDeduction(Sema &S, Decl *Templated, /// Diagnose a failed template-argument deduction, for function calls. static void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand, - unsigned NumArgs) { + unsigned NumArgs, + bool TakingCandidateAddress) { unsigned TDK = Cand->DeductionFailure.Result; if (TDK == Sema::TDK_TooFewArguments || TDK == Sema::TDK_TooManyArguments) { if (CheckArityMismatch(S, Cand, NumArgs)) return; } DiagnoseBadDeduction(S, Cand->Function, // pattern - Cand->DeductionFailure, NumArgs); + Cand->DeductionFailure, NumArgs, TakingCandidateAddress); } /// CUDA: diagnose an invalid call across targets. @@ -9443,7 +9538,8 @@ static void DiagnoseFailedEnableIfAttr(Sema &S, OverloadCandidate *Cand) { /// more richly for those diagnostic clients that cared, but we'd /// still have to be just as careful with the default diagnostics. static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand, - unsigned NumArgs) { + unsigned NumArgs, + bool TakingCandidateAddress) { FunctionDecl *Fn = Cand->Function; // Note deleted candidates, but only if they're viable. @@ -9471,7 +9567,7 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand, return DiagnoseArityMismatch(S, Cand, NumArgs); case ovl_fail_bad_deduction: - return DiagnoseBadDeduction(S, Cand, NumArgs); + return DiagnoseBadDeduction(S, Cand, NumArgs, TakingCandidateAddress); case ovl_fail_illegal_constructor: { S.Diag(Fn->getLocation(), diag::note_ovl_candidate_illegal_constructor) @@ -9489,7 +9585,7 @@ static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand, unsigned I = (Cand->IgnoreObjectArgument ? 1 : 0); for (unsigned N = Cand->NumConversions; I != N; ++I) if (Cand->Conversions[I].isBad()) - return DiagnoseBadConversion(S, Cand, I); + return DiagnoseBadConversion(S, Cand, I, TakingCandidateAddress); // FIXME: this currently happens when we're called from SemaInit // when user-conversion overload fails. Figure out how to handle @@ -9860,7 +9956,8 @@ void OverloadCandidateSet::NoteCandidates(Sema &S, ++CandsShown; if (Cand->Function) - NoteFunctionCandidate(S, Cand, Args.size()); + NoteFunctionCandidate(S, Cand, Args.size(), + /*TakingCandidateAddress=*/false); else if (Cand->IsSurrogate) NoteSurrogateCandidate(S, Cand); else { @@ -9928,9 +10025,10 @@ struct CompareTemplateSpecCandidatesForDisplay { /// Diagnose a template argument deduction failure. /// We are treating these failures as overload failures due to bad /// deductions. -void TemplateSpecCandidate::NoteDeductionFailure(Sema &S) { +void TemplateSpecCandidate::NoteDeductionFailure(Sema &S, + bool ForTakingAddress) { DiagnoseBadDeduction(S, Specialization, // pattern - DeductionFailure, /*NumArgs=*/0); + DeductionFailure, /*NumArgs=*/0, ForTakingAddress); } void TemplateSpecCandidateSet::destroyCandidates() { @@ -9983,7 +10081,7 @@ void TemplateSpecCandidateSet::NoteCandidates(Sema &S, SourceLocation Loc) { assert(Cand->Specialization && "Non-matching built-in candidates are not added to Cands."); - Cand->NoteDeductionFailure(S); + Cand->NoteDeductionFailure(S, ForTakingAddress); } if (I != E) @@ -10048,7 +10146,7 @@ public: HasComplained(false), OvlExprInfo(OverloadExpr::find(SourceExpr)), OvlExpr(OvlExprInfo.Expression), - FailedCandidates(OvlExpr->getNameLoc()) { + FailedCandidates(OvlExpr->getNameLoc(), /*ForTakingAddress=*/true) { ExtractUnqualifiedFunctionTypeFromTargetType(); if (TargetFunctionType->isFunctionType()) { @@ -10182,10 +10280,9 @@ private: Specialization = cast<FunctionDecl>(Specialization->getCanonicalDecl()); assert(S.isSameOrCompatibleFunctionType( Context.getCanonicalType(Specialization->getType()), - Context.getCanonicalType(TargetFunctionType)) || - (!S.getLangOpts().CPlusPlus && TargetType->isVoidPointerType())); + Context.getCanonicalType(TargetFunctionType))); - if (!isFunctionAlwaysEnabled(S.Context, Specialization)) + if (!S.checkAddressOfFunctionIsAvailable(Specialization)) return false; Matches.push_back(std::make_pair(CurAccessFunPair, Specialization)); @@ -10218,7 +10315,7 @@ private: return false; } - if (!isFunctionAlwaysEnabled(S.Context, FunDecl)) + if (!S.checkAddressOfFunctionIsAvailable(FunDecl)) return false; QualType ResultTy; @@ -10341,8 +10438,9 @@ public: I != IEnd; ++I) if (FunctionDecl *Fun = dyn_cast<FunctionDecl>((*I)->getUnderlyingDecl())) - S.NoteOverloadCandidate(Fun, TargetFunctionType, - /*TakingAddress=*/true); + if (!functionHasPassObjectSizeParams(Fun)) + S.NoteOverloadCandidate(Fun, TargetFunctionType, + /*TakingAddress=*/true); FailedCandidates.NoteCandidates(S, OvlExpr->getLocStart()); } } @@ -11052,9 +11150,23 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn, if (!Recovery.isInvalid()) return Recovery; - SemaRef.Diag(Fn->getLocStart(), - diag::err_ovl_no_viable_function_in_call) - << ULE->getName() << Fn->getSourceRange(); + // If the user passes in a function that we can't take the address of, we + // generally end up emitting really bad error messages. Here, we attempt to + // emit better ones. + for (const Expr *Arg : Args) { + if (!Arg->getType()->isFunctionType()) + continue; + if (auto *DRE = dyn_cast<DeclRefExpr>(Arg->IgnoreParenImpCasts())) { + auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl()); + if (FD && + !SemaRef.checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true, + Arg->getExprLoc())) + return ExprError(); + } + } + + SemaRef.Diag(Fn->getLocStart(), diag::err_ovl_no_viable_function_in_call) + << ULE->getName() << Fn->getSourceRange(); CandidateSet->NoteCandidates(SemaRef, OCD_AllCandidates, Args); break; } |