This patch allows the user to declare the C99 math builtins that take the 
generic "real-floating" argument types.
A use case is described in https://llvm.org/bugs/show_bug.cgi?id=20958 

In C++98 and C89, you may redeclare these builtins to take any type, Clang will 
pretend these don't exist in these languages.
In C++11 and C99 (and derivations thereof), you can only redeclare the builtin 
with types that are "real-floating". If you try to redeclare with different 
types, a diagnostic is produced.

I also tweaked the __noop builtin to specify that it's arguments are custom 
type-checked. This was avoid a semantic error about passing non-POD types 
through a variadic argument, which is not appropriate for that builtin.

REPOSITORY
  rL LLVM

http://reviews.llvm.org/D9912

Files:
  include/clang/Basic/Builtins.def
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/AST/ASTContext.cpp
  lib/Sema/SemaDecl.cpp
  test/Sema/builtins-c89-valid.c
  test/Sema/builtins-c99-generic.c
  test/SemaCXX/builtins.cpp

EMAIL PREFERENCES
  http://reviews.llvm.org/settings/panel/emailpreferences/
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<DiagGroup<"assume">>;
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<unsigned, const Type *>;
+  /// \brief All the declaration types of builtin libc/libm functions seen.
+  SmallVector<BuiltinTypeBinding, 2> 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<QualType, 2> ArgTypes;
+
+  auto DefaultFunctionType = LangOpts.CPlusPlus11 ?
+    Context.getFunctionType(Context.IntTy, ArrayRef<QualType>(), 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<FunctionProtoType>(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(...);
_______________________________________________
cfe-commits mailing list
cfe-commits@cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to