From: Douglas Gregor Date: Wed, 11 Feb 2009 23:02:49 +0000 (+0000) Subject: Initial implementation of function overloading in C. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f9201e0ff1779567150b70856753d9f2c6a91467;p=clang Initial implementation of function overloading in C. 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 --- diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h index 38e2b9ecb1..d446a352fb 100644 --- a/include/clang/AST/Attr.h +++ b/include/clang/AST/Attr.h @@ -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 { diff --git a/include/clang/Basic/DiagnosticSemaKinds.def b/include/clang/Basic/DiagnosticSemaKinds.def index 9cec83a411..9319fb0a4e 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.def +++ b/include/clang/Basic/DiagnosticSemaKinds.def @@ -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, diff --git a/include/clang/Parse/AttributeList.h b/include/clang/Parse/AttributeList.h index bca12a3e60..147fa1bd56 100644 --- a/include/clang/Parse/AttributeList.h +++ b/include/clang/Parse/AttributeList.h @@ -62,6 +62,7 @@ public: AT_nonnull, AT_noreturn, AT_nothrow, + AT_overloadable, // Clang-specific AT_packed, AT_pure, AT_stdcall, diff --git a/lib/Parse/AttributeList.cpp b/lib/Parse/AttributeList.cpp index eb0527dcf9..954e93e056 100644 --- a/lib/Parse/AttributeList.cpp +++ b/lib/Parse/AttributeList.cpp @@ -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; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index a3e0232987..de06ca610c 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -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(PrevDecl)) + return true; + + return PrevDecl->getAttr() != 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(D)) { + } else if (isa(D) && + AllowOverloadingOfFunction(D, Context)) { // We are pushing the name of a function, which might be an // overloaded name. FunctionDecl *FD = cast(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()) + 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()) { + // 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 diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 2a6da06f71..b4e3dd99ac 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -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(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; diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index c2051a5016..4ac7214518 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1909,12 +1909,16 @@ Sema::ActOnCallExpr(Scope *S, ExprArg fn, SourceLocation LParenLoc, Ovl = dyn_cast(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, diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 4bfd4fa0b5..6ce8331555 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -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; diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index ee4c14d0ca..f4bfe5ca3c 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -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()) { + // 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 MaybeResult = diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index e40455b5c6..e1939d329e 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -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; diff --git a/lib/Sema/SemaOverload.h b/lib/Sema/SemaOverload.h index 096bbf56c9..2511c2d6ea 100644 --- a/lib/Sema/SemaOverload.h +++ b/lib/Sema/SemaOverload.h @@ -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