Index: include/clang/Basic/Builtins.def =================================================================== --- include/clang/Basic/Builtins.def +++ include/clang/Basic/Builtins.def @@ -710,7 +710,7 @@ LANGBUILTIN(_InterlockedExchange, "LiLiD*Li", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_InterlockedExchangePointer, "v*v*D*v*", "n", ALL_MS_LANGUAGES) LANGBUILTIN(_InterlockedIncrement, "LiLiD*", "n", ALL_MS_LANGUAGES) -LANGBUILTIN(__noop, "i.", "n", ALL_MS_LANGUAGES) +LANGBUILTIN(__noop, "i.", "nt", ALL_MS_LANGUAGES) LANGBUILTIN(__readfsdword, "ULiULi", "n", ALL_MS_LANGUAGES) LANGBUILTIN(__va_start, "vc**.", "nt", ALL_MS_LANGUAGES) Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -470,7 +470,12 @@ def note_strncat_wrong_size : Note< "change the argument to be the free space in the destination buffer minus " "the terminating null byte">; - +def err_incorrect_args_builtin_redecl : Error< + "%0 is a builtin that takes %1 argument%s1, it has been redeclared with %2 " + "argument%s2">; +def err_builtin_expects_realfloating_type : Error< + "%0 expects either double, long double or float argument types, but has been " + "given an argument of type %1">; def warn_assume_side_effects : Warning< "the argument to %0 has side effects that will be discarded">, InGroup>; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -8707,6 +8707,32 @@ mutable IdentifierInfo *Ident_super; mutable IdentifierInfo *Ident___float128; +private: + using BuiltinTypeBinding = std::pair; + /// \brief All the declaration types of builtin libc/libm functions seen. + SmallVector SavedBuiltinFunctionDeclarations; + + /// \brief Create an appropriate declaration for a type-generic builtin. + /// If a user-specified declaration of a type-generic builtin has + /// been seen in the TU (tracked by \c SavedBuiltinFunctionDeclarations), use + /// that if it contains legal arguments. If the argument types are illegal + /// emit a variadic declaration in C++, and a no-arg declaration in C as + /// fall-back cases. + /// + /// If the language is not C++11 or C99 (or derivations thereof), return + /// whatever \c GetBuiltinType would return. + /// + /// \param [in] II The \c IdentifierInfo for the declaration name. + /// \param [in] ID The identifier ID for the detected compiler builtin. + /// \param [in] Loc The location this declaration was found at. + /// \param [in] BT The type computed from GetBuiltinType, used a fall-back + /// case if we can't be more specific. + /// + /// \return A declaration type for this builtin, or \c BT if no improvement + /// can be made. + QualType CheckGenericBuiltinRedeclaration(IdentifierInfo *II, unsigned ID, + SourceLocation Loc, QualType BT); + protected: friend class Parser; friend class InitializationSequence; Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -7889,8 +7889,8 @@ bool Variadic = (TypeStr[0] == '.'); - // We really shouldn't be making a no-proto type here, especially in C++. - if (ArgTypes.empty() && Variadic) + // We really shouldn't be making a no-proto type here. + if (ArgTypes.empty() && Variadic && !LangOpts.CPlusPlus) return getFunctionNoProtoType(ResType, EI); FunctionProtoType::ExtProtoInfo EPI; Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -1703,6 +1703,153 @@ llvm_unreachable("unhandled error kind"); } + +QualType Sema::CheckGenericBuiltinRedeclaration(IdentifierInfo *II, unsigned ID, + SourceLocation Loc, + QualType BuiltinType) { + bool HasC99MathLib = LangOpts.CPlusPlus11 || LangOpts.C99; + + FunctionType::ExtInfo EI(CC_C); + FunctionProtoType::ExtProtoInfo EPI; + EPI.ExtInfo = EI; + EPI.Variadic = LangOpts.CPlusPlus11; + SmallVector ArgTypes; + + auto DefaultFunctionType = LangOpts.CPlusPlus11 ? + Context.getFunctionType(Context.IntTy, ArrayRef(), EPI) : + Context.getFunctionNoProtoType(Context.IntTy, EI); + + // Check SavedBuiltinFunctionDeclarations for an instance of the builtin + // ID. + auto SeenDecl = std::find_if(SavedBuiltinFunctionDeclarations.begin(), + SavedBuiltinFunctionDeclarations.end(), + [ID] (const BuiltinTypeBinding &B) { + return B.first == ID; + }); + + if (SeenDecl != SavedBuiltinFunctionDeclarations.end()) { + const Type *TP = SeenDecl->second; + + if (TP->isFunctionProtoType()) { + if (const FunctionProtoType *FPT = dyn_cast(TP)) { + // Ignore if we're looking at a variadic prototype like T name(...) + // In C99, the semantic checks for function merging will complain + // about having no parameter before the ellipses. + if (FPT->getNumParams()) { + switch (ID) { + case Builtin::BI__builtin_isgreater: + case Builtin::BI__builtin_isgreaterequal: + case Builtin::BI__builtin_isless: + case Builtin::BI__builtin_islessequal: + case Builtin::BI__builtin_islessgreater: + case Builtin::BI__builtin_isunordered: + // These builtins take two real-floating arguments, all that is + // done here is to check that if they've been declared with + // two real-floating arguments, use that as the declaration of + // the builtin. + { + if (FPT->getNumParams() != 2 && HasC99MathLib) { + Diag(Loc, diag::err_incorrect_args_builtin_redecl) + << II->getName() + << 2 + << FPT->getNumParams(); + return DefaultFunctionType; + } + + QualType QT1 = FPT->getParamType(0); + QualType QT2 = FPT->getParamType(1); + ArgTypes.push_back(QT1); + ArgTypes.push_back(QT2); + + if (QT1->isRealFloatingType() && QT2->isRealFloatingType()) { + EPI.Variadic = false; + return Context.getFunctionType(Context.IntTy, ArgTypes, EPI); + } else { + if (HasC99MathLib) { + // Emit a diagnostic here since the user has attempted to + // redeclare a standard builtin with erroneous argument types. + if (QT1->isRealFloatingType()) + QT1 = QT2; + Diag(Loc, diag::err_builtin_expects_realfloating_type) + << II->getName() + << QT1.getAsString(); + + + return DefaultFunctionType; + } else { + // Return whatever the user defined. C99 builtins don't exist. + EPI.Variadic = false; + return Context.getFunctionType(Context.IntTy, ArgTypes, EPI); + } + } + break; + } + case Builtin::BI__builtin_isfinite: + case Builtin::BI__builtin_isinf: + case Builtin::BI__builtin_isinf_sign: + case Builtin::BI__builtin_isnormal: + case Builtin::BI__builtin_isnan: + { + QualType QT = FPT->getParamType(0); + ArgTypes.push_back(QT); + + if (QT->isRealFloatingType()) { + EPI.Variadic = false; + return Context.getFunctionType(Context.IntTy, ArgTypes, EPI); + } else { + + if (HasC99MathLib) { + // Emit a diagnostic here since the user has attempted to + // redeclare a standard builtin with an erroneous argument type. + Diag(Loc, diag::err_builtin_expects_realfloating_type) + << II->getName() + << QT.getAsString(); + return DefaultFunctionType; + } else { + // Return whatever the user defined. C99 builtins don't exist. + EPI.Variadic = false; + return Context.getFunctionType(Context.IntTy, ArgTypes, EPI); + } + } + break; + } + } + } + } + } else if (TP->isFunctionNoProtoType() && HasC99MathLib) { + // This is a K&R style int __builtin_name() declaration. + switch (ID) { + case Builtin::BI__builtin_isgreater: + case Builtin::BI__builtin_isgreaterequal: + case Builtin::BI__builtin_isless: + case Builtin::BI__builtin_islessequal: + case Builtin::BI__builtin_islessgreater: + case Builtin::BI__builtin_isunordered: + if (HasC99MathLib) + Diag(Loc, diag::err_incorrect_args_builtin_redecl) + << II->getName() + << 2 + << 0; + break; + case Builtin::BI__builtin_isfinite: + case Builtin::BI__builtin_isinf: + case Builtin::BI__builtin_isinf_sign: + case Builtin::BI__builtin_isnormal: + case Builtin::BI__builtin_isnan: + Diag(Loc, diag::err_incorrect_args_builtin_redecl) + << II->getName() + << 1 + << 0; + break; + } + return DefaultFunctionType; + } + } + + // Default to just returning whatever GetBuiltinType found. + return BuiltinType; +} + /// LazilyCreateBuiltin - The specified Builtin-ID was first used at /// file scope. lazily create a decl for it. ForRedeclaration is true /// if we're creating this built-in in anticipation of redeclaring the @@ -1714,6 +1861,9 @@ ASTContext::GetBuiltinTypeError Error; QualType R = Context.GetBuiltinType(ID, Error); + + R = CheckGenericBuiltinRedeclaration(II, ID, Loc, R); + if (Error) { if (ForRedeclaration) Diag(Loc, diag::warn_implicit_decl_requires_sysheader) @@ -4722,6 +4872,16 @@ if (IsLinkageLookup) Previous.clear(LookupRedeclarationWithLinkage); + if (IdentifierInfo *II = Name.getAsIdentifierInfo()) + if (unsigned BuiltinID = II->getBuiltinID()) + if (Context.BuiltinInfo.isLibFunction(BuiltinID)) + // Keep the declaration of this library function for later processing + // if/when we're asking to generate a declaration for it. (See + // LazilyCreateBuiltin). For type-generic builtins, keeping the user + // declaration around can inform what declaration we should + // actually create. + SavedBuiltinFunctionDeclarations.push_back(std::make_pair(BuiltinID, + R.getTypePtr())); LookupName(Previous, S, CreateBuiltins); } else { // Something like "int foo::x;" LookupQualifiedName(Previous, DC); Index: test/Sema/builtins-c89-valid.c =================================================================== --- /dev/null +++ test/Sema/builtins-c89-valid.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -std=c89 %s -fsyntax-only -verify -triple=i686-mingw32 +// expected-no-diagnostics +// In C89, pretend these builtins don't exist. + +int __builtin_isnan(double); +int __builtin_isinf(float); +int __builtin_isfinite(int); +int __builtin_isunordered(double, float); +int __builtin_islessgreater(); Index: test/Sema/builtins-c99-generic.c =================================================================== --- /dev/null +++ test/Sema/builtins-c99-generic.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -std=c99 %s -fsyntax-only -verify -triple=i686-mingw32 + +int __builtin_isnan(double); +int __builtin_isinf(float); + +int __builtin_isunordered(double, float); +int __builtin_islessgreater(long double, double); +int __builtin_isgreater(float, float); + +int __builtin_isfinite(int); // expected-error {{__builtin_isfinite expects either double, long double or float argument types, but has been given an argument of type int}} +int __builtin_isnormal(); // expected-error {{__builtin_isnormal is a builtin that takes 1 argument, it has been redeclared with 0 arguments}} + +int __builtin_islessequal(double); // expected-error {{__builtin_islessequal is a builtin that takes 2 arguments, it has been redeclared with 1 argument}} +int __builtin_isless(); // expected-error {{__builtin_isless is a builtin that takes 2 arguments, it has been redeclared with 0 arguments}} Index: test/SemaCXX/builtins.cpp =================================================================== --- test/SemaCXX/builtins.cpp +++ test/SemaCXX/builtins.cpp @@ -44,3 +44,9 @@ __noop(1); // expected-error {{use of undeclared}} __debugbreak(); // expected-error {{use of undeclared}} } + +// Make sure the processing for type-generic bultins allows the usual C++ overloading. +int __builtin_isnan(double); +int __builtin_isnan(long double); +int __builtin_isnan(float); +int __builtin_isnan(...);