]> granicus.if.org Git - clang/commitdiff
Initial implementation of function overloading in C.
authorDouglas Gregor <dgregor@apple.com>
Wed, 11 Feb 2009 23:02:49 +0000 (23:02 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 11 Feb 2009 23:02:49 +0000 (23:02 +0000)
This commit adds a new attribute, "overloadable", that enables C++
function overloading in C. The attribute can only be added to function
declarations, e.g.,

  int *f(int) __attribute__((overloadable));

If the "overloadable" attribute exists on a function with a given
name, *all* functions with that name (and in that scope) must have the
"overloadable" attribute. Sets of overloaded functions with the
"overloadable" attribute then follow the normal C++ rules for
overloaded functions, e.g., overloads must have different
parameter-type-lists from each other.

When calling an overloaded function in C, we follow the same
overloading rules as C++, with three extensions to the set of standard
conversions:

  - A value of a given struct or union type T can be converted to the
    type T. This is just the identity conversion. (In C++, this would
    go through a copy constructor).
  - A value of pointer type T* can be converted to a value of type U*
    if T and U are compatible types. This conversion has Conversion
    rank (it's considered a pointer conversion in C).
  - A value of type T can be converted to a value of type U if T and U
    are compatible (and are not both pointer types). This conversion
    has Conversion rank (it's considered to be a new kind of
    conversion unique to C, a "compatible" conversion).

Known defects (and, therefore, next steps):
  1) The standard-conversion handling does not understand conversions
  involving _Complex or vector extensions, so it is likely to get
  these wrong. We need to add these conversions.
  2) All overloadable functions with the same name will have the same
  linkage name, which means we'll get a collision in the linker (if
  not sooner). We'll need to mangle the names of these functions.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@64336 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Attr.h
include/clang/Basic/DiagnosticSemaKinds.def
include/clang/Parse/AttributeList.h
lib/Parse/AttributeList.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclAttr.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaExprCXX.cpp
lib/Sema/SemaLookup.cpp
lib/Sema/SemaOverload.cpp
lib/Sema/SemaOverload.h

index 38e2b9ecb18162cc8d6d31faf5649296279b15ed..d446a352fb1ed84a78e99601e282cff9866e134f 100644 (file)
@@ -43,6 +43,7 @@ public:
     NoThrow,
     ObjCGC,
     ObjCNSObject,
+    Overloadable, // Clang-specific
     Packed,
     StdCall,
     TransparentUnion,
@@ -429,6 +430,14 @@ static bool classof(const Attr *A) { return A->getKind() == ObjCNSObject; }
 static bool classof(const ObjCNSObjectAttr *A) { return true; }
 };
   
+class OverloadableAttr : public Attr {
+public:
+  OverloadableAttr() : Attr(Overloadable) { }
+
+  static bool classof(const Attr *A) { return A->getKind() == Overloadable; }
+  static bool classof(const OverloadableAttr *) { return true; }
+};
+
 class BlocksAttr : public Attr {
 public:
   enum BlocksAttrTypes {
index 9cec83a41179e9af263dfcf5d1e8b1beb255b172..9319fb0a4e4244c44ebb52c539c56aab0fd2244f 100644 (file)
@@ -393,6 +393,12 @@ DIAG(err_attribute_cleanup_func_arg_incompatible_type, ERROR,
 // Clang-Specific Attributes
 DIAG(err_attribute_iboutlet_non_ivar, ERROR,
      "'iboutlet' attribute can only be applied to instance variables")
+DIAG(err_attribute_overloadable_not_function, ERROR,
+     "'overloadable' attribute can only be applied to a function")
+DIAG(err_attribute_overloadable_missing, ERROR,
+     "overloaded function %0 must have the 'overloadable' attribute")
+DIAG(note_attribute_overloadable_prev_overload, NOTE,
+     "previous overload of function is here")
 
 // Function Parameter Semantic Analysis.
 DIAG(err_param_with_void_type, ERROR,
index bca12a3e60a19b733122117c9cead0e95d3fca99..147fa1bd56ce141230dcdf791138524267d48d2d 100644 (file)
@@ -62,6 +62,7 @@ public:
     AT_nonnull,
     AT_noreturn,
     AT_nothrow,
+    AT_overloadable,      // Clang-specific
     AT_packed,
     AT_pure,
     AT_stdcall,
index eb0527dcf9cb2f21f4e99ecfee4f1f474859e614..954e93e056a97ee74674991869492f7ffaa18ddc 100644 (file)
@@ -98,6 +98,9 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) {
     if (!memcmp(Str, "constructor", 11)) return AT_constructor;
     if (!memcmp(Str, "unavailable", 11)) return AT_unavailable;
     break;
+  case 12:
+    if (!memcmp(Str, "overloadable", 12)) return AT_overloadable;
+    break;
   case 13:
     if (!memcmp(Str, "address_space", 13)) return AT_address_space;
     if (!memcmp(Str, "always_inline", 13)) return AT_always_inline;
index a3e0232987c194178bc20fee28cb1ae8aab4af3a..de06ca610cf14c15a263a76349390191b024b517 100644 (file)
@@ -113,6 +113,25 @@ void Sema::PopDeclContext() {
   CurContext = getContainingDC(CurContext);
 }
 
+/// \brief Determine whether we allow overloading of the function
+/// PrevDecl with another declaration.
+///
+/// This routine determines whether overloading is possible, not
+/// whether some new function is actually an overload. It will return
+/// true in C++ (where we can always provide overloads) or, as an
+/// extension, in C when the previous function is already an
+/// overloaded function declaration or has the "overloadable"
+/// attribute.
+static bool AllowOverloadingOfFunction(Decl *PrevDecl, ASTContext &Context) {
+  if (Context.getLangOptions().CPlusPlus)
+    return true;
+
+  if (isa<OverloadedFunctionDecl>(PrevDecl))
+    return true;
+
+  return PrevDecl->getAttr<OverloadableAttr>() != 0;
+}
+
 /// Add this decl to the scope shadowed decl chains.
 void Sema::PushOnScopeChains(NamedDecl *D, Scope *S) {
   // Move up the scope chain until we find the nearest enclosing
@@ -173,7 +192,8 @@ void Sema::PushOnScopeChains(NamedDecl *D, Scope *S) {
         return;
       }
     }
-  } else if (getLangOptions().CPlusPlus && isa<FunctionDecl>(D)) {
+  } else if (isa<FunctionDecl>(D) &&
+             AllowOverloadingOfFunction(D, Context)) {
     // We are pushing the name of a function, which might be an
     // overloaded name.
     FunctionDecl *FD = cast<FunctionDecl>(D);
@@ -1637,15 +1657,16 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
     
   // Merge the decl with the existing one if appropriate. Since C functions
   // are in a flat namespace, make sure we consider decls in outer scopes.
+  bool OverloadableAttrRequired = false;
   bool Redeclaration = false;
   if (PrevDecl &&
       (!getLangOptions().CPlusPlus||isDeclInScope(PrevDecl, DC, S))) {
-    // If C++, determine whether NewFD is an overload of PrevDecl or
+    // Determine whether NewFD is an overload of PrevDecl or
     // a declaration that requires merging. If it's an overload,
     // there's no more work to do here; we'll just add the new
     // function to the scope.
     OverloadedFunctionDecl::function_iterator MatchedDecl;
-    if (!getLangOptions().CPlusPlus ||
+    if (!AllowOverloadingOfFunction(PrevDecl, Context) || 
         !IsOverload(NewFD, PrevDecl, MatchedDecl)) {
       Decl *OldDecl = PrevDecl;
 
@@ -1672,6 +1693,12 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
         }
       }
     }
+
+    // If we're in C, this new declaration better have the
+    // "overloadable" attribute on it.
+    if (!getLangOptions().CPlusPlus && 
+        PrevDecl->getAttr<OverloadableAttr>())
+      OverloadableAttrRequired = true;
   }
 
   if (D.getCXXScopeSpec().isSet() &&
@@ -1712,6 +1739,17 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
   // (for example to check for conflicts, etc).
   ProcessDeclAttributes(NewFD, D);
 
+  if (OverloadableAttrRequired && !NewFD->getAttr<OverloadableAttr>()) {
+    // If a function name is overloadable in C, then every function
+    // with that name must be marked "overloadable".
+    Diag(NewFD->getLocation(), diag::err_attribute_overloadable_missing)
+      << NewFD;
+    if (PrevDecl)
+      Diag(PrevDecl->getLocation(), 
+           diag::note_attribute_overloadable_prev_overload);
+    NewFD->addAttr(new OverloadableAttr);
+  }
+
   if (getLangOptions().CPlusPlus) {
     // In C++, check default arguments now that we have merged decls. Unless
     // the lexical context is the class, because in this case this is done
index 2a6da06f710570a4eda7a25902c4c7b78a7ad594..b4e3dd99ac7747194e07e533072965f37b60aed3 100644 (file)
@@ -593,6 +593,21 @@ static void HandleObjCNSObject(Decl *d, const AttributeList &Attr, Sema &S) {
   d->addAttr(new ObjCNSObjectAttr);
 }
 
+static void 
+HandleOverloadableAttr(Decl *D, const AttributeList &Attr, Sema &S) {
+  if (Attr.getNumArgs() != 0) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1;
+    return;
+  }
+
+  if (!isa<FunctionDecl>(D)) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_overloadable_not_function);
+    return;
+  }
+
+  D->addAttr(new OverloadableAttr);
+}
+
 static void HandleBlocksAttr(Decl *d, const AttributeList &Attr, Sema &S) {
   if (!Attr.getParameterName()) {    
     S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_string)
@@ -1301,6 +1316,7 @@ static void ProcessDeclAttribute(Decl *D, const AttributeList &Attr, Sema &S) {
     HandleTransparentUnionAttr(D, Attr, S);
     break;
   case AttributeList::AT_objc_gc:     HandleObjCGCAttr    (D, Attr, S); break;
+  case AttributeList::AT_overloadable:HandleOverloadableAttr(D, Attr, S); break;
   case AttributeList::AT_nsobject:    HandleObjCNSObject  (D, Attr, S); break;
   case AttributeList::AT_blocks:      HandleBlocksAttr    (D, Attr, S); break;
   case AttributeList::AT_sentinel:    HandleSentinelAttr  (D, Attr, S); break;
index c2051a50166ca8d3e9d937d00202e5e2973e9c8f..4ac7214518c9a4e5f98506a69ddb9ffd0d954b21 100644 (file)
@@ -1909,12 +1909,16 @@ Sema::ActOnCallExpr(Scope *S, ExprArg fn, SourceLocation LParenLoc,
     Ovl = dyn_cast<OverloadedFunctionDecl>(DRExpr->getDecl());
   }
 
-  if (getLangOptions().CPlusPlus && (FDecl || Ovl || UnqualifiedName)) {
+  if (Ovl || (getLangOptions().CPlusPlus && (FDecl || UnqualifiedName))) {
     // We don't perform ADL for builtins.
     if (FDecl && FDecl->getIdentifier() && 
         FDecl->getIdentifier()->getBuiltinID())
       ADL = false;
 
+    // We don't perform ADL in C.
+    if (!getLangOptions().CPlusPlus)
+      ADL = false;
+
     if (Ovl || ADL) {
       FDecl = ResolveOverloadedCallFn(Fn, DRExpr? DRExpr->getDecl() : 0, 
                                       UnqualifiedName, LParenLoc, Args, 
index 4bfd4fa0b569bf17684896a964afebe8616b5815..6ce83315553c26b6c2a6533b9c84049c62ee84e4 100644 (file)
@@ -809,6 +809,8 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType,
   case ICK_Integral_Conversion:
   case ICK_Floating_Conversion:
   case ICK_Floating_Integral:
+  case ICK_Compatible_Conversion:
+      // FIXME: Go deeper to get the unqualified type!
     FromType = ToType.getUnqualifiedType();
     ImpCastExprToType(From, FromType);
     break;
index ee4c14d0ca49d509e96fb7b8c73499024ea96111..f4bfe5ca3c8ccb332ed210344e166317dfe2401f 100644 (file)
@@ -776,8 +776,29 @@ Sema::LookupName(Scope *S, DeclarationName Name, LookupNameKind NameKind,
     for (IdentifierResolver::iterator I = IdResolver.begin(Name),
                                    IEnd = IdResolver.end(); 
          I != IEnd; ++I)
-      if ((*I)->isInIdentifierNamespace(IDNS))
+      if ((*I)->isInIdentifierNamespace(IDNS)) {
+        if ((*I)->getAttr<OverloadableAttr>()) {
+          // If this declaration has the "overloadable" attribute, we
+          // might have a set of overloaded functions.
+
+          // Figure out what scope the identifier is in.
+          while (!(S->getFlags() & Scope::DeclScope) || !S->isDeclScope(*I))
+            S = S->getParent();
+
+          // Find the last declaration in this scope (with the same
+          // name, naturally).
+          IdentifierResolver::iterator LastI = I;
+          for (++LastI; LastI != IEnd; ++LastI) {
+            if (!S->isDeclScope(*LastI))
+              break;
+          }
+
+          return LookupResult::CreateLookupResult(Context, I, LastI);
+        }
+
+        // We have a single lookup result.
         return LookupResult::CreateLookupResult(Context, *I);
+      }
   } else {
     // Perform C++ unqualified name lookup.
     std::pair<bool, LookupResult> MaybeResult =
index e40455b5c6d5e0dd9ff8f898a85a649bd7e6009e..e1939d329e923e9f106010565599c3295b6ad971 100644 (file)
@@ -45,6 +45,7 @@ GetConversionCategory(ImplicitConversionKind Kind) {
     ICC_Conversion,
     ICC_Conversion,
     ICC_Conversion,
+    ICC_Conversion,
     ICC_Conversion
   };
   return Category[(int)Kind];
@@ -68,6 +69,7 @@ ImplicitConversionRank GetConversionRank(ImplicitConversionKind Kind) {
     ICR_Conversion,
     ICR_Conversion,
     ICR_Conversion,
+    ICR_Conversion,
     ICR_Conversion
   };
   return Rank[(int)Kind];
@@ -90,6 +92,7 @@ const char* GetImplicitConversionName(ImplicitConversionKind Kind) {
     "Pointer conversion",
     "Pointer-to-member conversion",
     "Boolean conversion",
+    "Compatible-types conversion",
     "Derived-to-base conversion"
   };
   return Name[Kind];
@@ -371,7 +374,8 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType,
   ImplicitConversionSequence ICS;
   if (IsStandardConversion(From, ToType, ICS.Standard))
     ICS.ConversionKind = ImplicitConversionSequence::StandardConversion;
-  else if (IsUserDefinedConversion(From, ToType, ICS.UserDefined, 
+  else if (getLangOptions().CPlusPlus &&
+           IsUserDefinedConversion(From, ToType, ICS.UserDefined, 
                                    !SuppressUserConversions, AllowExplicit)) {
     ICS.ConversionKind = ImplicitConversionSequence::UserDefinedConversion;
     // C++ [over.ics.user]p4:
@@ -429,10 +433,6 @@ Sema::IsStandardConversion(Expr* From, QualType ToType,
 {
   QualType FromType = From->getType();
 
-  // There are no standard conversions for class types, so abort early.
-  if (FromType->isRecordType() || ToType->isRecordType())
-    return false;
-
   // Standard conversions (C++ [conv])
   SCS.setAsIdentityConversion();
   SCS.Deprecated = false;
@@ -440,6 +440,15 @@ Sema::IsStandardConversion(Expr* From, QualType ToType,
   SCS.FromTypePtr = FromType.getAsOpaquePtr();
   SCS.CopyConstructor = 0;
 
+  // There are no standard conversions for class types in C++, so
+  // abort early. When overloading in C, however, we do permit 
+  if (FromType->isRecordType() || ToType->isRecordType()) {
+    if (getLangOptions().CPlusPlus)
+      return false;
+
+    // When we're overloading in C, we allow, as standard conversions, 
+  }
+
   // The first conversion can be an lvalue-to-rvalue conversion,
   // array-to-pointer conversion, or function-to-pointer conversion
   // (C++ 4p1).
@@ -455,7 +464,10 @@ Sema::IsStandardConversion(Expr* From, QualType ToType,
 
     // If T is a non-class type, the type of the rvalue is the
     // cv-unqualified version of T. Otherwise, the type of the rvalue
-    // is T (C++ 4.1p1).
+    // is T (C++ 4.1p1). C++ can't get here with class types; in C, we
+    // just strip the qualifiers because they don't matter.
+
+    // FIXME: Doesn't see through to qualifiers behind a typedef!
     FromType = FromType.getUnqualifiedType();
   }
   // Array-to-pointer conversion (C++ 4.2)
@@ -522,9 +534,10 @@ Sema::IsStandardConversion(Expr* From, QualType ToType,
   // point promotion, integral conversion, floating point conversion,
   // floating-integral conversion, pointer conversion,
   // pointer-to-member conversion, or boolean conversion (C++ 4p1).
+  // For overloading in C, this can also be a "compatible-type"
+  // conversion.
   bool IncompatibleObjC = false;
-  if (Context.getCanonicalType(FromType).getUnqualifiedType() ==
-      Context.getCanonicalType(ToType).getUnqualifiedType()) {
+  if (Context.hasSameUnqualifiedType(FromType, ToType)) {
     // The unqualified versions of the types are the same: there's no
     // conversion to do.
     SCS.Second = ICK_Identity;
@@ -580,6 +593,11 @@ Sema::IsStandardConversion(Expr* From, QualType ToType,
             FromType->isMemberPointerType())) {
     SCS.Second = ICK_Boolean_Conversion;
     FromType = Context.BoolTy;
+  }
+  // Compatible conversions (Clang extension for C function overloading)
+  else if (!getLangOptions().CPlusPlus && 
+           Context.typesAreCompatible(ToType, FromType)) {
+    SCS.Second = ICK_Compatible_Conversion;
   } else {
     // No second conversion required.
     SCS.Second = ICK_Identity;
@@ -847,6 +865,16 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
     return true;
   }
 
+  // When we're overloading in C, we allow a special kind of pointer
+  // conversion for compatible-but-not-identical pointee types.
+  if (!getLangOptions().CPlusPlus && 
+      Context.typesAreCompatible(FromPointeeType, ToPointeeType)) {
+    ConvertedType = BuildSimilarlyQualifiedPointerType(FromTypePtr, 
+                                                       ToPointeeType,
+                                                       ToType, Context);    
+    return true;
+  }
+
   // C++ [conv.ptr]p3:
   // 
   //   An rvalue of type "pointer to cv D," where D is a class type,
@@ -860,7 +888,8 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType,
   //
   // Note that we do not check for ambiguity or inaccessibility
   // here. That is handled by CheckPointerConversion.
-  if (FromPointeeType->isRecordType() && ToPointeeType->isRecordType() &&
+  if (getLangOptions().CPlusPlus &&
+      FromPointeeType->isRecordType() && ToPointeeType->isRecordType() &&
       IsDerivedFrom(FromPointeeType, ToPointeeType)) {
     ConvertedType = BuildSimilarlyQualifiedPointerType(FromTypePtr, 
                                                        ToPointeeType,
@@ -1756,18 +1785,7 @@ Sema::CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1,
 ImplicitConversionSequence 
 Sema::TryCopyInitialization(Expr *From, QualType ToType, 
                             bool SuppressUserConversions) {
-  if (!getLangOptions().CPlusPlus) {
-    // In C, copy initialization is the same as performing an assignment.
-    AssignConvertType ConvTy =
-      CheckSingleAssignmentConstraints(ToType, From);
-    ImplicitConversionSequence ICS;
-    if (getLangOptions().NoExtensions? ConvTy != Compatible
-                                     : ConvTy == Incompatible)
-      ICS.ConversionKind = ImplicitConversionSequence::BadConversion;
-    else
-      ICS.ConversionKind = ImplicitConversionSequence::StandardConversion;
-    return ICS;
-  } else if (ToType->isReferenceType()) {
+  if (ToType->isReferenceType()) {
     ImplicitConversionSequence ICS;
     CheckReferenceInit(From, ToType, &ICS, SuppressUserConversions);
     return ICS;
index 096bbf56c9e5d25280a124458ff93f0ad2ee0208..2511c2d6ead04c1b715bdf08c8c645b211f427df 100644 (file)
@@ -26,21 +26,22 @@ namespace clang {
   /// match with Table 9 of (C++ 13.3.3.1.1) and are listed such that
   /// better conversion kinds have smaller values.
   enum ImplicitConversionKind {
-    ICK_Identity = 0,        ///< Identity conversion (no conversion)
-    ICK_Lvalue_To_Rvalue,    ///< Lvalue-to-rvalue conversion (C++ 4.1)
-    ICK_Array_To_Pointer,    ///< Array-to-pointer conversion (C++ 4.2)
-    ICK_Function_To_Pointer, ///< Function-to-pointer (C++ 4.3)
-    ICK_Qualification,       ///< Qualification conversions (C++ 4.4)
-    ICK_Integral_Promotion,  ///< Integral promotions (C++ 4.5)
-    ICK_Floating_Promotion,  ///< Floating point promotions (C++ 4.6)
-    ICK_Integral_Conversion, ///< Integral conversions (C++ 4.7)
-    ICK_Floating_Conversion, ///< Floating point conversions (C++ 4.8)
-    ICK_Floating_Integral,   ///< Floating-integral conversions (C++ 4.9)
-    ICK_Pointer_Conversion,  ///< Pointer conversions (C++ 4.10)
-    ICK_Pointer_Member,      ///< Pointer-to-member conversions (C++ 4.11)
-    ICK_Boolean_Conversion,  ///< Boolean conversions (C++ 4.12)
-    ICK_Derived_To_Base,     ///< Derived-to-base (C++ [over.best.ics][)
-    ICK_Num_Conversion_Kinds ///< The number of conversion kinds
+    ICK_Identity = 0,          ///< Identity conversion (no conversion)
+    ICK_Lvalue_To_Rvalue,      ///< Lvalue-to-rvalue conversion (C++ 4.1)
+    ICK_Array_To_Pointer,      ///< Array-to-pointer conversion (C++ 4.2)
+    ICK_Function_To_Pointer,   ///< Function-to-pointer (C++ 4.3)
+    ICK_Qualification,         ///< Qualification conversions (C++ 4.4)
+    ICK_Integral_Promotion,    ///< Integral promotions (C++ 4.5)
+    ICK_Floating_Promotion,    ///< Floating point promotions (C++ 4.6)
+    ICK_Integral_Conversion,   ///< Integral conversions (C++ 4.7)
+    ICK_Floating_Conversion,   ///< Floating point conversions (C++ 4.8)
+    ICK_Floating_Integral,     ///< Floating-integral conversions (C++ 4.9)
+    ICK_Pointer_Conversion,    ///< Pointer conversions (C++ 4.10)
+    ICK_Pointer_Member,        ///< Pointer-to-member conversions (C++ 4.11)
+    ICK_Boolean_Conversion,    ///< Boolean conversions (C++ 4.12)
+    ICK_Compatible_Conversion, ///< Conversions between compatible types in C99
+    ICK_Derived_To_Base,       ///< Derived-to-base (C++ [over.best.ics])
+    ICK_Num_Conversion_Kinds   ///< The number of conversion kinds
   };
 
   /// ImplicitConversionCategory - The category of an implicit