Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -628,6 +628,16 @@ const FunctionDecl *Callee, ArrayRef Args) const; + /// \brief If the current Expr is either 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(llvm::APSInt &Result, ASTContext &Ctx, + unsigned Type) const; + /// \brief Enumeration used to describe the kind of Null pointer constant /// returned from \c isNullPointerConstant(). enum NullPointerConstantKind { Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -989,6 +989,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: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ 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: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -1499,8 +1499,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">; @@ -2056,8 +2055,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">; @@ -2065,10 +2063,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< @@ -2990,8 +2990,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< @@ -3020,8 +3019,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">; @@ -3032,7 +3039,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|" @@ -4172,6 +4179,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">; @@ -5140,6 +5149,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< @@ -5722,8 +5734,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; @@ -5960,8 +5971,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: include/clang/CodeGen/CodeGenABITypes.h =================================================================== --- include/clang/CodeGen/CodeGenABITypes.h +++ 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: include/clang/Sema/Initialization.h =================================================================== --- include/clang/Sema/Initialization.h +++ 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: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -145,6 +145,7 @@ class ObjCProtocolDecl; class OMPThreadPrivateDecl; class OMPClause; + struct OverloadCandidate; class OverloadCandidateSet; class OverloadExpr; class ParenListExpr; @@ -2457,6 +2458,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) @@ -8138,12 +8147,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: include/clang/Sema/TemplateDeduction.h =================================================================== --- include/clang/Sema/TemplateDeduction.h +++ 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: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ 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) { @@ -6504,9 +6543,7 @@ // 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 @@ -9482,3 +9519,18 @@ Evaluate(ResultScratch, Info, E); return Diags.empty(); } + +bool Expr::tryEvaluateObjectSize(llvm::APSInt &Result, ASTContext &Ctx, + unsigned Type) const { + if (!getType()->isPointerType()) + return false; + + Expr::EvalStatus Status; + EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold); + uint64_t Size; + if (!::tryEvaluateBuiltinObjectSize(this, Type, Info, Size)) + return false; + + Result = llvm::APInt(/*NumBits=*/64, Size, /*IsSigned=*/true); + return true; +} Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -378,7 +378,8 @@ void mangleType(const TagType*); void mangleType(TemplateName); void mangleBareFunctionType(const FunctionType *T, - bool MangleReturnType); + bool MangleReturnType, + const FunctionDecl *FD = nullptr); void mangleNeonVectorType(const VectorType *T); void mangleAArch64NeonVectorType(const VectorType *T); @@ -523,7 +524,7 @@ } mangleBareFunctionType(FD->getType()->getAs(), - MangleReturnType); + MangleReturnType, FD); } static const DeclContext *IgnoreLinkageSpecDecls(const DeclContext *DC) { @@ -1282,7 +1283,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 +2173,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 +2197,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 != nullptr) { + 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); @@ -4227,4 +4241,3 @@ ItaniumMangleContext::create(ASTContext &Context, DiagnosticsEngine &Diags) { return new ItaniumMangleContextImpl(Context, Diags); } - Index: lib/AST/MicrosoftMangle.cpp =================================================================== --- lib/AST/MicrosoftMangle.cpp +++ lib/AST/MicrosoftMangle.cpp @@ -268,6 +268,7 @@ void mangleFunctionType(const FunctionType *T, const FunctionDecl *D = nullptr, bool ForceThisQuals = false); + void manglePassObjectSizeParams(const FunctionDecl *D); void mangleNestedName(const NamedDecl *ND); private: @@ -1736,11 +1737,43 @@ mangleFunctionType(T); } +void MicrosoftCXXNameMangler::manglePassObjectSizeParams( + const FunctionDecl *D) { + // Current mangling scheme: + // ::= _PS _PE + // ::= + // | & + // + // ::= T + // + // is the 0-indexed argument number, is the integer passed + // for type. If no pass_object_size attributes are present, this won't add + // anything. + + unsigned ArgNo = 0; + bool EmittedPS = false; + for (auto *Param : D->params()) { + if (auto *PS = Param->getAttr()) { + if (!EmittedPS) { + Out << "_PS"; + EmittedPS = true; + } else + Out << '&'; + Out << ArgNo << 'T' << PS->getType(); + } + ++ArgNo; + } + + if (EmittedPS) + Out << "_PE"; +} + void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T, const FunctionDecl *D, bool ForceThisQuals) { // ::= - // + // + // const FunctionProtoType *Proto = dyn_cast(T); SourceRange Range; @@ -1829,6 +1862,9 @@ } } + if (D != nullptr) + manglePassObjectSizeParams(D); + // ::= X # void // ::= + @ // ::= * Z # varargs Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -381,6 +381,62 @@ }); } +llvm::Value *CodeGenFunction::evaluateOrEmitBuiltinSizeOf(const Expr *E, + int Type, + llvm::Type *ResType) { + APSInt Result; + if (!E->tryEvaluateObjectSize(Result, getContext(), Type)) + return emitBuiltinSizeOf(E, Type, ResType); + return ConstantInt::get(ResType, Result.getZExtValue(), /*isSigned=*/true); +} + +/// 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); +} + +/// 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::emitBuiltinSizeOf(const Expr *E, int Type, + llvm::Type *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 always give correct results for Type=3 with @llvm.objectsize, + // so we can't lower this to the @llvm.objectsize intrinsic + if (Type == 3) + return ConstantInt::get(ResType, 0, /*isSigned=*/true); + + auto *CI = ConstantInt::get(Builder.getInt1Ty(), (Type & 0x2) >> 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) { @@ -638,13 +694,8 @@ 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})); + emitBuiltinSizeOf(E->getArg(0), CI->getSExtValue(), ResType)); } case Builtin::BI__builtin_prefetch: { Value *Locality, *RW, *Address = EmitScalarExpr(E->getArg(0)); Index: lib/CodeGen/CGCXXABI.cpp =================================================================== --- lib/CodeGen/CGCXXABI.cpp +++ 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: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ 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(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 @@ -2783,6 +2811,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 QT = Context.getSizeType(); + auto T = Builder.getIntNTy(Context.getTypeSize(QT)); + llvm::Value *V = evaluateOrEmitBuiltinSizeOf(Arg, PS->getType(), T); + Args.add(RValue::get(V), QT); + }; + // 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()) { @@ -2801,6 +2844,7 @@ for (int I = ArgTypes.size() - 1; I >= 0; --I) { CallExpr::const_arg_iterator Arg = ArgRange.begin() + I; EmitCallArg(Args, *Arg, ArgTypes[I]); + MaybeEmitImplicitObjectSize(I, *Arg); EmitNonNullArgCheck(Args.back().RV, ArgTypes[I], (*Arg)->getExprLoc(), CalleeDecl, ParamsToSkip + I); } @@ -2815,6 +2859,7 @@ CallExpr::const_arg_iterator Arg = ArgRange.begin() + I; assert(Arg != ArgRange.end()); EmitCallArg(Args, *Arg, ArgTypes[I]); + MaybeEmitImplicitObjectSize(I, *Arg); EmitNonNullArgCheck(Args.back().RV, ArgTypes[I], (*Arg)->getExprLoc(), CalleeDecl, ParamsToSkip + I); } Index: lib/CodeGen/CodeGenABITypes.cpp =================================================================== --- lib/CodeGen/CodeGenABITypes.cpp +++ 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: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ 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; @@ -3071,6 +3077,16 @@ std::string &ConstraintStr, SourceLocation Loc); + /// 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 *evaluateOrEmitBuiltinSizeOf(const Expr *E, int Type, + llvm::Type *ResType); + + /// 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 *emitBuiltinSizeOf(const Expr *E, int Type, llvm::Type *ResType); + public: #ifndef NDEBUG // Determine whether the given argument is an Objective-C method Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -911,7 +911,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: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -1835,8 +1835,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: lib/CodeGen/CodeGenTypes.h =================================================================== --- lib/CodeGen/CodeGenTypes.h +++ 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 @@ -265,10 +273,12 @@ 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: lib/CodeGen/CodeGenTypes.cpp =================================================================== --- lib/CodeGen/CodeGenTypes.cpp +++ 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: lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- lib/CodeGen/ItaniumCXXABI.cpp +++ lib/CodeGen/ItaniumCXXABI.cpp @@ -537,7 +537,7 @@ llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType( - CGM.getTypes().arrangeCXXMethodType(RD, FPT)); + CGM.getTypes().arrangeCXXMethodType(RD, FPT, /*FD=*/nullptr)); llvm::Constant *ptrdiff_1 = llvm::ConstantInt::get(CGM.PtrDiffTy, 1); Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -3230,7 +3230,7 @@ const CXXRecordDecl *RD = MPT->getMostRecentCXXRecordDecl(); llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType( - CGM.getTypes().arrangeCXXMethodType(RD, FPT)); + CGM.getTypes().arrangeCXXMethodType(RD, FPT, /*FD=*/nullptr)); CGBuilderTy &Builder = CGF.Builder; MSInheritanceAttr::Spelling Inheritance = RD->getMSInheritanceModel(); Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -8410,6 +8410,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; @@ -9860,4 +9869,3 @@ << ArgumentExpr->getSourceRange() << TypeTagExpr->getSourceRange(); } - Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -2608,6 +2608,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, @@ -2786,7 +2801,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: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ 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; @@ -2705,7 +2744,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; } @@ -3801,7 +3840,7 @@ QualType BufferTy = getFunctionOrMethodParamType(D, ArgumentIdx); if (!BufferTy->isPointerType()) { S.Diag(Attr.getLoc(), diag::err_attribute_pointers_only) - << Attr.getName(); + << Attr.getName() << 0; } } @@ -4839,6 +4878,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: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ 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,17 @@ // 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 +714,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()); @@ -7311,7 +7319,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; } @@ -9858,6 +9866,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; @@ -11807,6 +11821,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, @@ -11939,6 +11972,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: lib/Sema/SemaInit.cpp =================================================================== --- lib/Sema/SemaInit.cpp +++ 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: lib/Sema/SemaLambda.cpp =================================================================== --- lib/Sema/SemaLambda.cpp +++ 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: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ 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 @@ -8761,17 +8790,74 @@ return true; } +bool Sema::checkAddressOfFunctionIsAvailable(const FunctionDecl *Function, + bool Complain, + SourceLocation Loc) { + if (!isFunctionAlwaysEnabled(Context, Function)) { + // FIXME(gbiv): This lacks tests. We should add tests. + if (Complain) + Diag(Loc, diag::err_addrof_function_disabled_by_enable_if_attr) + << Function; + return false; + } + + auto I = std::find_if(Function->param_begin(), Function->param_end(), + std::mem_fn(&ParmVarDecl::hasAttr)); + if (I == Function->param_end()) + return true; + + if (Complain) { + // Add one because we're showing this to the user + unsigned ParamNo = std::distance(Function->param_begin(), I) + 1; + Diag(Loc, diag::err_address_of_function_with_pass_object_size_params) + << Function << ParamNo; + } + return false; +} + +/// \brief Returns true if we emitted a diagnostic about taking the address of +/// an ineligible overload candidate, false otherwise. +/// +/// \param Template true if we should emit a note about a template-related +/// overloading error; false if we should emit a note about regular overloading +/// behavior. +static bool diagnoseBadAddressOfCandidate(Sema &S, const FunctionDecl *FD) { + if (!isFunctionAlwaysEnabled(S.Context, FD)) { + // FIXME(gbiv): This lacks tests. We should add tests. + S.Diag(FD->getLocStart(), + diag::note_addrof_ovl_candidate_disabled_by_enable_if_attr); + return true; + } + + auto HasPassObjSize = std::mem_fn(&ParmVarDecl::hasAttr); + + auto I = std::find_if(FD->param_begin(), FD->param_end(), HasPassObjSize); + if (I == FD->param_end()) { + assert(S.checkAddressOfFunctionIsAvailable(FD)); + return false; + } + + // Add one because we're showing this to the user + unsigned Dist = std::distance(FD->param_begin(), I) + 1; + S.Diag(FD->getLocation(), + diag::note_ovl_candidate_has_pass_object_size_params) + << Dist; + return true; +} + + // Notes the location of an overload candidate. void Sema::NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType, bool TakingAddress) { + if (TakingAddress && diagnoseBadAddressOfCandidate(*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); } @@ -8825,7 +8911,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"); @@ -9023,7 +9109,11 @@ return; } } - + + if (TakingCandidateAddress && + diagnoseBadAddressOfCandidate(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 @@ -9134,7 +9224,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()) || @@ -9302,6 +9393,11 @@ } } } + + if (TakingCandidateAddress && isa(Templated) && + diagnoseBadAddressOfCandidate(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 @@ -9322,14 +9418,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. @@ -9410,7 +9507,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. @@ -9438,7 +9536,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) @@ -9456,7 +9554,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 @@ -9827,7 +9925,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 { @@ -9895,9 +9994,11 @@ /// 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() { @@ -9950,7 +10051,7 @@ assert(Cand->Specialization && "Non-matching built-in candidates are not added to Cands."); - Cand->NoteDeductionFailure(S); + Cand->NoteDeductionFailure(S, ForTakingAddress); } if (I != E) @@ -10015,7 +10116,7 @@ HasComplained(false), OvlExprInfo(OverloadExpr::find(SourceExpr)), OvlExpr(OvlExprInfo.Expression), - FailedCandidates(OvlExpr->getNameLoc()) { + FailedCandidates(OvlExpr->getNameLoc(), /*ForTakingAddress=*/true) { ExtractUnqualifiedFunctionTypeFromTargetType(); if (TargetFunctionType->isFunctionType()) { @@ -10149,10 +10250,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)); @@ -10185,7 +10285,7 @@ return false; } - if (!isFunctionAlwaysEnabled(S.Context, FunDecl)) + if (!S.checkAddressOfFunctionIsAvailable(FunDecl)) return false; QualType ResultTy; @@ -10308,8 +10408,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()); } } @@ -11019,6 +11120,22 @@ if (!Recovery.isInvalid()) return Recovery; + // 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(); Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ 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. @@ -6811,7 +6812,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: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -5468,9 +5468,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: test/CodeGen/pass-object-size.c =================================================================== --- /dev/null +++ test/CodeGen/pass-object-size.c @@ -0,0 +1,339 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -O0 %s -o - 2>&1 | FileCheck %s + +typedef unsigned long size_t; + +struct Foo { + int t[10]; +}; + +#define PS(N) __attribute__((pass_object_size(N))) + +int gi = 0; + +// CHECK-LABEL: define i32 @ObjectSize0(i8* %{{.*}}, i64) +int ObjectSize0(void *const p PS(0)) { + // CHECK-NOT: @llvm.objectsize + return __builtin_object_size(p, 0); +} + +// CHECK-LABEL: define i32 @ObjectSize1(i8* %{{.*}}, i64) +int ObjectSize1(void *const p PS(1)) { + // CHECK-NOT: @llvm.objectsize + return __builtin_object_size(p, 1); +} + +// CHECK-LABEL: define i32 @ObjectSize2(i8* %{{.*}}, i64) +int ObjectSize2(void *const p PS(2)) { + // CHECK-NOT: @llvm.objectsize + return __builtin_object_size(p, 2); +} + +// CHECK-LABEL: define i32 @ObjectSize3(i8* %{{.*}}, i64) +int ObjectSize3(void *const p PS(3)) { + // CHECK-NOT: @llvm.objectsize + return __builtin_object_size(p, 3); +} + +// CHECK-LABEL: define void @test1 +void test1() { + struct Foo t[10]; + + // CHECK: call i32 @ObjectSize0(i8* %{{.*}}, i64 360) + gi = ObjectSize0(&t[1]); + // CHECK: call i32 @ObjectSize1(i8* %{{.*}}, i64 360) + gi = ObjectSize1(&t[1]); + // CHECK: call i32 @ObjectSize2(i8* %{{.*}}, i64 360) + gi = ObjectSize2(&t[1]); + // CHECK: call i32 @ObjectSize3(i8* %{{.*}}, i64 360) + gi = ObjectSize3(&t[1]); + + // CHECK: call i32 @ObjectSize0(i8* %{{.*}}, i64 356) + gi = ObjectSize0(&t[1].t[1]); + // CHECK: call i32 @ObjectSize1(i8* %{{.*}}, i64 36) + gi = ObjectSize1(&t[1].t[1]); + // CHECK: call i32 @ObjectSize2(i8* %{{.*}}, i64 356) + gi = ObjectSize2(&t[1].t[1]); + // CHECK: call i32 @ObjectSize3(i8* %{{.*}}, i64 36) + gi = ObjectSize3(&t[1].t[1]); +} + +// CHECK-LABEL: define void @test2 +void test2(struct Foo *t) { + // CHECK: call i32 @ObjectSize1(i8* %{{.*}}, i64 36) + gi = ObjectSize1(&t->t[1]); + // CHECK: call i32 @ObjectSize3(i8* %{{.*}}, i64 36) + gi = ObjectSize3(&t->t[1]); +} + +// CHECK-LABEL: define i32 @_Z27NoViableOverloadObjectSize0Pv +int NoViableOverloadObjectSize0(void *const p) __attribute__((overloadable)) { + // CHECK: @llvm.objectsize + return __builtin_object_size(p, 0); +} + +// CHECK-LABEL: define i32 @_Z27NoViableOverloadObjectSize1Pv +int NoViableOverloadObjectSize1(void *const p) __attribute__((overloadable)) { + // CHECK: @llvm.objectsize + return __builtin_object_size(p, 1); +} + +// CHECK-LABEL: define i32 @_Z27NoViableOverloadObjectSize2Pv +int NoViableOverloadObjectSize2(void *const p) __attribute__((overloadable)) { + // CHECK: @llvm.objectsize + return __builtin_object_size(p, 2); +} + +// CHECK-LABEL: define i32 @_Z27NoViableOverloadObjectSize3Pv +int NoViableOverloadObjectSize3(void *const p) __attribute__((overloadable)) { + // CHECK-NOT: @llvm.objectsize + return __builtin_object_size(p, 3); +} + +// CHECK-LABEL: define i32 @_Z27NoViableOverloadObjectSize0Pv +// CHECK-NOT: @llvm.objectsize +int NoViableOverloadObjectSize0(void *const p PS(0)) + __attribute__((overloadable)) { + return __builtin_object_size(p, 0); +} + +int NoViableOverloadObjectSize1(void *const p PS(1)) + __attribute__((overloadable)) { + return __builtin_object_size(p, 1); +} + +int NoViableOverloadObjectSize2(void *const p PS(2)) + __attribute__((overloadable)) { + return __builtin_object_size(p, 2); +} + +int NoViableOverloadObjectSize3(void *const p PS(3)) + __attribute__((overloadable)) { + return __builtin_object_size(p, 3); +} + +const static int SHOULDNT_BE_CALLED = -100; +int NoViableOverloadObjectSize0(void *const p PS(0)) + __attribute__((overloadable, enable_if(p == 0, "never selected"))) { + return SHOULDNT_BE_CALLED; +} + +int NoViableOverloadObjectSize1(void *const p PS(1)) + __attribute__((overloadable, enable_if(p == 0, "never selected"))) { + return SHOULDNT_BE_CALLED; +} + +int NoViableOverloadObjectSize2(void *const p PS(2)) + __attribute__((overloadable, enable_if(p == 0, "never selected"))) { + return SHOULDNT_BE_CALLED; +} + +int NoViableOverloadObjectSize3(void *const p PS(3)) + __attribute__((overloadable, enable_if(p == 0, "never selected"))) { + return SHOULDNT_BE_CALLED; +} + +// CHECK-LABEL: define void @test3 +void test3() { + struct Foo t[10]; + + // CHECK: call i32 @_Z27NoViableOverloadObjectSize0PvU17pass_object_size0(i8* %{{.*}}, i64 360) + gi = NoViableOverloadObjectSize0(&t[1]); + // CHECK: call i32 @_Z27NoViableOverloadObjectSize1PvU17pass_object_size1(i8* %{{.*}}, i64 360) + gi = NoViableOverloadObjectSize1(&t[1]); + // CHECK: call i32 @_Z27NoViableOverloadObjectSize2PvU17pass_object_size2(i8* %{{.*}}, i64 360) + gi = NoViableOverloadObjectSize2(&t[1]); + // CHECK: call i32 @_Z27NoViableOverloadObjectSize3PvU17pass_object_size3(i8* %{{.*}}, i64 360) + gi = NoViableOverloadObjectSize3(&t[1]); + + // CHECK: call i32 @_Z27NoViableOverloadObjectSize0PvU17pass_object_size0(i8* %{{.*}}, i64 356) + gi = NoViableOverloadObjectSize0(&t[1].t[1]); + // CHECK: call i32 @_Z27NoViableOverloadObjectSize1PvU17pass_object_size1(i8* %{{.*}}, i64 36) + gi = NoViableOverloadObjectSize1(&t[1].t[1]); + // CHECK: call i32 @_Z27NoViableOverloadObjectSize2PvU17pass_object_size2(i8* %{{.*}}, i64 356) + gi = NoViableOverloadObjectSize2(&t[1].t[1]); + // CHECK: call i32 @_Z27NoViableOverloadObjectSize3PvU17pass_object_size3(i8* %{{.*}}, i64 36) + gi = NoViableOverloadObjectSize3(&t[1].t[1]); +} + +// CHECK-LABEL: define void @test4 +void test4(struct Foo *t) { + // CHECK: call i32 @_Z27NoViableOverloadObjectSize0PvU17pass_object_size0(i8* %{{.*}}, i64 %{{.*}}) + gi = NoViableOverloadObjectSize0(&t[1]); + // CHECK: call i32 @_Z27NoViableOverloadObjectSize1PvU17pass_object_size1(i8* %{{.*}}, i64 %{{.*}}) + gi = NoViableOverloadObjectSize1(&t[1]); + // CHECK: call i32 @_Z27NoViableOverloadObjectSize2PvU17pass_object_size2(i8* %{{.*}}, i64 %{{.*}}) + gi = NoViableOverloadObjectSize2(&t[1]); + // CHECK: call i32 @_Z27NoViableOverloadObjectSize3PvU17pass_object_size3(i8* %{{.*}}, i64 0) + gi = NoViableOverloadObjectSize3(&t[1]); + + // CHECK: call i32 @_Z27NoViableOverloadObjectSize0PvU17pass_object_size0(i8* %{{.*}}, i64 %{{.*}}) + gi = NoViableOverloadObjectSize0(&t[1].t[1]); + // CHECK: call i32 @_Z27NoViableOverloadObjectSize1PvU17pass_object_size1(i8* %{{.*}}, i64 36) + gi = NoViableOverloadObjectSize1(&t[1].t[1]); + // CHECK: call i32 @_Z27NoViableOverloadObjectSize2PvU17pass_object_size2(i8* %{{.*}}, i64 %{{.*}}) + gi = NoViableOverloadObjectSize2(&t[1].t[1]); + // CHECK: call i32 @_Z27NoViableOverloadObjectSize3PvU17pass_object_size3(i8* %{{.*}}, i64 36) + gi = NoViableOverloadObjectSize3(&t[1].t[1]); +} + +void test5() { + struct Foo t[10]; + + int (*f)(void *) = &NoViableOverloadObjectSize0; + gi = f(&t[1]); +} + +// CHECK-LABEL: define i32 @IndirectObjectSize0 +int IndirectObjectSize0(void *const p PS(0)) { + // CHECK: call i32 @ObjectSize0(i8* %{{.*}}, i64 %{{.*}}) + // CHECK-NOT: @llvm.objectsize + return ObjectSize0(p); +} + +// CHECK-LABEL: define i32 @IndirectObjectSize1 +int IndirectObjectSize1(void *const p PS(1)) { + // CHECK: call i32 @ObjectSize1(i8* %{{.*}}, i64 %{{.*}}) + // CHECK-NOT: @llvm.objectsize + return ObjectSize1(p); +} + +// CHECK-LABEL: define i32 @IndirectObjectSize2 +int IndirectObjectSize2(void *const p PS(2)) { + // CHECK: call i32 @ObjectSize2(i8* %{{.*}}, i64 %{{.*}}) + // CHECK-NOT: @llvm.objectsize + return ObjectSize2(p); +} + +// CHECK-LABEL: define i32 @IndirectObjectSize3 +int IndirectObjectSize3(void *const p PS(3)) { + // CHECK: call i32 @ObjectSize3(i8* %{{.*}}, i64 %{{.*}}) + // CHECK-NOT: @llvm.objectsize + return ObjectSize3(p); +} + +int Overload0(void *, size_t, void *, size_t); +int OverloadNoSize(void *, void *); + +int OverloadedObjectSize(void *const p PS(0), + void *const c PS(0)) + __attribute__((overloadable)) __asm__("Overload0"); + +int OverloadedObjectSize(void *const p, void *const c) + __attribute__((overloadable)) __asm__("OverloadNoSize"); + +// CHECK-LABEL: define void @test6 +void test6() { + int known[10], *opaque; + + // CHECK: call i32 @"\01Overload0" + gi = OverloadedObjectSize(&known[0], &known[0]); + + // CHECK: call i32 @"\01Overload0" + gi = OverloadedObjectSize(&known[0], opaque); + + // CHECK: call i32 @"\01Overload0" + gi = OverloadedObjectSize(opaque, &known[0]); + + // CHECK: call i32 @"\01Overload0" + gi = OverloadedObjectSize(opaque, opaque); +} + +int Identity(void *p, size_t i) { return i; } + +// CHECK-NOT: define void @AsmObjectSize +int AsmObjectSize0(void *const p PS(0)) __asm__("Identity"); + +int AsmObjectSize1(void *const p PS(1)) __asm__("Identity"); + +int AsmObjectSize2(void *const p PS(2)) __asm__("Identity"); + +int AsmObjectSize3(void *const p PS(3)) __asm__("Identity"); + +// CHECK-LABEL: define void @test7 +void test7() { + struct Foo t[10]; + + // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 360) + gi = AsmObjectSize0(&t[1]); + // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 360) + gi = AsmObjectSize1(&t[1]); + // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 360) + gi = AsmObjectSize2(&t[1]); + // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 360) + gi = AsmObjectSize3(&t[1]); + + // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 356) + gi = AsmObjectSize0(&t[1].t[1]); + // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 36) + gi = AsmObjectSize1(&t[1].t[1]); + // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 356) + gi = AsmObjectSize2(&t[1].t[1]); + // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 36) + gi = AsmObjectSize3(&t[1].t[1]); +} + +// CHECK-LABEL: define void @test8 +void test8(struct Foo *t) { + // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 36) + gi = AsmObjectSize1(&t[1].t[1]); + // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 36) + gi = AsmObjectSize3(&t[1].t[1]); +} + +void DifferingObjectSize0(void *const p __attribute__((pass_object_size(0)))); +void DifferingObjectSize1(void *const p __attribute__((pass_object_size(1)))); +void DifferingObjectSize2(void *const p __attribute__((pass_object_size(2)))); +void DifferingObjectSize3(void *const p __attribute__((pass_object_size(3)))); + +// CHECK-LABEL: define void @test9 +void test9(void *const p __attribute__((pass_object_size(0)))) { + // CHECK: @llvm.objectsize + DifferingObjectSize2(p); + + // CHECK-NOT: @llvm.objectsize + DifferingObjectSize0(p); + DifferingObjectSize1(p); + + // CHECK: call void @DifferingObjectSize3(i8* %{{.*}}, i64 0) + DifferingObjectSize3(p); +} + +// CHECK-LABEL: define void @test10 +void test10(void *const p __attribute__((pass_object_size(1)))) { + // CHECK: @llvm.objectsize + DifferingObjectSize2(p); + // CHECK: @llvm.objectsize + DifferingObjectSize0(p); + + // CHECK-NOT: @llvm.objectsize + DifferingObjectSize1(p); + + // CHECK: call void @DifferingObjectSize3(i8* %{{.*}}, i64 0) + DifferingObjectSize3(p); +} + +// CHECK-LABEL: define void @test11 +void test11(void *const p __attribute__((pass_object_size(2)))) { + // CHECK: @llvm.objectsize + DifferingObjectSize0(p); + // CHECK: @llvm.objectsize + DifferingObjectSize1(p); + + // CHECK-NOT: @llvm.objectsize + DifferingObjectSize2(p); + + // CHECK: call void @DifferingObjectSize3(i8* %{{.*}}, i64 0) + DifferingObjectSize3(p); +} + +// CHECK-LABEL: define void @test12 +void test12(void *const p __attribute__((pass_object_size(3)))) { + // CHECK: @llvm.objectsize + DifferingObjectSize0(p); + // CHECK: @llvm.objectsize + DifferingObjectSize1(p); + + // CHECK-NOT: @llvm.objectsize + DifferingObjectSize2(p); + DifferingObjectSize3(p); +} Index: test/CodeGenCXX/pass-object-size.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/pass-object-size.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -O0 %s -o - 2>&1 -std=c++11 | FileCheck %s + +int gi; + +namespace lambdas { +// CHECK-LABEL: define void @_ZN7lambdas7LambdasEPc +void Lambdas(char *ptr) { + auto L1 = [](void *const p __attribute__((pass_object_size(0)))) { + return __builtin_object_size(p, 0); + }; + + int i = 0; + auto L2 = [&i](void *const p __attribute__((pass_object_size(0)))) { + return __builtin_object_size(p, 0) + i; + }; + + // CHECK: @llvm.objectsize + gi = L1(ptr); + // CHECK: @llvm.objectsize + gi = L2(ptr); +} + +// CHECK-DAG: define internal i64 @"_ZZN7lambdas7LambdasEPcENK3$_0clEPvU17pass_object_size0" +// CHECK-NOT: call i64 @llvm.objectsize +// CHECK-DAG: define internal i64 @"_ZZN7lambdas7LambdasEPcENK3$_1clEPvU17pass_object_size0" +// CHECK-NOT: call i64 @llvm.objectsize +} Index: test/Sema/pass-object-size.c =================================================================== --- /dev/null +++ test/Sema/pass-object-size.c @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -triple x86_64-linux-gnu -Wincompatible-pointer-types +// +// Tests for the pass_object_size attribute +// Non-failure cases are covered in test/CodeGen/pass-object-size.c + +void a(void *p __attribute__((pass_object_size))); //expected-error{{'pass_object_size' attribute takes one argument}} +void b(void *p __attribute__((pass_object_size(1.0)))); //expected-error{{'pass_object_size' attribute requires parameter 1 to be an integer constant}} + +void c(void *p __attribute__((pass_object_size(4)))); //expected-error{{'pass_object_size' attribute requires integer constant between 0 and 3 inclusive}} +void d(void *p __attribute__((pass_object_size(-1)))); //expected-error{{'pass_object_size' attribute requires integer constant between 0 and 3 inclusive}} + +void e(void *p __attribute__((pass_object_size(1ULL<<32)))); //expected-error{{integer constant expression evaluates to value 4294967296 that cannot be represented in a 32-bit unsigned integer type}} + +void f(char p __attribute__((pass_object_size(0)))); //expected-error{{'pass_object_size' attribute only applies to constant pointer arguments}} +void g(const char p __attribute__((pass_object_size(0)))); //expected-error{{'pass_object_size' attribute only applies to constant pointer arguments}} +void h(char *p __attribute__((pass_object_size(0)))) {} //expected-error{{pass_object_size attribute only applies to constant pointer arguments}} +void i(char *p __attribute__((pass_object_size(0)))); // OK -- const is only necessary on definitions, not decls. +void j(char *p __attribute__((pass_object_size(0), pass_object_size(1)))); //expected-error{{'pass_object_size' attribute can only be applied once per parameter}} + +#define PS(N) __attribute__((pass_object_size(N))) +#define overloaded __attribute__((overloadable)) +void Overloaded(void *p PS(0)) overloaded; //expected-note{{previous declaration is here}} +void Overloaded(void *p PS(1)) overloaded; //expected-error{{conflicting pass_object_size attributes on parameters}} +void Overloaded2(void *p PS(1), void *p2 PS(0)) overloaded; //expected-note{{previous declaration is here}} +void Overloaded2(void *p PS(0), void *p2 PS(1)) overloaded; //expected-error{{conflicting pass_object_size attributes on parameters}} + +void Overloaded3(void *p PS(0), void *p2) overloaded; //expected-note{{previous declaration is here}} +void Overloaded3(void *p, void *p2 PS(0)) overloaded; //expected-error{{conflicting pass_object_size attributes on parameters}} + +void TakeFn(void (*)(void *)); +void TakeFnOvl(void (*)(void *)) overloaded; +void TakeFnOvl(void (*)(int *)) overloaded; + +void NotOverloaded(void *p PS(0)); +void IsOverloaded(void *p PS(0)) overloaded; +void IsOverloaded(char *p) overloaded; +void FunctionPtrs() { + void (*p)(void *) = NotOverloaded; //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}} + void (*p2)(void *) = &NotOverloaded; //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}} + + void (*p3)(void *) = IsOverloaded; //expected-error{{initializing 'void (*)(void *)' with an expression of incompatible type ''}} expected-note@-6{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@-5{{type mismatch}} + void (*p4)(void *) = &IsOverloaded; //expected-error{{initializing 'void (*)(void *)' with an expression of incompatible type ''}} expected-note@-7{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@-6{{type mismatch}} + + void (*p5)(char *) = IsOverloaded; + void (*p6)(char *) = &IsOverloaded; + + TakeFn(NotOverloaded); //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}} + TakeFn(&NotOverloaded); //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}} + + TakeFnOvl(NotOverloaded); //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}} + TakeFnOvl(&NotOverloaded); //expected-error{{cannot take address of function 'NotOverloaded' because parameter 1 has pass_object_size attribute}} +} Index: test/SemaCXX/init-priority-attr.cpp =================================================================== --- test/SemaCXX/init-priority-attr.cpp +++ 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}} Index: test/SemaCXX/pass-object-size.cpp =================================================================== --- /dev/null +++ test/SemaCXX/pass-object-size.cpp @@ -0,0 +1,122 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 + +namespace simple { +int Foo(void *const p __attribute__((pass_object_size(0)))); + +int OvlFoo(void *const p __attribute__((pass_object_size(0)))); +int OvlFoo(void *const p, int); + +struct Statics { + static int Foo(void *const p __attribute__((pass_object_size(0)))); + static int OvlFoo(void *const p __attribute__((pass_object_size(0)))); + static int OvlFoo(void *const p __attribute__((pass_object_size(1)))); // expected-error{{conflicting pass_object_size attributes on parameters}} expected-note@-1{{previous declaration is here}} + static int OvlFoo(double *p); +}; + +struct Members { + int Foo(void *const p __attribute__((pass_object_size(0)))); + int OvlFoo(void *const p __attribute__((pass_object_size(0)))); + int OvlFoo(void *const p, int); +}; + +void Decls() { + int (*A)(void *) = &Foo; //expected-error{{cannot take address of function 'Foo' because parameter 1 has pass_object_size attribute}} + int (*B)(void *) = Foo; //expected-error{{cannot take address of function 'Foo' because parameter 1 has pass_object_size attribute}} + + int (*C)(void *) = &OvlFoo; //expected-error{{address of overloaded function 'OvlFoo' does not match required type 'int (void *)'}} expected-note@6{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@7{{candidate function has different number of parameters (expected 1 but has 2)}} + int (*D)(void *) = OvlFoo; //expected-error{{address of overloaded function 'OvlFoo' does not match required type 'int (void *)'}} expected-note@6{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@7{{candidate function has different number of parameters (expected 1 but has 2)}} + + int (*E)(void *) = &Statics::Foo; //expected-error{{cannot take address of function 'Foo' because parameter 1 has pass_object_size attribute}} + int (*F)(void *) = &Statics::OvlFoo; //expected-error{{address of overloaded function 'OvlFoo' does not match required type 'int (void *)'}} expected-note@11{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@13{{candidate function has type mismatch at 1st parameter (expected 'void *' but has 'double *')}} + + int (*G)(void *) = &Members::Foo; //expected-error{{cannot take address of function 'Foo' because parameter 1 has pass_object_size attribute}} + int (*H)(void *) = &Members::OvlFoo; //expected-error{{address of overloaded function 'OvlFoo' does not match required type 'int (void *)'}} expected-note@18{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@19{{candidate function has different number of parameters (expected 1 but has 2)}} +} + +void Assigns() { + int (*A)(void *); + A = &Foo; //expected-error{{cannot take address of function 'Foo' because parameter 1 has pass_object_size attribute}} + A = Foo; //expected-error{{cannot take address of function 'Foo' because parameter 1 has pass_object_size attribute}} + + A = &OvlFoo; //expected-error{{assigning to 'int (*)(void *)' from incompatible type ''}} expected-note@6{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@7{{candidate function has different number of parameters (expected 1 but has 2)}} + A = OvlFoo; //expected-error{{assigning to 'int (*)(void *)' from incompatible type ''}} expected-note@6{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@7{{candidate function has different number of parameters (expected 1 but has 2)}} + + A = &Statics::Foo; //expected-error{{cannot take address of function 'Foo' because parameter 1 has pass_object_size attribute}} + A = &Statics::OvlFoo; //expected-error{{assigning to 'int (*)(void *)' from incompatible type ''}} expected-note@11{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@13{{candidate function has type mismatch at 1st parameter (expected 'void *' but has 'double *')}} + + int (Members::*M)(void *); + M = &Members::Foo; //expected-error{{cannot take address of function 'Foo' because parameter 1 has pass_object_size attribute}} + M = &Members::OvlFoo; //expected-error{{assigning to 'int (simple::Members::*)(void *)' from incompatible type ''}} expected-note@18{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} expected-note@19{{candidate function has different number of parameters (expected 1 but has 2)}} +} + +} // namespace simple + +namespace templates { +template +int Foo(void *const __attribute__((pass_object_size(0)))) { + return 0; +} + +template struct Bar { + template + int Foo(void *const __attribute__((pass_object_size(0)))) { + return 0; + } +}; + +void Decls() { + int (*A)(void *) = &Foo; //expected-error{{address of overloaded function 'Foo' does not match required type 'int (void *)'}} expected-note@56{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} + int (Bar::*B)(void *) = &Bar::Foo; //expected-error{{address of overloaded function 'Foo' does not match required type 'int (void *)'}} expected-note@62{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} +} + +void Assigns() { + int (*A)(void *); + A = &Foo; // expected-error{{assigning to 'int (*)(void *)' from incompatible type ''}} expected-note@56{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} + int (Bar::*B)(void *) = &Bar::Foo; //expected-error{{address of overloaded function 'Foo' does not match required type 'int (void *)'}} expected-note@62{{candidate address cannot be taken because parameter 1 has pass_object_size attribute}} +} +} // namespace templates + +namespace virt { +struct Foo { + virtual void DoIt(void *const p __attribute__((pass_object_size(0)))); +}; + +struct Bar : public Foo { + void DoIt(void *const p __attribute__((pass_object_size(0)))) override; // OK +}; + +struct Baz : public Foo { + void DoIt(void *const p) override; //expected-error{{non-virtual member function marked 'override' hides virtual member function}} expected-note@81{{hidden overloaded virtual function 'virt::Foo::DoIt' declared here}} +}; +} + +namespace why { +void TakeFn(void (*)(int, void *)); +void ObjSize(int, void *const __attribute__((pass_object_size(0)))); + +void Check() { + TakeFn(ObjSize); //expected-error{{cannot take address of function 'ObjSize' because parameter 2 has pass_object_size attribute}} + TakeFn(&ObjSize); //expected-error{{cannot take address of function 'ObjSize' because parameter 2 has pass_object_size attribute}} + TakeFn(*ObjSize); //expected-error{{cannot take address of function 'ObjSize' because parameter 2 has pass_object_size attribute}} + TakeFn(*****ObjSize); //expected-error{{cannot take address of function 'ObjSize' because parameter 2 has pass_object_size attribute}} + TakeFn(*****&ObjSize); //expected-error{{cannot take address of function 'ObjSize' because parameter 2 has pass_object_size attribute}} + + void (*P)(int, void *) = ****ObjSize; //expected-error{{cannot take address of function 'ObjSize' because parameter 2 has pass_object_size attribute}} + P = ****ObjSize; //expected-error{{cannot take address of function 'ObjSize' because parameter 2 has pass_object_size attribute}} + + TakeFn((ObjSize)); //expected-error{{cannot take address of function 'ObjSize' because parameter 2 has pass_object_size attribute}} + TakeFn((void*)ObjSize); //expected-error{{cannot take address of function 'ObjSize' because parameter 2 has pass_object_size attribute}} + TakeFn((decltype(P))((void*)ObjSize)); //expected-error{{cannot take address of function 'ObjSize' because parameter 2 has pass_object_size attribute}} +} +} + +namespace constexpr_support { +constexpr int getObjSizeType() { return 0; } +void Foo(void *p __attribute__((pass_object_size(getObjSizeType())))); +} + +namespace lambdas { +void Bar() { + (void)+[](void *const p __attribute__((pass_object_size(0)))) {}; //expected-error-re{{invalid argument type '(lambda at {{.*}})' to unary expression}} +} +}