Index: cfe/trunk/include/clang/AST/Expr.h =================================================================== --- cfe/trunk/include/clang/AST/Expr.h +++ cfe/trunk/include/clang/AST/Expr.h @@ -628,6 +628,16 @@ const FunctionDecl *Callee, ArrayRef Args) const; + /// \brief If the current Expr is a pointer, this will try to statically + /// determine the number of bytes available where the pointer is pointing. + /// Returns true if all of the above holds and we were able to figure out the + /// size, false otherwise. + /// + /// \param Type - How to evaluate the size of the Expr, as defined by the + /// "type" parameter of __builtin_object_size + bool tryEvaluateObjectSize(uint64_t &Result, ASTContext &Ctx, + unsigned Type) const; + /// \brief Enumeration used to describe the kind of Null pointer constant /// returned from \c isNullPointerConstant(). enum NullPointerConstantKind { Index: cfe/trunk/include/clang/Basic/Attr.td =================================================================== --- cfe/trunk/include/clang/Basic/Attr.td +++ cfe/trunk/include/clang/Basic/Attr.td @@ -1012,6 +1012,15 @@ let Documentation = [ReturnsNonNullDocs]; } +// pass_object_size(N) indicates that the parameter should have +// __builtin_object_size with Type=N evaluated on the parameter at the callsite. +def PassObjectSize : InheritableParamAttr { + let Spellings = [GNU<"pass_object_size">]; + let Args = [IntArgument<"Type">]; + let Subjects = SubjectList<[ParmVar]>; + let Documentation = [PassObjectSizeDocs]; +} + // Nullability type attributes. def TypeNonNull : TypeAttr { let Spellings = [Keyword<"_Nonnull">]; Index: cfe/trunk/include/clang/Basic/AttrDocs.td =================================================================== --- cfe/trunk/include/clang/Basic/AttrDocs.td +++ cfe/trunk/include/clang/Basic/AttrDocs.td @@ -263,6 +263,103 @@ }]; } +def PassObjectSizeDocs : Documentation { + let Category = DocCatVariable; // Technically it's a parameter doc, but eh. + let Content = [{ +.. Note:: The mangling of functions with parameters that are annotated with + ``pass_object_size`` is subject to change. You can get around this by + using ``__asm__("foo")`` to explicitly name your functions, thus preserving + your ABI; also, non-overloadable C functions with ``pass_object_size`` are + not mangled. + +The ``pass_object_size(Type)`` attribute can be placed on function parameters to +instruct clang to call ``__builtin_object_size(param, Type)`` at each callsite +of said function, and implicitly pass the result of this call in as an invisible +argument of type ``size_t`` directly after the parameter annotated with +``pass_object_size``. Clang will also replace any calls to +``__builtin_object_size(param, Type)`` in the function by said implicit +parameter. + +Example usage: + +.. code-block:: c + + int bzero1(char *const p __attribute__((pass_object_size(0)))) + __attribute__((noinline)) { + int i = 0; + for (/**/; i < (int)__builtin_object_size(p, 0); ++i) { + p[i] = 0; + } + return i; + } + + int main() { + char chars[100]; + int n = bzero1(&chars[0]); + assert(n == sizeof(chars)); + return 0; + } + +If successfully evaluating ``__builtin_object_size(param, Type)`` at the +callsite is not possible, then the "failed" value is passed in. So, using the +definition of ``bzero1`` from above, the following code would exit cleanly: + +.. code-block:: c + + int main2(int argc, char *argv[]) { + int n = bzero1(argv); + assert(n == -1); + return 0; + } + +``pass_object_size`` plays a part in overload resolution. If two overload +candidates are otherwise equally good, then the overload with one or more +parameters with ``pass_object_size`` is preferred. This implies that the choice +between two identical overloads both with ``pass_object_size`` on one or more +parameters will always be ambiguous; for this reason, having two such overloads +is illegal. For example: + +.. code-block:: c++ + + #define PS(N) __attribute__((pass_object_size(N))) + // OK + void Foo(char *a, char *b); // Overload A + // OK -- overload A has no parameters with pass_object_size. + void Foo(char *a PS(0), char *b PS(0)); // Overload B + // Error -- Same signature (sans pass_object_size) as overload B, and both + // overloads have one or more parameters with the pass_object_size attribute. + void Foo(void *a PS(0), void *b); + + // OK + void Bar(void *a PS(0)); // Overload C + // OK + void Bar(char *c PS(1)); // Overload D + + void main() { + char known[10], *unknown; + Foo(unknown, unknown); // Calls overload B + Foo(known, unknown); // Calls overload B + Foo(unknown, known); // Calls overload B + Foo(known, known); // Calls overload B + + Bar(known); // Calls overload D + Bar(unknown); // Calls overload D + } + +Currently, ``pass_object_size`` is a bit restricted in terms of its usage: + +* Only one use of ``pass_object_size`` is allowed per parameter. + +* It is an error to take the address of a function with ``pass_object_size`` on + any of its parameters. If you wish to do this, you can create an overload + without ``pass_object_size`` on any parameters. + +* It is an error to apply the ``pass_object_size`` attribute to parameters that + are not pointers. Additionally, any parameter that ``pass_object_size`` is + applied to must be marked ``const`` at its function's definition. + }]; +} + def OverloadableDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td @@ -1567,8 +1567,7 @@ "%select{none|const|restrict|const and restrict|volatile|const and volatile|" "volatile and restrict|const, volatile, and restrict}5 vs " "%select{none|const|restrict|const and restrict|volatile|const and volatile|" - "volatile and restrict|const, volatile, and restrict}6)" - "|: cannot take the address of a potentially disabled function}4">; + "volatile and restrict|const, volatile, and restrict}6)}4">; def err_lvalue_to_rvalue_ref : Error<"rvalue reference %diff{to type $ cannot " "bind to lvalue of type $|cannot bind to incompatible lvalue}0,1">; @@ -2126,8 +2125,7 @@ "%0 attribute requires %select{int or bool|an integer " "constant|a string|an identifier}1">; def err_attribute_argument_outof_range : Error< - "init_priority attribute requires integer constant between " - "101 and 65535 inclusive">; + "%0 attribute requires integer constant between %1 and %2 inclusive">; def err_init_priority_object_attr : Error< "can only use 'init_priority' attribute on file-scope definitions " "of objects of class type">; @@ -2135,10 +2133,12 @@ "invalid attribute argument %0 - expecting a vector or vectorizable scalar type">; def err_attribute_argument_out_of_bounds : Error< "%0 attribute parameter %1 is out of bounds">; +def err_attribute_only_once_per_parameter : Error< + "%0 attribute can only be applied once per parameter">; def err_attribute_uuid_malformed_guid : Error< "uuid attribute contains a malformed GUID">; def warn_attribute_pointers_only : Warning< - "%0 attribute only applies to pointer arguments">, + "%0 attribute only applies to%select{| constant}1 pointer arguments">, InGroup; def err_attribute_pointers_only : Error; def warn_attribute_return_pointers_only : Warning< @@ -3063,8 +3063,7 @@ "%select{none|const|restrict|const and restrict|volatile|const and volatile" "|volatile and restrict|const, volatile, and restrict}3 but found " "%select{none|const|restrict|const and restrict|volatile|const and volatile" - "|volatile and restrict|const, volatile, and restrict}4)" - "| made ineligible by enable_if}2">; + "|volatile and restrict|const, volatile, and restrict}4)}2">; def note_ovl_candidate_inherited_constructor : Note<"inherited from here">; def note_ovl_candidate_illegal_constructor : Note< @@ -3093,8 +3092,16 @@ "candidate template ignored: substitution failure%0%1">; def note_ovl_candidate_disabled_by_enable_if : Note< "candidate template ignored: disabled by %0%1">; +def note_ovl_candidate_has_pass_object_size_params: Note< + "candidate address cannot be taken because parameter %0 has " + "pass_object_size attribute">; def note_ovl_candidate_disabled_by_enable_if_attr : Note< "candidate disabled: %0">; +def err_addrof_function_disabled_by_enable_if_attr : Error< + "cannot take address of function %0 becuase it has one or more " + "non-tautological enable_if conditions">; +def note_addrof_ovl_candidate_disabled_by_enable_if_attr : Note< + "candidate function made ineligible by enable_if">; def note_ovl_candidate_failed_overload_resolution : Note< "candidate template ignored: couldn't resolve reference to overloaded " "function %0">; @@ -3105,7 +3112,7 @@ // can handle that case properly. def note_ovl_candidate_non_deduced_mismatch_qualified : Note< "candidate template ignored: could not match %q0 against %q1">; - + // Note that we don't treat templates differently for this diagnostic. def note_ovl_candidate_arity : Note<"candidate " "%select{function|function|constructor|function|function|constructor|" @@ -4245,6 +4252,8 @@ def err_tag_definition_of_typedef : Error< "definition of type %0 conflicts with %select{typedef|type alias}1 of the same name">; def err_conflicting_types : Error<"conflicting types for %0">; +def err_different_pass_object_size_params : Error< + "conflicting pass_object_size attributes on parameters">; def err_nested_redefinition : Error<"nested redefinition of %0">; def err_use_with_wrong_tag : Error< "use of %0 with tag type that does not match previous declaration">; @@ -5209,6 +5218,9 @@ "must explicitly qualify name of member function when taking its address">; def err_invalid_form_pointer_member_function : Error< "cannot create a non-constant pointer to member function">; +def err_address_of_function_with_pass_object_size_params: Error< + "cannot take address of function %0 because parameter %1 has " + "pass_object_size attribute">; def err_parens_pointer_member_function : Error< "cannot parenthesize the name of a method when forming a member pointer">; def err_typecheck_invalid_lvalue_addrof_addrof_function : Error< @@ -5793,8 +5805,7 @@ "%select{none|const|restrict|const and restrict|volatile|const and volatile|" "volatile and restrict|const, volatile, and restrict}2 vs " "%select{none|const|restrict|const and restrict|volatile|const and volatile|" - "volatile and restrict|const, volatile, and restrict}3)" - "|: mismatch in enable_if attributes}1">; + "volatile and restrict|const, volatile, and restrict}3)}1">; def warn_using_directive_in_header : Warning< "using namespace directive in global context in header">, InGroup, DefaultIgnore; @@ -6031,8 +6042,7 @@ "%select{none|const|restrict|const and restrict|volatile|const and volatile|" "volatile and restrict|const, volatile, and restrict}5 vs " "%select{none|const|restrict|const and restrict|volatile|const and volatile|" - "volatile and restrict|const, volatile, and restrict}6)" - "|: cannot take the address of a potentially disabled function}4">; + "volatile and restrict|const, volatile, and restrict}6)}4">; def err_typecheck_missing_return_type_incompatible : Error< "%diff{return type $ must match previous return type $|" "return type must match previous return type}0,1 when %select{block " Index: cfe/trunk/include/clang/CodeGen/CodeGenABITypes.h =================================================================== --- cfe/trunk/include/clang/CodeGen/CodeGenABITypes.h +++ cfe/trunk/include/clang/CodeGen/CodeGenABITypes.h @@ -36,6 +36,7 @@ namespace clang { class ASTContext; class CXXRecordDecl; +class CXXMethodDecl; class CodeGenOptions; class CoverageSourceInfo; class DiagnosticsEngine; @@ -60,12 +61,13 @@ const CGFunctionInfo &arrangeObjCMessageSendSignature( const ObjCMethodDecl *MD, QualType receiverType); - const CGFunctionInfo &arrangeFreeFunctionType( - CanQual Ty); + const CGFunctionInfo &arrangeFreeFunctionType(CanQual Ty, + const FunctionDecl *FD); const CGFunctionInfo &arrangeFreeFunctionType( CanQual Ty); const CGFunctionInfo &arrangeCXXMethodType(const CXXRecordDecl *RD, - const FunctionProtoType *FTP); + const FunctionProtoType *FTP, + const CXXMethodDecl *MD); const CGFunctionInfo &arrangeFreeFunctionCall(CanQualType returnType, ArrayRef argTypes, FunctionType::ExtInfo info, Index: cfe/trunk/include/clang/Sema/Initialization.h =================================================================== --- cfe/trunk/include/clang/Sema/Initialization.h +++ cfe/trunk/include/clang/Sema/Initialization.h @@ -828,6 +828,9 @@ /// \brief Initializer has a placeholder type which cannot be /// resolved by initialization. FK_PlaceholderType, + /// \brief Trying to take the address of a function that doesn't support + /// having its address taken. + FK_AddressOfUnaddressableFunction, /// \brief List-copy-initialization chose an explicit constructor. FK_ExplicitConstructor }; Index: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -145,6 +145,7 @@ class ObjCProtocolDecl; class OMPThreadPrivateDecl; class OMPClause; + struct OverloadCandidate; class OverloadCandidateSet; class OverloadExpr; class ParenListExpr; @@ -2475,6 +2476,14 @@ EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef Args, bool MissingImplicitThis = false); + /// Returns whether the given function's address can be taken or not, + /// optionally emitting a diagnostic if the address can't be taken. + /// + /// Returns false if taking the address of the function is illegal. + bool checkAddressOfFunctionIsAvailable(const FunctionDecl *Function, + bool Complain = false, + SourceLocation Loc = SourceLocation()); + // [PossiblyAFunctionType] --> [Return] // NonFunctionType --> NonFunctionType // R (A) --> R(A) @@ -8187,12 +8196,13 @@ // DefaultFunctionArrayConversion - converts functions and arrays // to their respective pointers (C99 6.3.2.1). - ExprResult DefaultFunctionArrayConversion(Expr *E); + ExprResult DefaultFunctionArrayConversion(Expr *E, bool Diagnose = true); // DefaultFunctionArrayLvalueConversion - converts functions and // arrays to their respective pointers and performs the // lvalue-to-rvalue conversion. - ExprResult DefaultFunctionArrayLvalueConversion(Expr *E); + ExprResult DefaultFunctionArrayLvalueConversion(Expr *E, + bool Diagnose = true); // DefaultLvalueConversion - performs lvalue-to-rvalue conversion on // the operand. This is DefaultFunctionArrayLvalueConversion, Index: cfe/trunk/include/clang/Sema/TemplateDeduction.h =================================================================== --- cfe/trunk/include/clang/Sema/TemplateDeduction.h +++ cfe/trunk/include/clang/Sema/TemplateDeduction.h @@ -236,7 +236,7 @@ } /// Diagnose a template argument deduction failure. - void NoteDeductionFailure(Sema &S); + void NoteDeductionFailure(Sema &S, bool ForTakingAddress); }; /// TemplateSpecCandidateSet - A set of generalized overload candidates, @@ -246,6 +246,10 @@ class TemplateSpecCandidateSet { SmallVector Candidates; SourceLocation Loc; + // Stores whether we're taking the address of these candidates. This helps us + // produce better error messages when dealing with the pass_object_size + // attribute on parameters. + bool ForTakingAddress; TemplateSpecCandidateSet( const TemplateSpecCandidateSet &) = delete; @@ -254,7 +258,8 @@ void destroyCandidates(); public: - TemplateSpecCandidateSet(SourceLocation Loc) : Loc(Loc) {} + TemplateSpecCandidateSet(SourceLocation Loc, bool ForTakingAddress = false) + : Loc(Loc), ForTakingAddress(ForTakingAddress) {} ~TemplateSpecCandidateSet() { destroyCandidates(); } SourceLocation getLocation() const { return Loc; } Index: cfe/trunk/lib/AST/ExprConstant.cpp =================================================================== --- cfe/trunk/lib/AST/ExprConstant.cpp +++ cfe/trunk/lib/AST/ExprConstant.cpp @@ -1156,6 +1156,7 @@ static bool EvaluateFloat(const Expr *E, APFloat &Result, EvalInfo &Info); static bool EvaluateComplex(const Expr *E, ComplexValue &Res, EvalInfo &Info); static bool EvaluateAtomic(const Expr *E, APValue &Result, EvalInfo &Info); +static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result); //===----------------------------------------------------------------------===// // Misc utilities @@ -6377,8 +6378,28 @@ return false; } -bool IntExprEvaluator::TryEvaluateBuiltinObjectSize(const CallExpr *E, - unsigned Type) { +/// Tries to evaluate the __builtin_object_size for @p E. If successful, returns +/// true and stores the result in @p Size. +/// +/// If @p WasError is non-null, this will report whether the failure to evaluate +/// is to be treated as an Error in IntExprEvaluator. +static bool tryEvaluateBuiltinObjectSize(const Expr *E, unsigned Type, + EvalInfo &Info, uint64_t &Size, + bool *WasError = nullptr) { + if (WasError != nullptr) + *WasError = false; + + auto Error = [&](const Expr *E) { + if (WasError != nullptr) + *WasError = true; + return false; + }; + + auto Success = [&](uint64_t S, const Expr *E) { + Size = S; + return true; + }; + // Determine the denoted object. LValue Base; { @@ -6387,8 +6408,15 @@ // ignore the side-effects. SpeculativeEvaluationRAII SpeculativeEval(Info); FoldOffsetRAII Fold(Info, Type & 1); - const Expr *Ptr = ignorePointerCastsAndParens(E->getArg(0)); - if (!EvaluatePointer(Ptr, Base, Info)) + + if (E->isGLValue()) { + // It's possible for us to be given GLValues if we're called via + // Expr::tryEvaluateObjectSize. + APValue RVal; + if (!EvaluateAsRValue(Info, E, RVal)) + return false; + Base.setFrom(Info.Ctx, RVal); + } else if (!EvaluatePointer(ignorePointerCastsAndParens(E), Base, Info)) return false; } @@ -6447,7 +6475,7 @@ End.Designator.Entries.size() == End.Designator.MostDerivedPathLength) { // We got a pointer to an array. Step to its end. AmountToAdd = End.Designator.MostDerivedArraySize - - End.Designator.Entries.back().ArrayIndex; + End.Designator.Entries.back().ArrayIndex; } else if (End.Designator.isOnePastTheEnd()) { // We're already pointing at the end of the object. AmountToAdd = 0; @@ -6484,7 +6512,18 @@ if (BaseOffset > EndOffset) return Success(0, E); - return Success(EndOffset - BaseOffset, E); + return Success((EndOffset - BaseOffset).getQuantity(), E); +} + +bool IntExprEvaluator::TryEvaluateBuiltinObjectSize(const CallExpr *E, + unsigned Type) { + uint64_t Size; + bool WasError; + if (::tryEvaluateBuiltinObjectSize(E->getArg(0), Type, Info, Size, &WasError)) + return Success(Size, E); + if (WasError) + return Error(E); + return false; } bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) { @@ -6501,12 +6540,7 @@ if (TryEvaluateBuiltinObjectSize(E, Type)) return true; - // If evaluating the argument has side-effects, we can't determine the size - // of the object, and so we lower it to unknown now. CodeGen relies on us to - // handle all cases where the expression has side-effects. - // Likewise, if Type is 3, we must handle this because CodeGen cannot give a - // conservatively correct answer in that case. - if (E->getArg(0)->HasSideEffects(Info.Ctx) || Type == 3) + if (E->getArg(0)->HasSideEffects(Info.Ctx)) return Success((Type & 2) ? 0 : -1, E); // Expression had no side effects, but we couldn't statically determine the @@ -9483,3 +9517,13 @@ Evaluate(ResultScratch, Info, E); return Diags.empty(); } + +bool Expr::tryEvaluateObjectSize(uint64_t &Result, ASTContext &Ctx, + unsigned Type) const { + if (!getType()->isPointerType()) + return false; + + Expr::EvalStatus Status; + EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold); + return ::tryEvaluateBuiltinObjectSize(this, Type, Info, Result); +} Index: cfe/trunk/lib/AST/ItaniumMangle.cpp =================================================================== --- cfe/trunk/lib/AST/ItaniumMangle.cpp +++ cfe/trunk/lib/AST/ItaniumMangle.cpp @@ -377,8 +377,8 @@ void mangleType(const TagType*); void mangleType(TemplateName); - void mangleBareFunctionType(const FunctionType *T, - bool MangleReturnType); + void mangleBareFunctionType(const FunctionType *T, bool MangleReturnType, + const FunctionDecl *FD = nullptr); void mangleNeonVectorType(const VectorType *T); void mangleAArch64NeonVectorType(const VectorType *T); @@ -523,7 +523,7 @@ } mangleBareFunctionType(FD->getType()->getAs(), - MangleReturnType); + MangleReturnType, FD); } static const DeclContext *IgnoreLinkageSpecDecls(const DeclContext *DC) { @@ -1282,7 +1282,8 @@ Out << "Ul"; const FunctionProtoType *Proto = Lambda->getLambdaTypeInfo()->getType()-> getAs(); - mangleBareFunctionType(Proto, /*MangleReturnType=*/false); + mangleBareFunctionType(Proto, /*MangleReturnType=*/false, + Lambda->getLambdaStaticInvoker()); Out << "E"; // The number is omitted for the first closure type with a given @@ -2171,7 +2172,8 @@ } void CXXNameMangler::mangleBareFunctionType(const FunctionType *T, - bool MangleReturnType) { + bool MangleReturnType, + const FunctionDecl *FD) { // We should never be mangling something without a prototype. const FunctionProtoType *Proto = cast(T); @@ -2194,8 +2196,19 @@ return; } - for (const auto &Arg : Proto->param_types()) - mangleType(Context.getASTContext().getSignatureParameterType(Arg)); + assert(!FD || FD->getNumParams() == Proto->getNumParams()); + for (unsigned I = 0, E = Proto->getNumParams(); I != E; ++I) { + const auto &ParamTy = Proto->getParamType(I); + mangleType(Context.getASTContext().getSignatureParameterType(ParamTy)); + + if (FD) { + if (auto *Attr = FD->getParamDecl(I)->getAttr()) { + // Attr can only take 1 character, so we can hardcode the length below. + assert(Attr->getType() <= 9 && Attr->getType() >= 0); + Out << "U17pass_object_size" << Attr->getType(); + } + } + } FunctionTypeDepth.pop(saved); @@ -4228,4 +4241,3 @@ ItaniumMangleContext::create(ASTContext &Context, DiagnosticsEngine &Diags) { return new ItaniumMangleContextImpl(Context, Diags); } - Index: cfe/trunk/lib/AST/MicrosoftMangle.cpp =================================================================== --- cfe/trunk/lib/AST/MicrosoftMangle.cpp +++ cfe/trunk/lib/AST/MicrosoftMangle.cpp @@ -1841,8 +1841,20 @@ Out << 'X'; } else { // Happens for function pointer type arguments for example. - for (const QualType &Arg : Proto->param_types()) - mangleArgumentType(Arg, Range); + for (unsigned I = 0, E = Proto->getNumParams(); I != E; ++I) { + mangleArgumentType(Proto->getParamType(I), Range); + // Mangle each pass_object_size parameter as if it's a paramater of enum + // type passed directly after the parameter with the pass_object_size + // attribute. The aforementioned enum's name is __pass_object_size, and we + // pretend it resides in a top-level namespace called __clang. + // + // FIXME: Is there a defined extension notation for the MS ABI, or is it + // necessary to just cross our fingers and hope this type+namespace + // combination doesn't conflict with anything? + if (D) + if (auto *P = D->getParamDecl(I)->getAttr()) + Out << "W4__pass_object_size" << P->getType() << "@__clang@@"; + } // ::= Z # ellipsis if (Proto->isVariadic()) Out << 'Z'; Index: cfe/trunk/lib/CodeGen/CGBuiltin.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGBuiltin.cpp +++ cfe/trunk/lib/CodeGen/CGBuiltin.cpp @@ -342,6 +342,71 @@ return Builder.CreateCall(CGM.getIntrinsic(inst), ArgValue); } +/// Checks if using the result of __builtin_object_size(p, @p From) in place of +/// __builtin_object_size(p, @p To) is correct +static bool areBOSTypesCompatible(int From, int To) { + // Note: Our __builtin_object_size implementation currently treats Type=0 and + // Type=2 identically. Encoding this implementation detail here may make + // improving __builtin_object_size difficult in the future, so it's omitted. + return From == To || (From == 0 && To == 1) || (From == 3 && To == 2); +} + +static llvm::Value * +getDefaultBuiltinObjectSizeResult(unsigned Type, llvm::IntegerType *ResType) { + return ConstantInt::get(ResType, (Type & 2) ? 0 : -1, /*isSigned=*/true); +} + +llvm::Value * +CodeGenFunction::evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type, + llvm::IntegerType *ResType) { + uint64_t ObjectSize; + if (!E->tryEvaluateObjectSize(ObjectSize, getContext(), Type)) + return emitBuiltinObjectSize(E, Type, ResType); + return ConstantInt::get(ResType, ObjectSize, /*isSigned=*/true); +} + +/// Returns a Value corresponding to the size of the given expression. +/// This Value may be either of the following: +/// - A llvm::Argument (if E is a param with the pass_object_size attribute on +/// it) +/// - A call to the @llvm.objectsize intrinsic +llvm::Value * +CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type, + llvm::IntegerType *ResType) { + // We need to reference an argument if the pointer is a parameter with the + // pass_object_size attribute. + if (auto *D = dyn_cast(E->IgnoreParenImpCasts())) { + auto *Param = dyn_cast(D->getDecl()); + auto *PS = D->getDecl()->getAttr(); + if (Param != nullptr && PS != nullptr && + areBOSTypesCompatible(PS->getType(), Type)) { + auto Iter = SizeArguments.find(Param); + assert(Iter != SizeArguments.end()); + + const ImplicitParamDecl *D = Iter->second; + auto DIter = LocalDeclMap.find(D); + assert(DIter != LocalDeclMap.end()); + + return EmitLoadOfScalar(DIter->second, /*volatile=*/false, + getContext().getSizeType(), E->getLocStart()); + } + } + + // LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't + // evaluate E for side-effects. In either case, we shouldn't lower to + // @llvm.objectsize. + if (Type == 3 || E->HasSideEffects(getContext())) + return getDefaultBuiltinObjectSizeResult(Type, ResType); + + // LLVM only supports 0 and 2, make sure that we pass along that + // as a boolean. + auto *CI = ConstantInt::get(Builder.getInt1Ty(), (Type & 2) >> 1); + // FIXME: Get right address space. + llvm::Type *Tys[] = {ResType, Builder.getInt8PtrTy(0)}; + Value *F = CGM.getIntrinsic(Intrinsic::objectsize, Tys); + return Builder.CreateCall(F, {EmitScalarExpr(E), CI}); +} + RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, unsigned BuiltinID, const CallExpr *E, ReturnValueSlot ReturnValue) { @@ -586,26 +651,13 @@ return RValue::get(Builder.CreateCall(F, ArgValue)); } case Builtin::BI__builtin_object_size: { - // We rely on constant folding to deal with expressions with side effects. - assert(!E->getArg(0)->HasSideEffects(getContext()) && - "should have been constant folded"); - - // We pass this builtin onto the optimizer so that it can - // figure out the object size in more complex cases. - llvm::Type *ResType = ConvertType(E->getType()); - - // LLVM only supports 0 and 2, make sure that we pass along that - // as a boolean. - Value *Ty = EmitScalarExpr(E->getArg(1)); - ConstantInt *CI = dyn_cast(Ty); - assert(CI); - uint64_t val = CI->getZExtValue(); - CI = ConstantInt::get(Builder.getInt1Ty(), (val & 0x2) >> 1); - // FIXME: Get right address space. - llvm::Type *Tys[] = { ResType, Builder.getInt8PtrTy(0) }; - Value *F = CGM.getIntrinsic(Intrinsic::objectsize, Tys); - return RValue::get( - Builder.CreateCall(F, {EmitScalarExpr(E->getArg(0)), CI})); + unsigned Type = + E->getArg(1)->EvaluateKnownConstInt(getContext()).getZExtValue(); + auto *ResType = cast(ConvertType(E->getType())); + + // We pass this builtin onto the optimizer so that it can figure out the + // object size in more complex cases. + return RValue::get(emitBuiltinObjectSize(E->getArg(0), Type, ResType)); } case Builtin::BI__builtin_prefetch: { Value *Locality, *RW, *Address = EmitScalarExpr(E->getArg(0)); Index: cfe/trunk/lib/CodeGen/CGCXXABI.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGCXXABI.cpp +++ cfe/trunk/lib/CodeGen/CGCXXABI.cpp @@ -85,7 +85,7 @@ const CXXRecordDecl *RD = cast(MPT->getClass()->getAs()->getDecl()); llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType( - CGM.getTypes().arrangeCXXMethodType(RD, FPT)); + CGM.getTypes().arrangeCXXMethodType(RD, FPT, /*FD=*/nullptr)); return llvm::Constant::getNullValue(FTy->getPointerTo()); } Index: cfe/trunk/lib/CodeGen/CGCall.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGCall.cpp +++ cfe/trunk/lib/CodeGen/CGCall.cpp @@ -92,15 +92,41 @@ FTNP->getExtInfo(), RequiredArgs(0)); } +/// Adds the formal paramaters in FPT to the given prefix. If any parameter in +/// FPT has pass_object_size attrs, then we'll add parameters for those, too. +static void appendParameterTypes(const CodeGenTypes &CGT, + SmallVectorImpl &prefix, + const CanQual &FPT, + const FunctionDecl *FD) { + // Fast path: unknown target. + if (FD == nullptr) { + prefix.append(FPT->param_type_begin(), FPT->param_type_end()); + return; + } + + // In the vast majority cases, we'll have precisely FPT->getNumParams() + // parameters; the only thing that can change this is the presence of + // pass_object_size. So, we preallocate for the common case. + prefix.reserve(prefix.size() + FPT->getNumParams()); + + assert(FD->getNumParams() == FPT->getNumParams()); + for (unsigned I = 0, E = FPT->getNumParams(); I != E; ++I) { + prefix.push_back(FPT->getParamType(I)); + if (FD->getParamDecl(I)->hasAttr()) + prefix.push_back(CGT.getContext().getSizeType()); + } +} + /// Arrange the LLVM function layout for a value of the given function /// type, on top of any implicit parameters already stored. static const CGFunctionInfo & arrangeLLVMFunctionInfo(CodeGenTypes &CGT, bool instanceMethod, SmallVectorImpl &prefix, - CanQual FTP) { + CanQual FTP, + const FunctionDecl *FD) { RequiredArgs required = RequiredArgs::forPrototypePlus(FTP, prefix.size()); // FIXME: Kill copy. - prefix.append(FTP->param_type_begin(), FTP->param_type_end()); + appendParameterTypes(CGT, prefix, FTP, FD); CanQualType resultType = FTP->getReturnType().getUnqualifiedType(); return CGT.arrangeLLVMFunctionInfo(resultType, instanceMethod, /*chainCall=*/false, prefix, @@ -110,10 +136,11 @@ /// Arrange the argument and result information for a value of the /// given freestanding function type. const CGFunctionInfo & -CodeGenTypes::arrangeFreeFunctionType(CanQual FTP) { +CodeGenTypes::arrangeFreeFunctionType(CanQual FTP, + const FunctionDecl *FD) { SmallVector argTypes; return ::arrangeLLVMFunctionInfo(*this, /*instanceMethod=*/false, argTypes, - FTP); + FTP, FD); } static CallingConv getCallingConventionForDecl(const Decl *D, bool IsWindows) { @@ -156,7 +183,8 @@ /// constructor or destructor. const CGFunctionInfo & CodeGenTypes::arrangeCXXMethodType(const CXXRecordDecl *RD, - const FunctionProtoType *FTP) { + const FunctionProtoType *FTP, + const CXXMethodDecl *MD) { SmallVector argTypes; // Add the 'this' pointer. @@ -167,7 +195,7 @@ return ::arrangeLLVMFunctionInfo( *this, true, argTypes, - FTP->getCanonicalTypeUnqualified().getAs()); + FTP->getCanonicalTypeUnqualified().getAs(), MD); } /// Arrange the argument and result information for a declaration or @@ -184,10 +212,10 @@ if (MD->isInstance()) { // The abstract case is perfectly fine. const CXXRecordDecl *ThisType = TheCXXABI.getThisArgumentTypeForMethod(MD); - return arrangeCXXMethodType(ThisType, prototype.getTypePtr()); + return arrangeCXXMethodType(ThisType, prototype.getTypePtr(), MD); } - return arrangeFreeFunctionType(prototype); + return arrangeFreeFunctionType(prototype, MD); } const CGFunctionInfo & @@ -208,7 +236,7 @@ CanQual FTP = GetFormalType(MD); // Add the formal parameters. - argTypes.append(FTP->param_type_begin(), FTP->param_type_end()); + appendParameterTypes(*this, argTypes, FTP, MD); TheCXXABI.buildStructorSignature(MD, Type, argTypes); @@ -274,7 +302,7 @@ } assert(isa(FTy)); - return arrangeFreeFunctionType(FTy.getAs()); + return arrangeFreeFunctionType(FTy.getAs(), FD); } /// Arrange the argument and result information for the declaration or @@ -2803,6 +2831,21 @@ llvm::iterator_range ArgRange, const FunctionDecl *CalleeDecl, unsigned ParamsToSkip) { assert((int)ArgTypes.size() == (ArgRange.end() - ArgRange.begin())); + + auto MaybeEmitImplicitObjectSize = [&](unsigned I, const Expr *Arg) { + if (CalleeDecl == nullptr || I >= CalleeDecl->getNumParams()) + return; + auto *PS = CalleeDecl->getParamDecl(I)->getAttr(); + if (PS == nullptr) + return; + + const auto &Context = getContext(); + auto SizeTy = Context.getSizeType(); + auto T = Builder.getIntNTy(Context.getTypeSize(SizeTy)); + llvm::Value *V = evaluateOrEmitBuiltinObjectSize(Arg, PS->getType(), T); + Args.add(RValue::get(V), SizeTy); + }; + // We *have* to evaluate arguments from right to left in the MS C++ ABI, // because arguments are destroyed left to right in the callee. if (CGM.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) { @@ -2823,6 +2866,7 @@ EmitCallArg(Args, *Arg, ArgTypes[I]); EmitNonNullArgCheck(Args.back().RV, ArgTypes[I], (*Arg)->getExprLoc(), CalleeDecl, ParamsToSkip + I); + MaybeEmitImplicitObjectSize(I, *Arg); } // Un-reverse the arguments we just evaluated so they match up with the LLVM @@ -2837,6 +2881,7 @@ EmitCallArg(Args, *Arg, ArgTypes[I]); EmitNonNullArgCheck(Args.back().RV, ArgTypes[I], (*Arg)->getExprLoc(), CalleeDecl, ParamsToSkip + I); + MaybeEmitImplicitObjectSize(I, *Arg); } } Index: cfe/trunk/lib/CodeGen/CodeGenABITypes.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenABITypes.cpp +++ cfe/trunk/lib/CodeGen/CodeGenABITypes.cpp @@ -44,8 +44,9 @@ } const CGFunctionInfo & -CodeGenABITypes::arrangeFreeFunctionType(CanQual Ty) { - return CGM->getTypes().arrangeFreeFunctionType(Ty); +CodeGenABITypes::arrangeFreeFunctionType(CanQual Ty, + const FunctionDecl *FD) { + return CGM->getTypes().arrangeFreeFunctionType(Ty, FD); } const CGFunctionInfo & @@ -55,8 +56,9 @@ const CGFunctionInfo & CodeGenABITypes::arrangeCXXMethodType(const CXXRecordDecl *RD, - const FunctionProtoType *FTP) { - return CGM->getTypes().arrangeCXXMethodType(RD, FTP); + const FunctionProtoType *FTP, + const CXXMethodDecl *MD) { + return CGM->getTypes().arrangeCXXMethodType(RD, FTP, MD); } const CGFunctionInfo &CodeGenABITypes::arrangeFreeFunctionCall( Index: cfe/trunk/lib/CodeGen/CodeGenFunction.h =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenFunction.h +++ cfe/trunk/lib/CodeGen/CodeGenFunction.h @@ -914,6 +914,12 @@ /// decls. DeclMapTy LocalDeclMap; + /// SizeArguments - If a ParmVarDecl had the pass_object_size attribute, this + /// will contain a mapping from said ParmVarDecl to its implicit "object_size" + /// parameter. + llvm::SmallDenseMap + SizeArguments; + /// Track escaped local variables with auto storage. Used during SEH /// outlining to produce a call to llvm.localescape. llvm::DenseMap EscapedLocals; @@ -3062,6 +3068,18 @@ std::string &ConstraintStr, SourceLocation Loc); + /// \brief Attempts to statically evaluate the object size of E. If that + /// fails, emits code to figure the size of E out for us. This is + /// pass_object_size aware. + llvm::Value *evaluateOrEmitBuiltinObjectSize(const Expr *E, unsigned Type, + llvm::IntegerType *ResType); + + /// \brief Emits the size of E, as required by __builtin_object_size. This + /// function is aware of pass_object_size parameters, and will act accordingly + /// if E is a parameter with the pass_object_size attribute. + llvm::Value *emitBuiltinObjectSize(const Expr *E, unsigned Type, + llvm::IntegerType *ResType); + public: #ifndef NDEBUG // Determine whether the given argument is an Objective-C method Index: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp +++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp @@ -920,7 +920,18 @@ CGM.getCXXABI().buildThisParam(*this, Args); } - Args.append(FD->param_begin(), FD->param_end()); + for (auto *Param : FD->params()) { + Args.push_back(Param); + if (!Param->hasAttr()) + continue; + + IdentifierInfo *NoID = nullptr; + auto *Implicit = ImplicitParamDecl::Create( + getContext(), Param->getDeclContext(), Param->getLocation(), NoID, + getContext().getSizeType()); + SizeArguments[Param] = Implicit; + Args.push_back(Implicit); + } if (MD && (isa(MD) || isa(MD))) CGM.getCXXABI().addImplicitStructorParams(*this, ResTy, Args); Index: cfe/trunk/lib/CodeGen/CodeGenModule.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenModule.cpp +++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp @@ -1830,8 +1830,11 @@ bool DontDefer, bool IsForDefinition) { // If there was no specific requested type, just convert it now. - if (!Ty) - Ty = getTypes().ConvertType(cast(GD.getDecl())->getType()); + if (!Ty) { + const auto *FD = cast(GD.getDecl()); + auto CanonTy = Context.getCanonicalType(FD->getType()); + Ty = getTypes().ConvertFunctionType(CanonTy, FD); + } StringRef MangledName = getMangledName(GD); return GetOrCreateLLVMFunction(MangledName, Ty, GD, ForVTable, DontDefer, Index: cfe/trunk/lib/CodeGen/CodeGenTypes.h =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenTypes.h +++ cfe/trunk/lib/CodeGen/CodeGenTypes.h @@ -178,6 +178,14 @@ /// ConvertType - Convert type T into a llvm::Type. llvm::Type *ConvertType(QualType T); + /// \brief Converts the GlobalDecl into an llvm::Type. This should be used + /// when we know the target of the function we want to convert. This is + /// because some functions (explicitly, those with pass_object_size + /// parameters) may not have the same signature as their type portrays, and + /// can only be called directly. + llvm::Type *ConvertFunctionType(QualType FT, + const FunctionDecl *FD = nullptr); + /// ConvertTypeForMem - Convert type T into a llvm::Type. This differs from /// ConvertType in that it is used to convert to the memory representation for /// a type. For example, the scalar representation for _Bool is i1, but the @@ -264,11 +272,12 @@ const CGFunctionInfo &arrangeMSMemberPointerThunk(const CXXMethodDecl *MD); const CGFunctionInfo &arrangeMSCtorClosure(const CXXConstructorDecl *CD, CXXCtorType CT); - - const CGFunctionInfo &arrangeFreeFunctionType(CanQual Ty); + const CGFunctionInfo &arrangeFreeFunctionType(CanQual Ty, + const FunctionDecl *FD); const CGFunctionInfo &arrangeFreeFunctionType(CanQual Ty); const CGFunctionInfo &arrangeCXXMethodType(const CXXRecordDecl *RD, - const FunctionProtoType *FTP); + const FunctionProtoType *FTP, + const CXXMethodDecl *MD); /// "Arrange" the LLVM information for a call or type with the given /// signature. This is largely an internal method; other clients Index: cfe/trunk/lib/CodeGen/CodeGenTypes.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenTypes.cpp +++ cfe/trunk/lib/CodeGen/CodeGenTypes.cpp @@ -294,6 +294,76 @@ llvm_unreachable("Unknown float format!"); } +llvm::Type *CodeGenTypes::ConvertFunctionType(QualType QFT, + const FunctionDecl *FD) { + assert(QFT.isCanonical()); + const Type *Ty = QFT.getTypePtr(); + const FunctionType *FT = cast(QFT.getTypePtr()); + // First, check whether we can build the full function type. If the + // function type depends on an incomplete type (e.g. a struct or enum), we + // cannot lower the function type. + if (!isFuncTypeConvertible(FT)) { + // This function's type depends on an incomplete tag type. + + // Force conversion of all the relevant record types, to make sure + // we re-convert the FunctionType when appropriate. + if (const RecordType *RT = FT->getReturnType()->getAs()) + ConvertRecordDeclType(RT->getDecl()); + if (const FunctionProtoType *FPT = dyn_cast(FT)) + for (unsigned i = 0, e = FPT->getNumParams(); i != e; i++) + if (const RecordType *RT = FPT->getParamType(i)->getAs()) + ConvertRecordDeclType(RT->getDecl()); + + SkippedLayout = true; + + // Return a placeholder type. + return llvm::StructType::get(getLLVMContext()); + } + + // While we're converting the parameter types for a function, we don't want + // to recursively convert any pointed-to structs. Converting directly-used + // structs is ok though. + if (!RecordsBeingLaidOut.insert(Ty).second) { + SkippedLayout = true; + return llvm::StructType::get(getLLVMContext()); + } + + // The function type can be built; call the appropriate routines to + // build it. + const CGFunctionInfo *FI; + if (const FunctionProtoType *FPT = dyn_cast(FT)) { + FI = &arrangeFreeFunctionType( + CanQual::CreateUnsafe(QualType(FPT, 0)), FD); + } else { + const FunctionNoProtoType *FNPT = cast(FT); + FI = &arrangeFreeFunctionType( + CanQual::CreateUnsafe(QualType(FNPT, 0))); + } + + llvm::Type *ResultType = nullptr; + // If there is something higher level prodding our CGFunctionInfo, then + // don't recurse into it again. + if (FunctionsBeingProcessed.count(FI)) { + + ResultType = llvm::StructType::get(getLLVMContext()); + SkippedLayout = true; + } else { + + // Otherwise, we're good to go, go ahead and convert it. + ResultType = GetFunctionType(*FI); + } + + RecordsBeingLaidOut.erase(Ty); + + if (SkippedLayout) + TypeCache.clear(); + + if (RecordsBeingLaidOut.empty()) + while (!DeferredRecords.empty()) + ConvertRecordDeclType(DeferredRecords.pop_back_val()); + return ResultType; +} + /// ConvertType - Convert the specified type to its LLVM form. llvm::Type *CodeGenTypes::ConvertType(QualType T) { T = Context.getCanonicalType(T); @@ -485,75 +555,9 @@ break; } case Type::FunctionNoProto: - case Type::FunctionProto: { - const FunctionType *FT = cast(Ty); - // First, check whether we can build the full function type. If the - // function type depends on an incomplete type (e.g. a struct or enum), we - // cannot lower the function type. - if (!isFuncTypeConvertible(FT)) { - // This function's type depends on an incomplete tag type. - - // Force conversion of all the relevant record types, to make sure - // we re-convert the FunctionType when appropriate. - if (const RecordType *RT = FT->getReturnType()->getAs()) - ConvertRecordDeclType(RT->getDecl()); - if (const FunctionProtoType *FPT = dyn_cast(FT)) - for (unsigned i = 0, e = FPT->getNumParams(); i != e; i++) - if (const RecordType *RT = FPT->getParamType(i)->getAs()) - ConvertRecordDeclType(RT->getDecl()); - - // Return a placeholder type. - ResultType = llvm::StructType::get(getLLVMContext()); - - SkippedLayout = true; - break; - } - - // While we're converting the parameter types for a function, we don't want - // to recursively convert any pointed-to structs. Converting directly-used - // structs is ok though. - if (!RecordsBeingLaidOut.insert(Ty).second) { - ResultType = llvm::StructType::get(getLLVMContext()); - - SkippedLayout = true; - break; - } - - // The function type can be built; call the appropriate routines to - // build it. - const CGFunctionInfo *FI; - if (const FunctionProtoType *FPT = dyn_cast(FT)) { - FI = &arrangeFreeFunctionType( - CanQual::CreateUnsafe(QualType(FPT, 0))); - } else { - const FunctionNoProtoType *FNPT = cast(FT); - FI = &arrangeFreeFunctionType( - CanQual::CreateUnsafe(QualType(FNPT, 0))); - } - - // If there is something higher level prodding our CGFunctionInfo, then - // don't recurse into it again. - if (FunctionsBeingProcessed.count(FI)) { - - ResultType = llvm::StructType::get(getLLVMContext()); - SkippedLayout = true; - } else { - - // Otherwise, we're good to go, go ahead and convert it. - ResultType = GetFunctionType(*FI); - } - - RecordsBeingLaidOut.erase(Ty); - - if (SkippedLayout) - TypeCache.clear(); - - if (RecordsBeingLaidOut.empty()) - while (!DeferredRecords.empty()) - ConvertRecordDeclType(DeferredRecords.pop_back_val()); + case Type::FunctionProto: + ResultType = ConvertFunctionType(T); break; - } - case Type::ObjCObject: ResultType = ConvertType(cast(Ty)->getBaseType()); break; Index: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp +++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp @@ -534,9 +534,8 @@ const CXXRecordDecl *RD = cast(MPT->getClass()->getAs()->getDecl()); - llvm::FunctionType *FTy = - CGM.getTypes().GetFunctionType( - CGM.getTypes().arrangeCXXMethodType(RD, FPT)); + llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType( + CGM.getTypes().arrangeCXXMethodType(RD, FPT, /*FD=*/nullptr)); llvm::Constant *ptrdiff_1 = llvm::ConstantInt::get(CGM.PtrDiffTy, 1); Index: cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp +++ cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp @@ -3225,9 +3225,8 @@ const FunctionProtoType *FPT = MPT->getPointeeType()->castAs(); const CXXRecordDecl *RD = MPT->getMostRecentCXXRecordDecl(); - llvm::FunctionType *FTy = - CGM.getTypes().GetFunctionType( - CGM.getTypes().arrangeCXXMethodType(RD, FPT)); + llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType( + CGM.getTypes().arrangeCXXMethodType(RD, FPT, /*FD=*/nullptr)); CGBuilderTy &Builder = CGF.Builder; MSInheritanceAttr::Spelling Inheritance = RD->getMSInheritanceModel(); Index: cfe/trunk/lib/Sema/SemaChecking.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaChecking.cpp +++ cfe/trunk/lib/Sema/SemaChecking.cpp @@ -8419,6 +8419,15 @@ } } } + + // Parameters with the pass_object_size attribute only need to be marked + // constant at function definitions. Because we lack information about + // whether we're on a declaration or definition when we're instantiating the + // attribute, we need to check for constness here. + if (const auto *Attr = Param->getAttr()) + if (!Param->getType().isConstQualified()) + Diag(Param->getLocation(), diag::err_attribute_pointers_only) + << Attr->getSpelling() << 1; } return HasInvalidParm; @@ -9869,4 +9878,3 @@ << ArgumentExpr->getSourceRange() << TypeTagExpr->getSourceRange(); } - Index: cfe/trunk/lib/Sema/SemaDecl.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp +++ cfe/trunk/lib/Sema/SemaDecl.cpp @@ -2621,6 +2621,21 @@ return false; } +static bool hasIdenticalPassObjectSizeAttrs(const FunctionDecl *A, + const FunctionDecl *B) { + assert(A->getNumParams() == B->getNumParams()); + + auto AttrEq = [](const ParmVarDecl *A, const ParmVarDecl *B) { + const auto *AttrA = A->getAttr(); + const auto *AttrB = B->getAttr(); + if (AttrA == AttrB) + return true; + return AttrA && AttrB && AttrA->getType() == AttrB->getType(); + }; + + return std::equal(A->param_begin(), A->param_end(), B->param_begin(), AttrEq); +} + /// MergeFunctionDecl - We just parsed a function 'New' from /// declarator D which has the same name and scope as a previous /// declaration 'Old'. Figure out how to resolve this situation, @@ -2799,7 +2814,17 @@ Old->isInlined() && !Old->hasAttr()) { UndefinedButUsed.erase(Old->getCanonicalDecl()); } - + + // If pass_object_size params don't match up perfectly, this isn't a valid + // redeclaration. + if (Old->getNumParams() > 0 && Old->getNumParams() == New->getNumParams() && + !hasIdenticalPassObjectSizeAttrs(Old, New)) { + Diag(New->getLocation(), diag::err_different_pass_object_size_params) + << New->getDeclName(); + Diag(OldLocation, PrevDiag) << Old << Old->getType(); + return true; + } + if (getLangOpts().CPlusPlus) { // (C++98 13.1p2): // Certain function declarations cannot be overloaded: Index: cfe/trunk/lib/Sema/SemaDeclAttr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp @@ -809,6 +809,43 @@ Attr.getAttributeSpellingListIndex())); } +static void handlePassObjectSizeAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (D->hasAttr()) { + S.Diag(D->getLocStart(), diag::err_attribute_only_once_per_parameter) + << Attr.getName(); + return; + } + + Expr *E = Attr.getArgAsExpr(0); + uint32_t Type; + if (!checkUInt32Argument(S, Attr, E, Type, /*Idx=*/1)) + return; + + // pass_object_size's argument is passed in as the second argument of + // __builtin_object_size. So, it has the same constraints as that second + // argument; namely, it must be in the range [0, 3]. + if (Type > 3) { + S.Diag(E->getLocStart(), diag::err_attribute_argument_outof_range) + << Attr.getName() << 0 << 3 << E->getSourceRange(); + return; + } + + // pass_object_size is only supported on constant pointer parameters; as a + // kindness to users, we allow the parameter to be non-const for declarations. + // At this point, we have no clue if `D` belongs to a function declaration or + // definition, so we defer the constness check until later. + if (!cast(D)->getType()->isPointerType()) { + S.Diag(D->getLocStart(), diag::err_attribute_pointers_only) + << Attr.getName() << 1; + return; + } + + D->addAttr(::new (S.Context) + PassObjectSizeAttr(Attr.getRange(), S.Context, (int)Type, + Attr.getAttributeSpellingListIndex())); +} + static void handleConsumableAttr(Sema &S, Decl *D, const AttributeList &Attr) { ConsumableAttr::ConsumedState DefaultState; @@ -1162,10 +1199,12 @@ SourceRange TypeRange, bool isReturnValue = false) { if (!S.isValidPointerAttrType(T)) { - S.Diag(Attr.getLoc(), isReturnValue - ? diag::warn_attribute_return_pointers_only - : diag::warn_attribute_pointers_only) - << Attr.getName() << AttrParmRange << TypeRange; + if (isReturnValue) + S.Diag(Attr.getLoc(), diag::warn_attribute_return_pointers_only) + << Attr.getName() << AttrParmRange << TypeRange; + else + S.Diag(Attr.getLoc(), diag::warn_attribute_pointers_only) + << Attr.getName() << AttrParmRange << TypeRange << 0; return false; } return true; @@ -2724,7 +2763,7 @@ if (prioritynum < 101 || prioritynum > 65535) { S.Diag(Attr.getLoc(), diag::err_attribute_argument_outof_range) - << E->getSourceRange(); + << E->getSourceRange() << Attr.getName() << 101 << 65535; Attr.setInvalid(); return; } @@ -3862,7 +3901,7 @@ QualType BufferTy = getFunctionOrMethodParamType(D, ArgumentIdx); if (!BufferTy->isPointerType()) { S.Diag(Attr.getLoc(), diag::err_attribute_pointers_only) - << Attr.getName(); + << Attr.getName() << 0; } } @@ -4972,6 +5011,9 @@ case AttributeList::AT_CUDAConstant: handleSimpleAttribute(S, D, Attr); break; + case AttributeList::AT_PassObjectSize: + handlePassObjectSizeAttr(S, D, Attr); + break; case AttributeList::AT_Constructor: handleConstructorAttr(S, D, Attr); break; Index: cfe/trunk/lib/Sema/SemaExpr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaExpr.cpp +++ cfe/trunk/lib/Sema/SemaExpr.cpp @@ -496,7 +496,7 @@ //===----------------------------------------------------------------------===// /// DefaultFunctionArrayConversion (C99 6.3.2.1p3, C99 6.3.2.1p4). -ExprResult Sema::DefaultFunctionArrayConversion(Expr *E) { +ExprResult Sema::DefaultFunctionArrayConversion(Expr *E, bool Diagnose) { // Handle any placeholder expressions which made it here. if (E->getType()->isPlaceholderType()) { ExprResult result = CheckPlaceholderExpr(E); @@ -511,9 +511,16 @@ // If we are here, we are not calling a function but taking // its address (which is not allowed in OpenCL v1.0 s6.8.a.3). if (getLangOpts().OpenCL) { - Diag(E->getExprLoc(), diag::err_opencl_taking_function_address); + if (Diagnose) + Diag(E->getExprLoc(), diag::err_opencl_taking_function_address); return ExprError(); } + + if (auto *DRE = dyn_cast(E->IgnoreParenCasts())) + if (auto *FD = dyn_cast(DRE->getDecl())) + if (!checkAddressOfFunctionIsAvailable(FD, Diagnose, E->getExprLoc())) + return ExprError(); + E = ImpCastExprToType(E, Context.getPointerType(Ty), CK_FunctionToPointerDecay).get(); } else if (Ty->isArrayType()) { @@ -706,8 +713,8 @@ return Res; } -ExprResult Sema::DefaultFunctionArrayLvalueConversion(Expr *E) { - ExprResult Res = DefaultFunctionArrayConversion(E); +ExprResult Sema::DefaultFunctionArrayLvalueConversion(Expr *E, bool Diagnose) { + ExprResult Res = DefaultFunctionArrayConversion(E, Diagnose); if (Res.isInvalid()) return ExprError(); Res = DefaultLvalueConversion(Res.get()); @@ -7338,7 +7345,7 @@ // Suppress this for references: C++ 8.5.3p5. if (!LHSType->isReferenceType()) { // FIXME: We potentially allocate here even if ConvertRHS is false. - RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); + RHS = DefaultFunctionArrayLvalueConversion(RHS.get(), Diagnose); if (RHS.isInvalid()) return Incompatible; } @@ -9882,6 +9889,12 @@ // expressions here, but the result of one is always an lvalue anyway. } ValueDecl *dcl = getPrimaryDecl(op); + + if (auto *FD = dyn_cast_or_null(dcl)) + if (!checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true, + op->getLocStart())) + return QualType(); + Expr::LValueClassification lval = op->ClassifyLValue(Context); unsigned AddressOfError = AO_No_Error; @@ -11831,6 +11844,25 @@ return true; } +static bool maybeDiagnoseAssignmentToFunction(Sema &S, QualType DstType, + const Expr *SrcExpr) { + if (!DstType->isFunctionPointerType() || + !SrcExpr->getType()->isFunctionType()) + return false; + + auto *DRE = dyn_cast(SrcExpr->IgnoreParenImpCasts()); + if (!DRE) + return false; + + auto *FD = dyn_cast(DRE->getDecl()); + if (!FD) + return false; + + return !S.checkAddressOfFunctionIsAvailable(FD, + /*Complain=*/true, + SrcExpr->getLocStart()); +} + bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, SourceLocation Loc, QualType DstType, QualType SrcType, @@ -11963,6 +11995,12 @@ DiagKind = diag::err_arc_weak_unavailable_assign; break; case Incompatible: + if (maybeDiagnoseAssignmentToFunction(*this, DstType, SrcExpr)) { + if (Complained) + *Complained = true; + return true; + } + DiagKind = diag::err_typecheck_convert_incompatible; ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this); MayHaveConvFixit = true; Index: cfe/trunk/lib/Sema/SemaInit.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaInit.cpp +++ cfe/trunk/lib/Sema/SemaInit.cpp @@ -3011,6 +3011,7 @@ case FK_VariableLengthArrayHasInitializer: case FK_PlaceholderType: case FK_ExplicitConstructor: + case FK_AddressOfUnaddressableFunction: return false; case FK_ReferenceInitOverloadFailed: @@ -4801,6 +4802,17 @@ InitializeFrom(S, Entity, Kind, Args, TopLevelOfInitList); } +/// Tries to get a FunctionDecl out of `E`. If it succeeds and we can take the +/// address of that function, this returns true. Otherwise, it returns false. +static bool isExprAnUnaddressableFunction(Sema &S, const Expr *E) { + auto *DRE = dyn_cast(E); + if (!DRE || !isa(DRE->getDecl())) + return false; + + return !S.checkAddressOfFunctionIsAvailable( + cast(DRE->getDecl())); +} + void InitializationSequence::InitializeFrom(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, @@ -4982,7 +4994,7 @@ } assert(S.getLangOpts().CPlusPlus); - + // - If the destination type is a (possibly cv-qualified) class type: if (DestType->isRecordType()) { // - If the initialization is direct-initialization, or if it is @@ -5079,6 +5091,9 @@ !S.ResolveAddressOfOverloadedFunction(Initializer, DestType, false, dap)) SetFailed(InitializationSequence::FK_AddressOfOverloadFailed); + else if (Initializer->getType()->isFunctionType() && + isExprAnUnaddressableFunction(S, Initializer)) + SetFailed(InitializationSequence::FK_AddressOfUnaddressableFunction); else SetFailed(InitializationSequence::FK_ConversionFailed); } else { @@ -6926,6 +6941,13 @@ break; } + case FK_AddressOfUnaddressableFunction: { + auto *FD = cast(cast(Args[0])->getDecl()); + S.checkAddressOfFunctionIsAvailable(FD, /*Complain=*/true, + Args[0]->getLocStart()); + break; + } + case FK_ReferenceInitOverloadFailed: case FK_UserConversionOverloadFailed: switch (FailedOverloadResult) { @@ -7248,6 +7270,10 @@ OS << "array requires initializer list"; break; + case FK_AddressOfUnaddressableFunction: + OS << "address of unaddressable function was taken"; + break; + case FK_ArrayNeedsInitListOrStringLiteral: OS << "array requires initializer list or string literal"; break; Index: cfe/trunk/lib/Sema/SemaLambda.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaLambda.cpp +++ cfe/trunk/lib/Sema/SemaLambda.cpp @@ -1141,6 +1141,12 @@ SourceRange IntroducerRange, CXXRecordDecl *Class, CXXMethodDecl *CallOperator) { + // This conversion is explicitly disabled if the lambda's function has + // pass_object_size attributes on any of its parameters. + if (std::any_of(CallOperator->param_begin(), CallOperator->param_end(), + std::mem_fn(&ParmVarDecl::hasAttr))) + return; + // Add the conversion to function pointer. const FunctionProtoType *CallOpProto = CallOperator->getType()->getAs(); Index: cfe/trunk/lib/Sema/SemaOverload.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaOverload.cpp +++ cfe/trunk/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)); +} + /// A convenience routine for creating a decayed reference to a function. static ExprResult CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl, @@ -60,12 +65,8 @@ 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 @@ 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 NewI = New->specific_attr_begin(), @@ -1548,6 +1557,11 @@ // Function-to-pointer conversion (C++ 4.3). SCS.First = ICK_Function_To_Pointer; + if (auto *DRE = dyn_cast(From->IgnoreParenCasts())) + if (auto *FD = dyn_cast(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 @@ 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()) + return FPT; + + if (auto *MPT = FromType->getAs()) + return MPT->getPointeeType()->getAs(); + + 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 @@ return; } - const FunctionProtoType *FromFunction = FromType->getAs(), - *ToFunction = ToType->getAs(); + const FunctionProtoType *FromFunction = tryGetFunctionProtoType(FromType), + *ToFunction = tryGetFunctionProtoType(ToType); // Both types need to be function types. if (!FromFunction || !ToFunction) { @@ -8572,7 +8597,11 @@ 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 @@ } } -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 @@ 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)); + 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 @@ } 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 @@ 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 @@ /// 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()) || @@ -9335,6 +9424,11 @@ } } } + + if (TakingCandidateAddress && isa(Templated) && + !checkAddressOfCandidateIsAvailable(S, cast(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 @@ /// 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 @@ /// 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 @@ 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 @@ 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 @@ ++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 @@ /// 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 @@ 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 @@ 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 @@ Specialization = cast(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 @@ return false; } - if (!isFunctionAlwaysEnabled(S.Context, FunDecl)) + if (!S.checkAddressOfFunctionIsAvailable(FunDecl)) return false; QualType ResultTy; @@ -10341,8 +10438,9 @@ I != IEnd; ++I) if (FunctionDecl *Fun = dyn_cast((*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 @@ 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(Arg->IgnoreParenImpCasts())) { + auto *FD = dyn_cast(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; } Index: cfe/trunk/lib/Sema/SemaTemplate.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaTemplate.cpp +++ cfe/trunk/lib/Sema/SemaTemplate.cpp @@ -2748,7 +2748,8 @@ typedef PartialSpecMatchResult MatchResult; SmallVector Matched; SourceLocation PointOfInstantiation = TemplateNameLoc; - TemplateSpecCandidateSet FailedCandidates(PointOfInstantiation); + TemplateSpecCandidateSet FailedCandidates(PointOfInstantiation, + /*ForTakingAddress=*/false); // 1. Attempt to find the closest partial specialization that this // specializes, if any. @@ -6822,7 +6823,8 @@ // The set of function template specializations that could match this // explicit function template specialization. UnresolvedSet<8> Candidates; - TemplateSpecCandidateSet FailedCandidates(FD->getLocation()); + TemplateSpecCandidateSet FailedCandidates(FD->getLocation(), + /*ForTakingAddress=*/false); llvm::SmallDenseMap ConvertedTemplateArgs; Index: cfe/trunk/lib/Sema/SemaType.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaType.cpp +++ cfe/trunk/lib/Sema/SemaType.cpp @@ -5483,9 +5483,12 @@ // Pointer type qualifiers can only operate on pointer types, but not // pointer-to-member types. if (!isa(Desugared)) { - S.Diag(Attr.getLoc(), Type->isMemberPointerType() ? - diag::err_attribute_no_member_pointers : - diag::err_attribute_pointers_only) << Attr.getName(); + if (Type->isMemberPointerType()) + S.Diag(Attr.getLoc(), diag::err_attribute_no_member_pointers) + << Attr.getName(); + else + S.Diag(Attr.getLoc(), diag::err_attribute_pointers_only) + << Attr.getName() << 0; return true; } Index: cfe/trunk/test/CodeGenCXX/mangle-ms.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/mangle-ms.cpp +++ cfe/trunk/test/CodeGenCXX/mangle-ms.cpp @@ -418,3 +418,22 @@ // X64-DAG: @"\01?f@UnnamedType@@YAXUT5@S@1@@Z" // X64-DAG: @"\01?f@UnnamedType@@YAXPEAU@S@1@@Z" } + +namespace PassObjectSize { +// NOTE: This mangling is subject to change. +// Reiterating from the comment in MicrosoftMangle, the scheme is pretend a +// parameter of type __clang::__pass_object_sizeN exists after each pass object +// size param P, where N is the Type of the pass_object_size attribute on P. +// +// e.g. we want to mangle: +// void foo(void *const __attribute__((pass_object_size(0)))); +// as if it were +// namespace __clang { enum __pass_object_size0 : size_t {}; } +// void foo(void *const, __clang::__pass_object_size0); +// where __clang is a top-level namespace. + +// CHECK-DAG: define i32 @"\01?foo@PassObjectSize@@YAHQAHW4__pass_object_size0@__clang@@@Z" +int foo(int *const i __attribute__((pass_object_size(0)))) { return 0; } +// CHECK-DAG: define i32 @"\01?bar@PassObjectSize@@YAHQAHW4__pass_object_size1@__clang@@@Z" +int bar(int *const i __attribute__((pass_object_size(1)))) { return 0; } +} Index: cfe/trunk/test/SemaCXX/init-priority-attr.cpp =================================================================== --- cfe/trunk/test/SemaCXX/init-priority-attr.cpp +++ cfe/trunk/test/SemaCXX/init-priority-attr.cpp @@ -21,7 +21,7 @@ Two goo __attribute__((init_priority(2,3))) ( 5, 6 ); // expected-error {{'init_priority' attribute takes one argument}} -Two coo[2] __attribute__((init_priority(3))); // expected-error {{init_priority attribute requires integer constant between 101 and 65535 inclusive}} +Two coo[2] __attribute__((init_priority(3))); // expected-error {{'init_priority' attribute requires integer constant between 101 and 65535 inclusive}} Two koo[4] __attribute__((init_priority(1.13))); // expected-error {{'init_priority' attribute requires an integer constant}}