From 0a0d2b179085a52c10402feebeb6db8b4d96a140 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 23 Mar 2011 00:50:03 +0000 Subject: [PATCH] Implement a new 'availability' attribute, that allows one to specify which versions of an OS provide a certain facility. For example, void foo() __attribute__((availability(macosx,introduced=10.2,deprecated=10.4,obsoleted=10.6))); says that the function "foo" was introduced in 10.2, deprecated in 10.4, and completely obsoleted in 10.6. This attribute ties in with the deployment targets (e.g., -mmacosx-version-min=10.1 specifies that we want to deploy back to Mac OS X 10.1). There are several concrete behaviors that this attribute enables, as illustrated with the function foo() above: - If we choose a deployment target >= Mac OS X 10.4, uses of "foo" will result in a deprecation warning, as if we had placed attribute((deprecated)) on it (but with a better diagnostic) - If we choose a deployment target >= Mac OS X 10.6, uses of "foo" will result in an "unavailable" warning (in C)/error (in C++), as if we had placed attribute((unavailable)) on it - If we choose a deployment target prior to 10.2, foo() is weak-imported (if it is a kind of entity that can be weak imported), as if we had placed the weak_import attribute on it. Naturally, there can be multiple availability attributes on a declaration, for different platforms; only the current platform matters when checking availability attributes. The only platforms this attribute currently works for are "ios" and "macosx", since we already have -mxxxx-version-min flags for them and we have experience there with macro tricks translating down to the deprecated/unavailable/weak_import attributes. The end goal is to open this up to other platforms, and even extension to other "platforms" that are really libraries (say, through a #pragma clang define_system), but that hasn't yet been designed and we may want to shake out more issues with this narrower problem first. Addresses . As a drive-by bug-fix, if an entity is both deprecated and unavailable, we only emit the "unavailable" diagnostic. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@128127 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 1 + include/clang/AST/Attr.h | 2 + include/clang/AST/Decl.h | 8 - include/clang/AST/DeclBase.h | 55 +++++ include/clang/Basic/Attr.td | 16 ++ include/clang/Basic/DiagnosticGroups.td | 1 + include/clang/Basic/DiagnosticParseKinds.td | 17 ++ include/clang/Basic/DiagnosticSemaKinds.td | 17 +- include/clang/Basic/TargetInfo.h | 14 +- include/clang/Basic/VersionTuple.h | 126 ++++++++++ include/clang/Parse/Parser.h | 18 +- include/clang/Sema/AttributeList.h | 62 +++++ include/clang/Sema/Sema.h | 1 + include/clang/Serialization/ASTReader.h | 4 + include/clang/Serialization/ASTWriter.h | 4 + lib/AST/ASTContext.cpp | 1 - lib/AST/DeclBase.cpp | 150 ++++++++++++ lib/AST/ExprConstant.cpp | 2 +- lib/Basic/CMakeLists.txt | 1 + lib/Basic/TargetInfo.cpp | 4 + lib/Basic/Targets.cpp | 15 +- lib/Basic/VersionTuple.cpp | 36 +++ lib/CodeGen/CGObjCMac.cpp | 10 +- lib/CodeGen/CodeGenModule.cpp | 6 +- lib/Parse/ParseDecl.cpp | 240 +++++++++++++++++++- lib/Parse/Parser.cpp | 4 + lib/Sema/AttributeList.cpp | 19 ++ lib/Sema/CodeCompleteConsumer.cpp | 17 +- lib/Sema/SemaDeclAttr.cpp | 81 +++++-- lib/Sema/SemaDeclObjC.cpp | 11 +- lib/Sema/SemaExpr.cpp | 60 +++-- lib/Sema/SemaExprCXX.cpp | 6 +- lib/Sema/SemaObjCProperty.cpp | 12 +- lib/Sema/SemaOverload.cpp | 26 +-- lib/Serialization/ASTReader.cpp | 13 ++ lib/Serialization/ASTWriter.cpp | 14 ++ test/CodeGenObjC/attr-availability.m | 24 ++ test/Parser/attr-availability.c | 9 + test/Sema/attr-availability-ios.c | 15 ++ test/Sema/attr-availability-macosx.c | 15 ++ test/Sema/attr-availability.c | 6 + test/SemaCXX/attr-unavailable.cpp | 2 +- test/SemaObjC/protocol-attribute.m | 2 +- test/SemaObjC/special-dep-unavail-warning.m | 6 +- tools/libclang/CIndex.cpp | 16 +- 45 files changed, 1060 insertions(+), 109 deletions(-) create mode 100644 include/clang/Basic/VersionTuple.h create mode 100644 lib/Basic/VersionTuple.cpp create mode 100644 test/CodeGenObjC/attr-availability.m create mode 100644 test/Parser/attr-availability.c create mode 100644 test/Sema/attr-availability-ios.c create mode 100644 test/Sema/attr-availability-macosx.c create mode 100644 test/Sema/attr-availability.c diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 9e3d10f63b..1bf13f6437 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -19,6 +19,7 @@ #include "clang/Basic/LangOptions.h" #include "clang/Basic/OperatorKinds.h" #include "clang/Basic/PartialDiagnostic.h" +#include "clang/Basic/VersionTuple.h" #include "clang/AST/Decl.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/PrettyPrinter.h" diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h index 502b9f775f..719023926b 100644 --- a/include/clang/AST/Attr.h +++ b/include/clang/AST/Attr.h @@ -17,9 +17,11 @@ #include "llvm/Support/Casting.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" #include "clang/Basic/AttrKinds.h" #include "clang/AST/Type.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Basic/VersionTuple.h" #include #include #include diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 08e4525f55..339074bde0 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -119,14 +119,6 @@ public: return getIdentifier() ? getIdentifier()->getName() : ""; } - llvm::StringRef getMessageUnavailableAttr(bool unavailable) const { - if (!unavailable) - return ""; - if (const UnavailableAttr *UA = getAttr()) - return UA->getMessage(); - return ""; - } - /// getNameAsString - Get a human-readable name for the declaration, even if /// it is one of the special kinds of names (C++ constructor, Objective-C /// selector, etc). Creating this name requires expensive string diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index b35d134d05..caab56a0cd 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -62,6 +62,15 @@ public: namespace clang { + /// \brief Captures the result of checking the availability of a + /// declaration. + enum AvailabilityResult { + AR_Available = 0, + AR_NotYetIntroduced, + AR_Deprecated, + AR_Unavailable + }; + /// Decl - This represents one declaration (or definition), e.g. a variable, /// typedef, function, struct, etc. /// @@ -399,6 +408,52 @@ public: void setUsed(bool U = true) { Used = U; } + /// \brief Determine the availability of the given declaration. + /// + /// This routine will determine the most restrictive availability of + /// the given declaration (e.g., preferring 'unavailable' to + /// 'deprecated'). + /// + /// \param Message If non-NULL and the result is not \c + /// AR_Available, will be set to a (possibly empty) message + /// describing why the declaration has not been introduced, is + /// deprecated, or is unavailable. + AvailabilityResult getAvailability(std::string *Message = 0) const; + + /// \brief Determine whether this declaration is marked 'deprecated'. + /// + /// \param Message If non-NULL and the declaration is deprecated, + /// this will be set to the message describing why the declaration + /// was deprecated (which may be empty). + bool isDeprecated(std::string *Message = 0) const { + return getAvailability(Message) == AR_Deprecated; + } + + /// \brief Determine whether this declaration is marked 'unavailable'. + /// + /// \param Message If non-NULL and the declaration is unavailable, + /// this will be set to the message describing why the declaration + /// was made unavailable (which may be empty). + bool isUnavailable(std::string *Message = 0) const { + return getAvailability(Message) == AR_Unavailable; + } + + /// \brief Determine whether this is a weak-imported symbol. + /// + /// Weak-imported symbols are typically marked with the + /// 'weak_import' attribute, but may also be marked with an + /// 'availability' attribute where we're targing a platform prior to + /// the introduction of this feature. + bool isWeakImported() const; + + /// \brief Determines whether this symbol can be weak-imported, + /// e.g., whether it would be well-formed to add the weak_import + /// attribute. + /// + /// \param IsDefinition Set to \c true to indicate that this + /// declaration cannot be weak-imported because it has a definition. + bool canBeWeakImported(bool &IsDefinition) const; + /// \brief Retrieve the level of precompiled header from which this /// declaration was generated. /// diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 3579e2c6f1..6c3d5e5d0c 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -55,6 +55,9 @@ class TypeArgument : Argument; class UnsignedArgument : Argument; class VariadicUnsignedArgument : Argument; +// A version of the form major.minor[.subminor]. +class VersionArgument : Argument; + // This one's a doozy, so it gets its own special type // It can be an unsigned integer, or a type. Either can // be dependent. @@ -134,6 +137,19 @@ def AsmLabel : InheritableAttr { let Args = [StringArgument<"Label">]; } +def Availability : InheritableAttr { + let Spellings = ["availability"]; + let Args = [IdentifierArgument<"platform">, VersionArgument<"introduced">, + VersionArgument<"deprecated">, VersionArgument<"obsoleted">]; + let AdditionalMembers = +[{static llvm::StringRef getPrettyPlatformName(llvm::StringRef Platform) { + return llvm::StringSwitch(Platform) + .Case("ios", "iOS") + .Case("macosx", "Mac OS X") + .Default(llvm::StringRef()); +} }]; +} + def Blocks : InheritableAttr { let Spellings = ["blocks"]; let Args = [EnumArgument<"Type", "BlockType", ["byref"], ["ByRef"]>]; diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index d3cc08319a..2b7c5de25c 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -24,6 +24,7 @@ def : DiagGroup<"aggregate-return">; def AmbigMemberTemplate : DiagGroup<"ambiguous-member-template">; def : DiagGroup<"attributes">; def : DiagGroup<"bad-function-cast">; +def Availability : DiagGroup<"availability">; def BoolConversions : DiagGroup<"bool-conversions">; def CXXCompat: DiagGroup<"c++-compat">; def CastAlign : DiagGroup<"cast-align">; diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 0f011f5741..8f4414d3d4 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -119,6 +119,7 @@ def err_function_declared_typedef : Error< "function definition declared 'typedef'">; def err_iboutletcollection_builtintype : Error< "type argument of iboutletcollection attribute cannot be a builtin type">; + def err_expected_fn_body : Error< "expected function body after function declarator">; def err_expected_method_body : Error<"expected method body">; @@ -130,6 +131,7 @@ def err_expected_statement : Error<"expected statement">; def err_expected_lparen_after : Error<"expected '(' after '%0'">; def err_expected_lparen_after_id : Error<"expected '(' after %0">; def err_expected_less_after : Error<"expected '<' after '%0'">; +def err_expected_equal_after : Error<"expected '=' after %0">; def err_expected_comma : Error<"expected ','">; def err_expected_lbrace_in_compound_literal : Error< "expected '{' in compound literal">; @@ -431,6 +433,21 @@ def err_paren_sizeof_parameter_pack : Error< def err_sizeof_parameter_pack : Error< "expected parenthesized parameter pack name in 'sizeof...' expression">; +// Availability attribute +def err_expected_version : Error< + "expected a version of the form 'major[.minor[.subminor]]'">; +def err_zero_version : Error< + "version number must have non-zero major, minor, or sub-minor version">; +def err_availability_expected_platform : Error< + "expected a platform name, e.g., 'macosx'">; +def err_availability_expected_change : Error< + "expected 'introduced', 'deprecated', or 'obsoleted'">; +def err_availability_unknown_change : Error< + "%0 is not an availability stage; use 'introduced', 'deprecated', or " + "'obsoleted'">; +def err_availability_redundant : Error< + "redundant %0 availability change; only the last specified change will " "be used">; + // Language specific pragmas // - Generic warnings def warn_pragma_expected_lparen : Warning< diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 41feb7e019..fa66d51369 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1148,6 +1148,14 @@ def err_regparm_mismatch : Error<"function declared with with regparm(%0) " "attribute was previously declared " "%plural{0:without the regparm|:with the regparm(%1)}1 attribute">; +// Availability attribute +def warn_availability_unknown_platform : Warning< + "unknown platform %0 in availability macro">; +def warn_availability_version_ordering : Warning< + "feature cannot be %select{introduced|deprecated|obsoleted}0 in %1 version " + "%2 before it was %select{introduced|deprecated|obsoleted}3 in version %4; " + "attribute ignored">; + def warn_impcast_vector_scalar : Warning< "implicit conversion turns vector to scalar: %0 to %1">, InGroup>, DefaultIgnore; @@ -1339,11 +1347,11 @@ def err_ovl_no_viable_member_function_in_call : Error< def err_ovl_ambiguous_call : Error< "call to %0 is ambiguous">; def err_ovl_deleted_call : Error< - "call to %select{unavailable|deleted}0 function %1 %2">; + "call to %select{unavailable|deleted}0 function %1%2">; def err_ovl_ambiguous_member_call : Error< "call to member function %0 is ambiguous">; def err_ovl_deleted_member_call : Error< - "call to %select{unavailable|deleted}0 member function %1 %2">; + "call to %select{unavailable|deleted}0 member function %1%2">; def note_ovl_too_many_candidates : Note< "remaining %0 candidate%s0 omitted; " "pass -fshow-overloads=all to show them">; @@ -1495,7 +1503,7 @@ def err_ovl_ambiguous_oper_binary : Error< "use of overloaded operator '%0' is ambiguous (with operand types %1 and %2)">; def err_ovl_no_viable_oper : Error<"no viable overloaded '%0'">; def err_ovl_deleted_oper : Error< - "overload resolution selected %select{unavailable|deleted}0 operator '%1' %2">; + "overload resolution selected %select{unavailable|deleted}0 operator '%1'%2">; def err_ovl_no_viable_subscript : Error<"no viable overloaded operator[] for type %0">; def err_ovl_no_oper : @@ -1509,7 +1517,7 @@ def err_ovl_no_viable_object_call : Error< def err_ovl_ambiguous_object_call : Error< "call to object of type %0 is ambiguous">; def err_ovl_deleted_object_call : Error< - "call to %select{unavailable|deleted}0 function call operator in type %1 %2">; + "call to %select{unavailable|deleted}0 function call operator in type %1%2">; def note_ovl_surrogate_cand : Note<"conversion candidate of type %0">; def err_member_call_without_object : Error< "call to non-static member function without an object argument">; @@ -3831,5 +3839,6 @@ def err_sizeof_pack_no_pack_name_suggest : Error< def note_parameter_pack_here : Note<"parameter pack %0 declared here">; } // end of sema category + } // end of sema component. diff --git a/include/clang/Basic/TargetInfo.h b/include/clang/Basic/TargetInfo.h index af5fd5598a..b830bf2f82 100644 --- a/include/clang/Basic/TargetInfo.h +++ b/include/clang/Basic/TargetInfo.h @@ -16,17 +16,18 @@ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/DataTypes.h" #include "clang/Basic/AddressSpaces.h" +#include "clang/Basic/VersionTuple.h" #include #include #include namespace llvm { struct fltSemantics; -class StringRef; } namespace clang { @@ -81,6 +82,9 @@ protected: TargetCXXABI CXXABI; const LangAS::Map *AddrSpaceMap; + mutable llvm::StringRef PlatformName; + mutable VersionTuple PlatformMinVersion; + unsigned HasAlignMac68kSupport : 1; unsigned RealTypeUsesObjCFPRet : 3; @@ -537,6 +541,14 @@ public: return *AddrSpaceMap; } + /// \brief Retrieve the name of the platform as it is used in the + /// availability attribute. + llvm::StringRef getPlatformName() const { return PlatformName; } + + /// \brief Retrieve the minimum desired version of the platform, to + /// which the program should be compiled. + VersionTuple getPlatformMinVersion() const { return PlatformMinVersion; } + protected: virtual uint64_t getPointerWidthV(unsigned AddrSpace) const { return PointerWidth; diff --git a/include/clang/Basic/VersionTuple.h b/include/clang/Basic/VersionTuple.h new file mode 100644 index 0000000000..91eb68eaad --- /dev/null +++ b/include/clang/Basic/VersionTuple.h @@ -0,0 +1,126 @@ +//===- VersionTuple.h - Version Number Handling -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This header defines the VersionTuple class, which represents a version in +// the form major[.minor[.subminor]]. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_BASIC_VERSIONTUPLE_H +#define LLVM_CLANG_BASIC_VERSIONTUPLE_H + +#include "llvm/ADT/Optional.h" +#include + +namespace llvm { + class raw_ostream; +} + +namespace clang { + +/// \brief Represents a version number in the form major[.minor[.subminor]]. +class VersionTuple { + unsigned Major; + unsigned Minor : 31; + unsigned Subminor : 31; + unsigned HasMinor : 1; + unsigned HasSubminor : 1; + +public: + VersionTuple() + : Major(0), Minor(0), Subminor(0), HasMinor(false), HasSubminor(false) { } + + explicit VersionTuple(unsigned Major) + : Major(Major), Minor(0), Subminor(0), HasMinor(false), HasSubminor(false) + { } + + explicit VersionTuple(unsigned Major, unsigned Minor) + : Major(Major), Minor(Minor), Subminor(0), HasMinor(true), + HasSubminor(false) + { } + + explicit VersionTuple(unsigned Major, unsigned Minor, unsigned Subminor) + : Major(Major), Minor(Minor), Subminor(Subminor), HasMinor(true), + HasSubminor(true) + { } + + /// \brief Determine whether this version information is empty + /// (e.g., all version components are zero). + bool empty() const { return Major == 0 && Minor == 0 && Subminor == 0; } + + /// \brief Retrieve the major version number. + unsigned getMajor() const { return Major; } + + /// \brief Retrieve the minor version number, if provided. + llvm::Optional getMinor() const { + if (!HasMinor) + return llvm::Optional(); + return Minor; + } + + /// \brief Retrieve the subminor version number, if provided. + llvm::Optional getSubminor() const { + if (!HasSubminor) + return llvm::Optional(); + return Subminor; + } + + /// \brief Determine if two version numbers are equivalent. If not + /// provided, minor and subminor version numbers are considered to be zero. + friend bool operator==(const VersionTuple& X, const VersionTuple &Y) { + return X.Major == Y.Major && X.Minor == Y.Minor && X.Subminor == Y.Subminor; + } + + /// \brief Determine if two version numbers are not equivalent. If + /// not provided, minor and subminor version numbers are considered to be + /// zero. + friend bool operator!=(const VersionTuple &X, const VersionTuple &Y) { + return !(X == Y); + } + + /// \brief Determine whether one version number precedes another. If not + /// provided, minor and subminor version numbers are considered to be zero. + friend bool operator<(const VersionTuple &X, const VersionTuple &Y) { + if (X.Major != Y.Major) + return X.Major < Y.Major; + + if (X.Minor != Y.Minor) + return X.Minor < Y.Minor; + + return X.Subminor < Y.Subminor; + } + + /// \brief Determine whether one version number follows another. If not + /// provided, minor and subminor version numbers are considered to be zero. + friend bool operator>(const VersionTuple &X, const VersionTuple &Y) { + return Y < X; + } + + /// \brief Determine whether one version number precedes or is + /// equivalent to another. If not provided, minor and subminor + /// version numbers are considered to be zero. + friend bool operator<=(const VersionTuple &X, const VersionTuple &Y) { + return !(Y < X); + } + + /// \brief Determine whether one version number follows or is + /// equivalent to another. If not provided, minor and subminor + /// version numbers are considered to be zero. + friend bool operator>=(const VersionTuple &X, const VersionTuple &Y) { + return !(X < Y); + } + + /// \brief Retrieve a string representation of the version number/ + std::string getAsString() const; +}; + +/// \brief Print a version number. +llvm::raw_ostream& operator<<(llvm::raw_ostream &Out, const VersionTuple &V); + +} // end namespace clang +#endif // LLVM_CLANG_BASIC_VERSIONTUPLE_H diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index d07f605270..b51632c6ac 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -33,7 +33,8 @@ namespace clang { class PragmaUnusedHandler; class ColonProtectionRAIIObject; class InMessageExpressionRAIIObject; - + class VersionTuple; + /// PrettyStackTraceParserEntry - If a crash happens while the parser is active, /// an entry is printed for it. class PrettyStackTraceParserEntry : public llvm::PrettyStackTraceEntry { @@ -111,6 +112,15 @@ class Parser : public CodeCompletionHandler { IdentifierInfo *Ident_vector; IdentifierInfo *Ident_pixel; + /// \brief Identifier for "introduced". + IdentifierInfo *Ident_introduced; + + /// \brief Identifier for "deprecated". + IdentifierInfo *Ident_deprecated; + + /// \brief Identifier for "obsoleted". + IdentifierInfo *Ident_obsoleted; + /// C++0x contextual keywords. mutable IdentifierInfo *Ident_final; mutable IdentifierInfo *Ident_override; @@ -1547,6 +1557,12 @@ private: void ParseOpenCLAttributes(ParsedAttributes &attrs); void ParseOpenCLQualifiers(DeclSpec &DS); + VersionTuple ParseVersionTuple(SourceRange &Range); + void ParseAvailabilityAttribute(IdentifierInfo &Availability, + SourceLocation AvailabilityLoc, + ParsedAttributes &attrs, + SourceLocation *endLoc); + void ParseTypeofSpecifier(DeclSpec &DS); void ParseDecltypeSpecifier(DeclSpec &DS); diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 382e50494e..3657b44b02 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -18,6 +18,7 @@ #include "llvm/Support/Allocator.h" #include "clang/Sema/Ownership.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Basic/VersionTuple.h" #include "clang/AST/Expr.h" #include @@ -25,6 +26,23 @@ namespace clang { class IdentifierInfo; class Expr; +/// \brief Represents information about a change in availability for +/// an entity, which is part of the encoding of the 'availability' +/// attribute. +struct AvailabilityChange { + /// \brief The location of the keyword indicating the kind of change. + SourceLocation KeywordLoc; + + /// \brief The version number at which the change occurred. + VersionTuple Version; + + /// \brief The source range covering the version number. + SourceRange VersionRange; + + /// \brief Determine whether this availability change is valid. + bool isValid() const { return !Version.empty(); } +}; + /// AttributeList - Represents GCC's __attribute__ declaration. There are /// 4 forms of this construct...they are: /// @@ -48,6 +66,11 @@ private: AttributeList *Next; bool DeclspecAttribute, CXX0XAttribute; + // For the 'availability' attribute. + AvailabilityChange AvailabilityIntroduced; + AvailabilityChange AvailabilityDeprecated; + AvailabilityChange AvailabilityObsoleted; + /// True if already diagnosed as invalid. mutable bool Invalid; @@ -61,6 +84,15 @@ private: IdentifierInfo *ParmName, SourceLocation ParmLoc, Expr **args, unsigned numargs, bool declspec, bool cxx0x); + + AttributeList(llvm::BumpPtrAllocator &Alloc, + IdentifierInfo *AttrName, SourceLocation AttrLoc, + IdentifierInfo *ScopeName, SourceLocation ScopeLoc, + IdentifierInfo *ParmName, SourceLocation ParmLoc, + const AvailabilityChange &Introduced, + const AvailabilityChange &Deprecated, + const AvailabilityChange &Obsoleted, + bool declspec, bool cxx0x); public: class Factory { llvm::BumpPtrAllocator Alloc; @@ -78,6 +110,20 @@ public: return Mem; } + AttributeList *Create(IdentifierInfo *AttrName, SourceLocation AttrLoc, + IdentifierInfo *ScopeName, SourceLocation ScopeLoc, + IdentifierInfo *ParmName, SourceLocation ParmLoc, + const AvailabilityChange &Introduced, + const AvailabilityChange &Deprecated, + const AvailabilityChange &Obsoleted, + bool declspec = false, bool cxx0x = false) { + AttributeList *Mem = Alloc.Allocate(); + new (Mem) AttributeList(Alloc, AttrName, AttrLoc, ScopeName, ScopeLoc, + ParmName, ParmLoc, Introduced, Deprecated, + Obsoleted, declspec, cxx0x); + return Mem; + } + AttributeList* CreateIntegerAttribute(ASTContext &C, IdentifierInfo *Name, SourceLocation TokLoc, int Arg) { Expr* IArg = IntegerLiteral::Create(C, llvm::APInt(32, (uint64_t)Arg), @@ -96,6 +142,7 @@ public: AT_always_inline, AT_analyzer_noreturn, AT_annotate, + AT_availability, // Clang-specific AT_base_check, AT_blocks, AT_carries_dependency, @@ -244,6 +291,21 @@ public: arg_iterator arg_end() const { return arg_iterator(Args, NumArgs); } + + const AvailabilityChange &getAvailabilityIntroduced() const { + assert(getKind() == AT_availability && "Not an availability attribute"); + return AvailabilityIntroduced; + } + + const AvailabilityChange &getAvailabilityDeprecated() const { + assert(getKind() == AT_availability && "Not an availability attribute"); + return AvailabilityDeprecated; + } + + const AvailabilityChange &getAvailabilityObsoleted() const { + assert(getKind() == AT_availability && "Not an availability attribute"); + return AvailabilityObsoleted; + } }; /// addAttributeLists - Add two AttributeLists together diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 043722b29c..bacd7691a3 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1901,6 +1901,7 @@ public: bool DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, bool UnknownObjCClass=false); + std::string getDeletedOrUnavailableSuffix(const FunctionDecl *FD); bool DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *PD, ObjCMethodDecl *Getter, SourceLocation Loc); diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index 835a0dbb10..559d14554b 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -69,6 +69,7 @@ class ASTStmtReader; class ASTIdentifierLookupTrait; class TypeLocReader; struct HeaderFileInfo; +class VersionTuple; struct PCHPredefinesBlock { /// \brief The file ID for this predefines buffer in a PCH file. @@ -1208,6 +1209,9 @@ public: // \brief Read a string std::string ReadString(const RecordData &Record, unsigned &Idx); + /// \brief Read a version tuple. + VersionTuple ReadVersionTuple(const RecordData &Record, unsigned &Idx); + CXXTemporary *ReadCXXTemporary(const RecordData &Record, unsigned &Idx); /// \brief Reads attributes from the current stream position. diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h index 210f1e67ca..474631fce0 100644 --- a/include/clang/Serialization/ASTWriter.h +++ b/include/clang/Serialization/ASTWriter.h @@ -56,6 +56,7 @@ class Sema; class SourceManager; class SwitchCase; class TargetInfo; +class VersionTuple; /// \brief Writes an AST file containing the contents of a translation unit. /// @@ -514,6 +515,9 @@ public: /// \brief Add a string to the given record. void AddString(llvm::StringRef Str, RecordDataImpl &Record); + /// \brief Add a version tuple to the given record + void AddVersionTuple(const VersionTuple &Version, RecordDataImpl &Record); + /// \brief Mark a declaration context as needing an update. void AddUpdatedDeclContext(const DeclContext *DC) { UpdatedDeclContexts.insert(DC); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index fd5fd0e54b..e208bfdf1d 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -451,7 +451,6 @@ void ASTContext::eraseDeclAttrs(const Decl *D) { } } - MemberSpecializationInfo * ASTContext::getInstantiatedFromStaticDataMember(const VarDecl *Var) { assert(Var->isStaticDataMember() && "Not a static data member"); diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 8d6a5412a5..11d28df4be 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -25,6 +25,7 @@ #include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/ASTMutationListener.h" +#include "clang/Basic/TargetInfo.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/raw_ostream.h" #include @@ -241,6 +242,155 @@ bool Decl::isUsed(bool CheckUsedAttr) const { return false; } +/// \brief Determine the availability of the given declaration based on +/// the target platform. +/// +/// When it returns an availability result other than \c AR_Available, +/// if the \p Message parameter is non-NULL, it will be set to a +/// string describing why the entity is unavailable. +/// +/// FIXME: Make these strings localizable, since they end up in +/// diagnostics. +static AvailabilityResult CheckAvailability(ASTContext &Context, + const AvailabilityAttr *A, + std::string *Message) { + llvm::StringRef TargetPlatform = Context.Target.getPlatformName(); + llvm::StringRef PrettyPlatformName + = AvailabilityAttr::getPrettyPlatformName(TargetPlatform); + if (PrettyPlatformName.empty()) + PrettyPlatformName = TargetPlatform; + + VersionTuple TargetMinVersion = Context.Target.getPlatformMinVersion(); + if (TargetMinVersion.empty()) + return AR_Available; + + // Match the platform name. + if (A->getPlatform()->getName() != TargetPlatform) + return AR_Available; + + // Make sure that this declaration has already been introduced. + if (!A->getIntroduced().empty() && + TargetMinVersion < A->getIntroduced()) { + if (Message) { + Message->clear(); + llvm::raw_string_ostream Out(*Message); + Out << "introduced in " << PrettyPlatformName << ' ' + << A->getIntroduced(); + } + + return AR_NotYetIntroduced; + } + + // Make sure that this declaration hasn't been obsoleted. + if (!A->getObsoleted().empty() && TargetMinVersion >= A->getObsoleted()) { + if (Message) { + Message->clear(); + llvm::raw_string_ostream Out(*Message); + Out << "obsoleted in " << PrettyPlatformName << ' ' + << A->getObsoleted(); + } + + return AR_Unavailable; + } + + // Make sure that this declaration hasn't been deprecated. + if (!A->getDeprecated().empty() && TargetMinVersion >= A->getDeprecated()) { + if (Message) { + Message->clear(); + llvm::raw_string_ostream Out(*Message); + Out << "first deprecated in " << PrettyPlatformName << ' ' + << A->getDeprecated(); + } + + return AR_Deprecated; + } + + return AR_Available; +} + +AvailabilityResult Decl::getAvailability(std::string *Message) const { + AvailabilityResult Result = AR_Available; + std::string ResultMessage; + + for (attr_iterator A = attr_begin(), AEnd = attr_end(); A != AEnd; ++A) { + if (DeprecatedAttr *Deprecated = dyn_cast(*A)) { + if (Result >= AR_Deprecated) + continue; + + if (Message) + ResultMessage = Deprecated->getMessage(); + + Result = AR_Deprecated; + continue; + } + + if (UnavailableAttr *Unavailable = dyn_cast(*A)) { + if (Message) + *Message = Unavailable->getMessage(); + return AR_Unavailable; + } + + if (AvailabilityAttr *Availability = dyn_cast(*A)) { + AvailabilityResult AR = CheckAvailability(getASTContext(), Availability, + Message); + + if (AR == AR_Unavailable) + return AR_Unavailable; + + if (AR > Result) { + Result = AR; + if (Message) + ResultMessage.swap(*Message); + } + continue; + } + } + + if (Message) + Message->swap(ResultMessage); + return Result; +} + +bool Decl::canBeWeakImported(bool &IsDefinition) const { + IsDefinition = false; + if (const VarDecl *Var = dyn_cast(this)) { + if (!Var->hasExternalStorage() || Var->getInit()) { + IsDefinition = true; + return false; + } + } else if (const FunctionDecl *FD = dyn_cast(this)) { + if (FD->hasBody()) { + IsDefinition = true; + return false; + } + } else if (isa(this) || isa(this)) + return false; + else if (!(getASTContext().getLangOptions().ObjCNonFragileABI && + isa(this))) + return false; + + return true; +} + +bool Decl::isWeakImported() const { + bool IsDefinition; + if (!canBeWeakImported(IsDefinition)) + return false; + + ASTContext &Context = getASTContext(); + for (attr_iterator A = attr_begin(), AEnd = attr_end(); A != AEnd; ++A) { + if (isa(*A)) + return true; + + if (AvailabilityAttr *Availability = dyn_cast(*A)) { + if (CheckAvailability(getASTContext(), Availability, 0) + == AR_NotYetIntroduced) + return true; + } + } + + return false; +} unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { switch (DeclKind) { diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index b6ab33258d..345c7fa8c0 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -175,7 +175,7 @@ static bool EvalPointerValueAsBool(LValue& Value, bool& Result) { const ValueDecl* Decl = DeclRef->getDecl(); if (Decl->hasAttr() || Decl->hasAttr() || - Decl->hasAttr()) + Decl->isWeakImported()) return false; return true; diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt index 91e7deb078..b23a156cba 100644 --- a/lib/Basic/CMakeLists.txt +++ b/lib/Basic/CMakeLists.txt @@ -14,6 +14,7 @@ add_clang_library(clangBasic Targets.cpp TokenKinds.cpp Version.cpp + VersionTuple.cpp ) # Determine Subversion revision. diff --git a/lib/Basic/TargetInfo.cpp b/lib/Basic/TargetInfo.cpp index 1696cae840..078452e988 100644 --- a/lib/Basic/TargetInfo.cpp +++ b/lib/Basic/TargetInfo.cpp @@ -70,6 +70,10 @@ TargetInfo::TargetInfo(const std::string &T) : Triple(T) { // Default to an empty address space map. AddrSpaceMap = &DefaultAddrSpaceMap; + + // Default to an unknown platform name. + PlatformName = "unknown"; + PlatformMinVersion = VersionTuple(); } // Out of line virtual dtor for TargetInfo. diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index 653cf80688..90b0ee0c87 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -76,7 +76,9 @@ public: static void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, - const llvm::Triple &Triple) { + const llvm::Triple &Triple, + llvm::StringRef &PlatformName, + VersionTuple &PlatformMinVersion) { Builder.defineMacro("__APPLE_CC__", "5621"); Builder.defineMacro("__APPLE__"); Builder.defineMacro("__MACH__"); @@ -120,6 +122,9 @@ static void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, Str[4] = '0' + (Rev % 10); Str[5] = '\0'; Builder.defineMacro("__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__", Str); + + PlatformName = "ios"; + PlatformMinVersion = VersionTuple(Maj, Min, Rev); } else { // For historical reasons that make little sense, the version passed here is // the "darwin" version, which drops the 10 and offsets by 4. @@ -136,6 +141,9 @@ static void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, Str[3] = '0' + Rev; Str[4] = '\0'; Builder.defineMacro("__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__", Str); + + PlatformName = "macosx"; + PlatformMinVersion = VersionTuple(Maj, Min, Rev); } } @@ -145,7 +153,8 @@ class DarwinTargetInfo : public OSTargetInfo { protected: virtual void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, MacroBuilder &Builder) const { - getDarwinDefines(Builder, Opts, Triple); + getDarwinDefines(Builder, Opts, Triple, this->PlatformName, + this->PlatformMinVersion); } public: @@ -2005,7 +2014,7 @@ class DarwinARMTargetInfo : protected: virtual void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, MacroBuilder &Builder) const { - getDarwinDefines(Builder, Opts, Triple); + getDarwinDefines(Builder, Opts, Triple, PlatformName, PlatformMinVersion); } public: diff --git a/lib/Basic/VersionTuple.cpp b/lib/Basic/VersionTuple.cpp new file mode 100644 index 0000000000..d5cf126ff4 --- /dev/null +++ b/lib/Basic/VersionTuple.cpp @@ -0,0 +1,36 @@ +//===- VersionTuple.cpp - Version Number Handling ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the VersionTuple class, which represents a version in +// the form major[.minor[.subminor]]. +// +//===----------------------------------------------------------------------===// +#include "clang/Basic/VersionTuple.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +std::string VersionTuple::getAsString() const { + std::string Result; + { + llvm::raw_string_ostream Out(Result); + Out << *this; + } + return Result; +} + +llvm::raw_ostream& clang::operator<<(llvm::raw_ostream &Out, + const VersionTuple &V) { + Out << V.getMajor(); + if (llvm::Optional Minor = V.getMinor()) + Out << '.' << *Minor; + if (llvm::Optional Subminor = V.getSubminor()) + Out << '.' << *Subminor; + return Out; +} diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 607df6d0b8..a4d95d443f 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -5018,14 +5018,14 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) { while (const ObjCInterfaceDecl *Super = Root->getSuperClass()) Root = Super; IsAGV = GetClassGlobal(ObjCMetaClassName + Root->getNameAsString()); - if (Root->hasAttr()) + if (Root->isWeakImported()) IsAGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage); // work on super class metadata symbol. std::string SuperClassName = ObjCMetaClassName + ID->getClassInterface()->getSuperClass()->getNameAsString(); SuperClassGV = GetClassGlobal(SuperClassName); - if (ID->getClassInterface()->getSuperClass()->hasAttr()) + if (ID->getClassInterface()->getSuperClass()->isWeakImported()) SuperClassGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage); } llvm::GlobalVariable *CLASS_RO_GV = BuildClassRoTInitializer(flags, @@ -5055,7 +5055,7 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) { std::string RootClassName = ID->getClassInterface()->getSuperClass()->getNameAsString(); SuperClassGV = GetClassGlobal(ObjCClassName + RootClassName); - if (ID->getClassInterface()->getSuperClass()->hasAttr()) + if (ID->getClassInterface()->getSuperClass()->isWeakImported()) SuperClassGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage); } GetClassSizeInfo(ID, InstanceStart, InstanceSize); @@ -5137,7 +5137,7 @@ void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { Values[0] = GetClassName(OCD->getIdentifier()); // meta-class entry symbol llvm::GlobalVariable *ClassGV = GetClassGlobal(ExtClassName); - if (Interface->hasAttr()) + if (Interface->isWeakImported()) ClassGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage); Values[1] = ClassGV; @@ -5810,7 +5810,7 @@ llvm::Value *CGObjCNonFragileABIMac::EmitMetaClassRef(CGBuilderTy &Builder, /// decl. llvm::Value *CGObjCNonFragileABIMac::GetClass(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID) { - if (ID->hasAttr()) { + if (ID->isWeakImported()) { std::string ClassName(getClassSymbolPrefix() + ID->getNameAsString()); llvm::GlobalVariable *ClassGV = GetClassGlobal(ClassName); ClassGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage); diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 7eee83c928..647dcc50a9 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -509,7 +509,7 @@ void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, if (FD->hasAttr()) { F->setLinkage(llvm::Function::DLLImportLinkage); } else if (FD->hasAttr() || - FD->hasAttr()) { + FD->isWeakImported()) { // "extern_weak" is overloaded in LLVM; we probably should have // separate linkage types for this. F->setLinkage(llvm::Function::ExternalWeakLinkage); @@ -986,7 +986,7 @@ CodeGenModule::GetOrCreateLLVMGlobal(llvm::StringRef MangledName, } else { if (D->hasAttr()) GV->setLinkage(llvm::GlobalValue::DLLImportLinkage); - else if (D->hasAttr() || D->hasAttr()) + else if (D->hasAttr() || D->isWeakImported()) GV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage); // Set visibility on a declaration only if it's explicit. @@ -1530,7 +1530,7 @@ void CodeGenModule::EmitAliasDefinition(GlobalDecl GD) { } } else if (D->hasAttr() || D->hasAttr() || - D->hasAttr()) { + D->isWeakImported()) { GA->setLinkage(llvm::Function::WeakAnyLinkage); } diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index f27ff79f19..6928d0d6d0 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -112,8 +112,11 @@ void Parser::ParseGNUAttributes(ParsedAttributes &attrs, IdentifierInfo *AttrName = Tok.getIdentifierInfo(); SourceLocation AttrNameLoc = ConsumeToken(); + // Availability attributes have their own grammar. + if (AttrName->isStr("availability")) + ParseAvailabilityAttribute(*AttrName, AttrNameLoc, attrs, endLoc); // check if we have a "parameterized" attribute - if (Tok.is(tok::l_paren)) { + else if (Tok.is(tok::l_paren)) { ConsumeParen(); // ignore the left paren loc for now if (Tok.is(tok::identifier)) { @@ -362,6 +365,241 @@ void Parser::ParseOpenCLQualifiers(DeclSpec &DS) { } } +/// \brief Parse a version number. +/// +/// version: +/// simple-integer +/// simple-integer ',' simple-integer +/// simple-integer ',' simple-integer ',' simple-integer +VersionTuple Parser::ParseVersionTuple(SourceRange &Range) { + Range = Tok.getLocation(); + + if (!Tok.is(tok::numeric_constant)) { + Diag(Tok, diag::err_expected_version); + SkipUntil(tok::comma, tok::r_paren, true, true, true); + return VersionTuple(); + } + + // Parse the major (and possibly minor and subminor) versions, which + // are stored in the numeric constant. We utilize a quirk of the + // lexer, which is that it handles something like 1.2.3 as a single + // numeric constant, rather than two separate tokens. + llvm::SmallString<512> Buffer; + Buffer.resize(Tok.getLength()+1); + const char *ThisTokBegin = &Buffer[0]; + + // Get the spelling of the token, which eliminates trigraphs, etc. + bool Invalid = false; + unsigned ActualLength = PP.getSpelling(Tok, ThisTokBegin, &Invalid); + if (Invalid) + return VersionTuple(); + + // Parse the major version. + unsigned AfterMajor = 0; + unsigned Major = 0; + while (AfterMajor < ActualLength && isdigit(ThisTokBegin[AfterMajor])) { + Major = Major * 10 + ThisTokBegin[AfterMajor] - '0'; + ++AfterMajor; + } + + if (AfterMajor == 0) { + Diag(Tok, diag::err_expected_version); + SkipUntil(tok::comma, tok::r_paren, true, true, true); + return VersionTuple(); + } + + if (AfterMajor == ActualLength) { + ConsumeToken(); + + // We only had a single version component. + if (Major == 0) { + Diag(Tok, diag::err_zero_version); + return VersionTuple(); + } + + return VersionTuple(Major); + } + + if (ThisTokBegin[AfterMajor] != '.' || (AfterMajor + 1 == ActualLength)) { + Diag(Tok, diag::err_expected_version); + SkipUntil(tok::comma, tok::r_paren, true, true, true); + return VersionTuple(); + } + + // Parse the minor version. + unsigned AfterMinor = AfterMajor + 1; + unsigned Minor = 0; + while (AfterMinor < ActualLength && isdigit(ThisTokBegin[AfterMinor])) { + Minor = Minor * 10 + ThisTokBegin[AfterMinor] - '0'; + ++AfterMinor; + } + + if (AfterMinor == ActualLength) { + ConsumeToken(); + + // We had major.minor. + if (Major == 0 && Minor == 0) { + Diag(Tok, diag::err_zero_version); + return VersionTuple(); + } + + return VersionTuple(Major, Minor); + } + + // If what follows is not a '.', we have a problem. + if (ThisTokBegin[AfterMinor] != '.') { + Diag(Tok, diag::err_expected_version); + SkipUntil(tok::comma, tok::r_paren, true, true, true); + return VersionTuple(); + } + + // Parse the subminor version. + unsigned AfterSubminor = AfterMinor + 1; + unsigned Subminor = 0; + while (AfterSubminor < ActualLength && isdigit(ThisTokBegin[AfterSubminor])) { + Subminor = Subminor * 10 + ThisTokBegin[AfterSubminor] - '0'; + ++AfterSubminor; + } + + if (AfterSubminor != ActualLength) { + Diag(Tok, diag::err_expected_version); + SkipUntil(tok::comma, tok::r_paren, true, true, true); + return VersionTuple(); + } + ConsumeToken(); + return VersionTuple(Major, Minor, Subminor); +} + +/// \brief Parse the contents of the "availability" attribute. +/// +/// availability-attribute: +/// 'availability' '(' platform ',' version-arg-list ')' +/// +/// platform: +/// identifier +/// +/// version-arg-list: +/// version-arg +/// version-arg ',' version-arg-list +/// +/// version-arg: +/// 'introduced' '=' version +/// 'deprecated' '=' version +/// 'removed' = version +void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, + SourceLocation AvailabilityLoc, + ParsedAttributes &attrs, + SourceLocation *endLoc) { + SourceLocation PlatformLoc; + IdentifierInfo *Platform = 0; + + enum { Introduced, Deprecated, Obsoleted, Unknown }; + AvailabilityChange Changes[Unknown]; + + // Opening '('. + SourceLocation LParenLoc; + if (!Tok.is(tok::l_paren)) { + Diag(Tok, diag::err_expected_lparen); + return; + } + LParenLoc = ConsumeParen(); + + // Parse the platform name, + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_availability_expected_platform); + SkipUntil(tok::r_paren); + return; + } + Platform = Tok.getIdentifierInfo(); + PlatformLoc = ConsumeToken(); + + // Parse the ',' following the platform name. + if (ExpectAndConsume(tok::comma, diag::err_expected_comma, "", tok::r_paren)) + return; + + // If we haven't grabbed the pointers for the identifiers + // "introduced", "deprecated", and "obsoleted", do so now. + if (!Ident_introduced) { + Ident_introduced = PP.getIdentifierInfo("introduced"); + Ident_deprecated = PP.getIdentifierInfo("deprecated"); + Ident_obsoleted = PP.getIdentifierInfo("obsoleted"); + } + + // Parse the set of introductions/deprecations/removals. + do { + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_availability_expected_change); + SkipUntil(tok::r_paren); + return; + } + IdentifierInfo *Keyword = Tok.getIdentifierInfo(); + SourceLocation KeywordLoc = ConsumeToken(); + + if (Tok.isNot(tok::equal)) { + Diag(Tok, diag::err_expected_equal_after) + << Keyword; + SkipUntil(tok::r_paren); + return; + } + ConsumeToken(); + + SourceRange VersionRange; + VersionTuple Version = ParseVersionTuple(VersionRange); + + if (Version.empty()) { + SkipUntil(tok::r_paren); + return; + } + + unsigned Index; + if (Keyword == Ident_introduced) + Index = Introduced; + else if (Keyword == Ident_deprecated) + Index = Deprecated; + else if (Keyword == Ident_obsoleted) + Index = Obsoleted; + else + Index = Unknown; + + if (Index < Unknown) { + if (!Changes[Index].KeywordLoc.isInvalid()) { + Diag(KeywordLoc, diag::err_availability_redundant) + << Keyword + << SourceRange(Changes[Index].KeywordLoc, + Changes[Index].VersionRange.getEnd()); + } + + Changes[Index].KeywordLoc = KeywordLoc; + Changes[Index].Version = Version; + Changes[Index].VersionRange = VersionRange; + } else { + Diag(KeywordLoc, diag::err_availability_unknown_change) + << Keyword << VersionRange; + } + + if (Tok.isNot(tok::comma)) + break; + + ConsumeToken(); + } while (true); + + // Closing ')'. + SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); + if (RParenLoc.isInvalid()) + return; + + if (endLoc) + *endLoc = RParenLoc; + + // Record this attribute + attrs.add(AttrFactory.Create(&Availability, AvailabilityLoc, + 0, SourceLocation(), + Platform, PlatformLoc, + Changes[Introduced], + Changes[Deprecated], + Changes[Obsoleted], false, false)); +} + void Parser::DiagnoseProhibitedAttributes(ParsedAttributesWithRange &attrs) { Diag(attrs.Range.getBegin(), diag::err_attributes_not_allowed) << attrs.Range; diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 60ca1d49e7..f18531ebba 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -422,6 +422,10 @@ void Parser::Initialize() { Ident_vector = &PP.getIdentifierTable().get("vector"); Ident_pixel = &PP.getIdentifierTable().get("pixel"); } + + Ident_introduced = 0; + Ident_deprecated = 0; + Ident_obsoleted = 0; } /// ParseTopLevelDecl - Parse one top-level declaration, return whatever the diff --git a/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp index a715989c68..ae5ea673eb 100644 --- a/lib/Sema/AttributeList.cpp +++ b/lib/Sema/AttributeList.cpp @@ -36,6 +36,24 @@ AttributeList::AttributeList(llvm::BumpPtrAllocator &Alloc, } } +AttributeList::AttributeList(llvm::BumpPtrAllocator &Alloc, + IdentifierInfo *AttrName, SourceLocation AttrLoc, + IdentifierInfo *ScopeName, SourceLocation ScopeLoc, + IdentifierInfo *ParmName, SourceLocation ParmLoc, + const AvailabilityChange &Introduced, + const AvailabilityChange &Deprecated, + const AvailabilityChange &Obsoleted, + bool declspec, bool cxx0x) + : AttrName(AttrName), AttrLoc(AttrLoc), ScopeName(ScopeName), + ScopeLoc(ScopeLoc), ParmName(ParmName), ParmLoc(ParmLoc), + Args(0), NumArgs(0), Next(0), + DeclspecAttribute(declspec), CXX0XAttribute(cxx0x), + AvailabilityIntroduced(Introduced), + AvailabilityDeprecated(Deprecated), + AvailabilityObsoleted(Obsoleted), + Invalid(false) { +} + AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { llvm::StringRef AttrName = Name->getName(); @@ -83,6 +101,7 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { .Case("may_alias", AT_may_alias) .Case("base_check", AT_base_check) .Case("deprecated", AT_deprecated) + .Case("availability", AT_availability) .Case("visibility", AT_visibility) .Case("destructor", AT_destructor) .Case("format_arg", AT_format_arg) diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp index b7037ce83e..2334ab5128 100644 --- a/lib/Sema/CodeCompleteConsumer.cpp +++ b/lib/Sema/CodeCompleteConsumer.cpp @@ -375,12 +375,21 @@ void CodeCompletionResult::computeCursorKindAndAvailability() { switch (Kind) { case RK_Declaration: // Set the availability based on attributes. - Availability = CXAvailability_Available; - if (Declaration->getAttr()) - Availability = CXAvailability_NotAvailable; - else if (Declaration->getAttr()) + switch (Declaration->getAvailability()) { + case AR_Available: + case AR_NotYetIntroduced: + Availability = CXAvailability_Available; + break; + + case AR_Deprecated: Availability = CXAvailability_Deprecated; + break; + case AR_Unavailable: + Availability = CXAvailability_NotAvailable; + break; + } + if (FunctionDecl *Function = dyn_cast(Declaration)) if (Function->isDeleted()) Availability = CXAvailability_NotAvailable; diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 3c1127784d..3f33f144e4 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -1074,6 +1074,57 @@ static void HandleUnavailableAttr(Decl *d, const AttributeList &Attr, Sema &S) { d->addAttr(::new (S.Context) UnavailableAttr(Attr.getLoc(), S.Context, Str)); } +static void HandleAvailabilityAttr(Decl *d, const AttributeList &Attr, + Sema &S) { + IdentifierInfo *Platform = Attr.getParameterName(); + SourceLocation PlatformLoc = Attr.getParameterLoc(); + + llvm::StringRef PlatformName + = AvailabilityAttr::getPrettyPlatformName(Platform->getName()); + if (PlatformName.empty()) { + S.Diag(PlatformLoc, diag::warn_availability_unknown_platform) + << Platform; + + PlatformName = Platform->getName(); + } + + AvailabilityChange Introduced = Attr.getAvailabilityIntroduced(); + AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated(); + AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted(); + + // Ensure that Introduced < Deprecated < Obsoleted (although not all + // of these steps are needed). + if (Introduced.isValid() && Deprecated.isValid() && + !(Introduced.Version < Deprecated.Version)) { + S.Diag(Introduced.KeywordLoc, diag::warn_availability_version_ordering) + << 1 << PlatformName << Deprecated.Version.getAsString() + << 0 << Introduced.Version.getAsString(); + return; + } + + if (Introduced.isValid() && Obsoleted.isValid() && + !(Introduced.Version < Obsoleted.Version)) { + S.Diag(Introduced.KeywordLoc, diag::warn_availability_version_ordering) + << 2 << PlatformName << Obsoleted.Version.getAsString() + << 0 << Introduced.Version.getAsString(); + return; + } + + if (Deprecated.isValid() && Obsoleted.isValid() && + !(Deprecated.Version < Obsoleted.Version)) { + S.Diag(Deprecated.KeywordLoc, diag::warn_availability_version_ordering) + << 2 << PlatformName << Obsoleted.Version.getAsString() + << 1 << Deprecated.Version.getAsString(); + return; + } + + d->addAttr(::new (S.Context) AvailabilityAttr(Attr.getLoc(), S.Context, + Platform, + Introduced.Version, + Deprecated.Version, + Obsoleted.Version)); +} + static void HandleVisibilityAttr(Decl *d, const AttributeList &Attr, Sema &S) { // check the attribute arguments. if (Attr.getNumArgs() != 1) { @@ -1380,27 +1431,18 @@ static void HandleWeakImportAttr(Decl *D, const AttributeList &Attr, Sema &S) { // weak_import only applies to variable & function declarations. bool isDef = false; - if (VarDecl *VD = dyn_cast(D)) { - isDef = (!VD->hasExternalStorage() || VD->getInit()); - } else if (FunctionDecl *FD = dyn_cast(D)) { - isDef = FD->hasBody(); - } else if (isa(D) || isa(D)) { - // We ignore weak import on properties and methods - return; - } else if (!(S.LangOpts.ObjCNonFragileABI && isa(D))) { - // Don't issue the warning for darwin as target; yet, ignore the attribute. - if (S.Context.Target.getTriple().getOS() != llvm::Triple::Darwin || - !isa(D)) + if (!D->canBeWeakImported(isDef)) { + if (isDef) + S.Diag(Attr.getLoc(), + diag::warn_attribute_weak_import_invalid_on_definition) + << "weak_import" << 2 /*variable and function*/; + else if (S.Context.Target.getTriple().getOS() != llvm::Triple::Darwin || + (!isa(D) && + !isa(D) && + !isa(D))) S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedVariableOrFunction; - return; - } - // Merge should handle any subsequent violations. - if (isDef) { - S.Diag(Attr.getLoc(), - diag::warn_attribute_weak_import_invalid_on_definition) - << "weak_import" << 2 /*variable and function*/; return; } @@ -2745,6 +2787,7 @@ static void ProcessInheritableDeclAttr(Scope *scope, Decl *D, case AttributeList::AT_analyzer_noreturn: HandleAnalyzerNoReturnAttr (D, Attr, S); break; case AttributeList::AT_annotate: HandleAnnotateAttr (D, Attr, S); break; + case AttributeList::AT_availability:HandleAvailabilityAttr(D, Attr, S); break; case AttributeList::AT_carries_dependency: HandleDependencyAttr (D, Attr, S); break; case AttributeList::AT_common: HandleCommonAttr (D, Attr, S); break; @@ -3058,7 +3101,7 @@ void Sema::DelayedDiagnostics::popParsingDecl(Sema &S, ParsingDeclState state, static bool isDeclDeprecated(Decl *D) { do { - if (D->hasAttr()) + if (D->isDeprecated()) return true; } while ((D = cast_or_null(D->getDeclContext()))); return false; diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 8717385b51..101ade72e3 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -28,7 +28,7 @@ static void DiagnoseObjCImplementedDeprecations(Sema &S, NamedDecl *ND, SourceLocation ImplLoc, int select) { - if (ND && ND->getAttr()) { + if (ND && ND->isDeprecated()) { S.Diag(ImplLoc, diag::warn_deprecated_def) << select; if (select == 0) S.Diag(ND->getLocation(), diag::note_method_declared_at); @@ -1369,15 +1369,14 @@ void Sema::AddMethodToGlobalPool(ObjCMethodDecl *Method, bool impl, PrevObjCMethod->setDefined(impl); // If a method is deprecated, push it in the global pool. // This is used for better diagnostics. - if (Method->getAttr()) { - if (!PrevObjCMethod->getAttr()) + if (Method->isDeprecated()) { + if (!PrevObjCMethod->isDeprecated()) List->Method = Method; } // If new method is unavailable, push it into global pool // unless previous one is deprecated. - if (Method->getAttr()) { - if (!PrevObjCMethod->getAttr() && - !PrevObjCMethod->getAttr()) + if (Method->isUnavailable()) { + if (PrevObjCMethod->getAvailability() < AR_Deprecated) List->Method = Method; } return; diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index b8f0c729d1..c2f3a434b8 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -68,7 +68,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, Diag(Suppressed[I].first, Suppressed[I].second); // Clear out the list of suppressed diagnostics, so that we don't emit - // them again for this specialization. However, we don't remove this + // them again for this specialization. However, we don't obsolete this // entry from the table, because we want to avoid ever emitting these // diagnostics again. Suppressed.clear(); @@ -82,13 +82,28 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, return true; } - // See if the decl is deprecated. - if (const DeprecatedAttr *DA = D->getAttr()) - EmitDeprecationWarning(D, DA->getMessage(), Loc, UnknownObjCClass); + // See if this is a deleted function. + if (FunctionDecl *FD = dyn_cast(D)) { + if (FD->isDeleted()) { + Diag(Loc, diag::err_deleted_function_use); + Diag(D->getLocation(), diag::note_unavailable_here) << true; + return true; + } + } + + // See if this declaration is unavailable or deprecated. + std::string Message; + switch (D->getAvailability(&Message)) { + case AR_Available: + case AR_NotYetIntroduced: + break; - // See if the decl is unavailable - if (const UnavailableAttr *UA = D->getAttr()) { - if (UA->getMessage().empty()) { + case AR_Deprecated: + EmitDeprecationWarning(D, Message, Loc, UnknownObjCClass); + break; + + case AR_Unavailable: + if (Message.empty()) { if (!UnknownObjCClass) Diag(Loc, diag::err_unavailable) << D->getDeclName(); else @@ -97,17 +112,9 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, } else Diag(Loc, diag::err_unavailable_message) - << D->getDeclName() << UA->getMessage(); - Diag(D->getLocation(), diag::note_unavailable_here) << 0; - } - - // See if this is a deleted function. - if (FunctionDecl *FD = dyn_cast(D)) { - if (FD->isDeleted()) { - Diag(Loc, diag::err_deleted_function_use); - Diag(D->getLocation(), diag::note_unavailable_here) << true; - return true; - } + << D->getDeclName() << Message; + Diag(D->getLocation(), diag::note_unavailable_here) << 0; + break; } // Warn if this is used but marked unused. @@ -117,6 +124,23 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, return false; } +/// \brief Retrieve the message suffix that should be added to a +/// diagnostic complaining about the given function being deleted or +/// unavailable. +std::string Sema::getDeletedOrUnavailableSuffix(const FunctionDecl *FD) { + // FIXME: C++0x implicitly-deleted special member functions could be + // detected here so that we could improve diagnostics to say, e.g., + // "base class 'A' had a deleted copy constructor". + if (FD->isDeleted()) + return std::string(); + + std::string Message; + if (FD->getAvailability(&Message)) + return ": " + Message; + + return std::string(); +} + /// DiagnoseSentinelCalls - This routine checks on method dispatch calls /// (and other functions in future), which have been declared with sentinel /// attribute. It warns if call does not have the sentinel argument. diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 8fa2a5d2e8..0a8bd8fb52 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1388,16 +1388,16 @@ bool Sema::FindAllocationOverload(SourceLocation StartLoc, SourceRange Range, Candidates.NoteCandidates(*this, OCD_ViableCandidates, Args, NumArgs); return true; - case OR_Deleted: + case OR_Deleted: { Diag(StartLoc, diag::err_ovl_deleted_call) << Best->Function->isDeleted() << Name - << Best->Function->getMessageUnavailableAttr( - !Best->Function->isDeleted()) + << getDeletedOrUnavailableSuffix(Best->Function) << Range; Candidates.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs); return true; } + } assert(false && "Unreachable, bad result from BestViableFunction"); return true; } diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 91de095ac7..f607d65895 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -1138,10 +1138,14 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, static void AddPropertyAttrs(Sema &S, ObjCMethodDecl *PropertyMethod, ObjCPropertyDecl *Property) { // Should we just clone all attributes over? - if (DeprecatedAttr *A = Property->getAttr()) - PropertyMethod->addAttr(A->clone(S.Context)); - if (UnavailableAttr *A = Property->getAttr()) - PropertyMethod->addAttr(A->clone(S.Context)); + for (Decl::attr_iterator A = Property->attr_begin(), + AEnd = Property->attr_end(); + A != AEnd; ++A) { + if (isa(*A) || + isa(*A) || + isa(*A)) + PropertyMethod->addAttr((*A)->clone(S.Context)); + } } /// ProcessPropertyDecl - Make sure that any user-defined setter/getter methods diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 110f8cdc1c..7a4e68d3cb 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -6282,8 +6282,7 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc, // Best is the best viable function. if (Best->Function && - (Best->Function->isDeleted() || - Best->Function->getAttr())) + (Best->Function->isDeleted() || Best->Function->isUnavailable())) return OR_Deleted; return OR_Success; @@ -6735,7 +6734,7 @@ void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand, FunctionDecl *Fn = Cand->Function; // Note deleted candidates, but only if they're viable. - if (Cand->Viable && (Fn->isDeleted() || Fn->hasAttr())) { + if (Cand->Viable && (Fn->isDeleted() || Fn->isUnavailable())) { std::string FnDesc; OverloadCandidateKind FnKind = ClassifyOverloadCandidate(S, Fn, FnDesc); @@ -7744,8 +7743,7 @@ Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE, Diag(Fn->getSourceRange().getBegin(), diag::err_ovl_deleted_call) << Best->Function->isDeleted() << ULE->getName() - << Best->Function->getMessageUnavailableAttr( - !Best->Function->isDeleted()) + << getDeletedOrUnavailableSuffix(Best->Function) << Fn->getSourceRange(); CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs); } @@ -7928,8 +7926,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn, Diag(OpLoc, diag::err_ovl_deleted_oper) << Best->Function->isDeleted() << UnaryOperator::getOpcodeStr(Opc) - << Best->Function->getMessageUnavailableAttr( - !Best->Function->isDeleted()) + << getDeletedOrUnavailableSuffix(Best->Function) << Input->getSourceRange(); CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs); return ExprError(); @@ -8199,8 +8196,7 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, Diag(OpLoc, diag::err_ovl_deleted_oper) << Best->Function->isDeleted() << BinaryOperator::getOpcodeStr(Opc) - << Best->Function->getMessageUnavailableAttr( - !Best->Function->isDeleted()) + << getDeletedOrUnavailableSuffix(Best->Function) << Args[0]->getSourceRange() << Args[1]->getSourceRange(); CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, 2); return ExprError(); @@ -8349,8 +8345,7 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc, case OR_Deleted: Diag(LLoc, diag::err_ovl_deleted_oper) << Best->Function->isDeleted() << "[]" - << Best->Function->getMessageUnavailableAttr( - !Best->Function->isDeleted()) + << getDeletedOrUnavailableSuffix(Best->Function) << Args[0]->getSourceRange() << Args[1]->getSourceRange(); CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, 2, "[]", LLoc); @@ -8468,8 +8463,7 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, Diag(UnresExpr->getMemberLoc(), diag::err_ovl_deleted_member_call) << Best->Function->isDeleted() << DeclName - << Best->Function->getMessageUnavailableAttr( - !Best->Function->isDeleted()) + << getDeletedOrUnavailableSuffix(Best->Function) << MemExprE->getSourceRange(); CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs); // FIXME: Leaking incoming expressions! @@ -8643,8 +8637,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Object, diag::err_ovl_deleted_object_call) << Best->Function->isDeleted() << Object->getType() - << Best->Function->getMessageUnavailableAttr( - !Best->Function->isDeleted()) + << getDeletedOrUnavailableSuffix(Best->Function) << Object->getSourceRange(); CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs); break; @@ -8852,8 +8845,7 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc) { Diag(OpLoc, diag::err_ovl_deleted_oper) << Best->Function->isDeleted() << "->" - << Best->Function->getMessageUnavailableAttr( - !Best->Function->isDeleted()) + << getDeletedOrUnavailableSuffix(Best->Function) << Base->getSourceRange(); CandidateSet.NoteCandidates(*this, OCD_AllCandidates, &Base, 1); return ExprError(); diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index b34b62ab6f..1b55f71c36 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -37,6 +37,7 @@ #include "clang/Basic/FileSystemStatCache.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/Version.h" +#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Bitcode/BitstreamReader.h" #include "llvm/Support/MemoryBuffer.h" @@ -4870,6 +4871,18 @@ std::string ASTReader::ReadString(const RecordData &Record, unsigned &Idx) { return Result; } +VersionTuple ASTReader::ReadVersionTuple(const RecordData &Record, + unsigned &Idx) { + unsigned Major = Record[Idx++]; + unsigned Minor = Record[Idx++]; + unsigned Subminor = Record[Idx++]; + if (Minor == 0) + return VersionTuple(Major); + if (Subminor == 0) + return VersionTuple(Major, Minor - 1); + return VersionTuple(Major, Minor - 1, Subminor - 1); +} + CXXTemporary *ASTReader::ReadCXXTemporary(const RecordData &Record, unsigned &Idx) { CXXDestructorDecl *Decl = cast(GetDecl(Record[Idx++])); diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 877650f9de..482ce4bec7 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -37,6 +37,7 @@ #include "clang/Basic/SourceManagerInternals.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/Version.h" +#include "clang/Basic/VersionTuple.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/StringExtras.h" @@ -2622,6 +2623,19 @@ void ASTWriter::AddString(llvm::StringRef Str, RecordDataImpl &Record) { Record.insert(Record.end(), Str.begin(), Str.end()); } +void ASTWriter::AddVersionTuple(const VersionTuple &Version, + RecordDataImpl &Record) { + Record.push_back(Version.getMajor()); + if (llvm::Optional Minor = Version.getMinor()) + Record.push_back(*Minor + 1); + else + Record.push_back(0); + if (llvm::Optional Subminor = Version.getSubminor()) + Record.push_back(*Subminor + 1); + else + Record.push_back(0); +} + /// \brief Note that the identifier II occurs at the given offset /// within the identifier table. void ASTWriter::SetIdentifierOffset(const IdentifierInfo *II, uint32_t Offset) { diff --git a/test/CodeGenObjC/attr-availability.m b/test/CodeGenObjC/attr-availability.m new file mode 100644 index 0000000000..4f1fad81b3 --- /dev/null +++ b/test/CodeGenObjC/attr-availability.m @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi "-triple" "x86_64-apple-darwin8.0.0" -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-10_4 %s +// RUN: %clang_cc1 -fobjc-nonfragile-abi "-triple" "x86_64-apple-darwin9.0.0" -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-10_5 %s +// RUN: %clang_cc1 -fobjc-nonfragile-abi "-triple" "x86_64-apple-darwin10.0.0" -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-10_6 %s + +// CHECK-10_4: @"OBJC_CLASS_$_WeakClass1" = extern_weak global +// CHECK-10_5: @"OBJC_CLASS_$_WeakClass1" = external global +// CHECK-10_6: @"OBJC_CLASS_$_WeakClass1" = external global +__attribute__((availability(macosx,introduced=10.5))) +@interface WeakClass1 @end + +@implementation WeakClass1(MyCategory) @end + +@implementation WeakClass1(YourCategory) @end + +// CHECK-10_4: @"OBJC_CLASS_$_WeakClass2" = extern_weak global +// CHECK-10_5: @"OBJC_CLASS_$_WeakClass2" = extern_weak global +// CHECK-10_6: @"OBJC_CLASS_$_WeakClass2" = external global +__attribute__((availability(macosx,introduced=10.6))) +@interface WeakClass2 @end + +@implementation WeakClass2(MyCategory) @end + +@implementation WeakClass2(YourCategory) @end + diff --git a/test/Parser/attr-availability.c b/test/Parser/attr-availability.c new file mode 100644 index 0000000000..10d55b558b --- /dev/null +++ b/test/Parser/attr-availability.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +void f0() __attribute__((availability(macosx,introduced=10.2,deprecated=10.4,obsoleted=10.6))); + +void f1() __attribute__((availability(macosx,deprecated=10.4,introduced=10.2,obsoleted=10.6))); + +void f2() __attribute__((availability(ios,deprecated=10.4.7,introduced=10,obsoleted=10.6))); + +void f3() __attribute__((availability(ios,deprecated=10.4.7,introduced=10,obsoleted=10.6,introduced=10.2))); // expected-error{{redundant 'introduced' availability change; only the last specified change will be used}} + diff --git a/test/Sema/attr-availability-ios.c b/test/Sema/attr-availability-ios.c new file mode 100644 index 0000000000..e531d6bf31 --- /dev/null +++ b/test/Sema/attr-availability-ios.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 "-triple" "x86_64-apple-darwin3.0.0-iphoneos" -fsyntax-only -verify %s + +void f0(int) __attribute__((availability(ios,introduced=2.0,deprecated=2.1))); +void f1(int) __attribute__((availability(ios,introduced=2.1))); +void f2(int) __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); +void f3(int) __attribute__((availability(ios,introduced=3.0))); +void f4(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5), availability(ios,introduced=2.0,deprecated=2.1,obsoleted=3.0))); // expected-note{{explicitly marked unavailable}} + +void test() { + f0(0); // expected-warning{{'f0' is deprecated: first deprecated in iOS 2.1}} + f1(0); + f2(0); // expected-warning{{'f2' is deprecated: first deprecated in iOS 3.0}} + f3(0); + f4(0); // expected-error{{f4' is unavailable: obsoleted in iOS 3.0}} +} diff --git a/test/Sema/attr-availability-macosx.c b/test/Sema/attr-availability-macosx.c new file mode 100644 index 0000000000..b43da6f59f --- /dev/null +++ b/test/Sema/attr-availability-macosx.c @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 "-triple" "x86_64-apple-darwin9.0.0" -fsyntax-only -verify %s + +void f0(int) __attribute__((availability(macosx,introduced=10.4,deprecated=10.6))); +void f1(int) __attribute__((availability(macosx,introduced=10.5))); +void f2(int) __attribute__((availability(macosx,introduced=10.4,deprecated=10.5))); +void f3(int) __attribute__((availability(macosx,introduced=10.6))); +void f4(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5), availability(ios,introduced=2.0,deprecated=3.0))); // expected-note{{explicitly marked unavailable}} + +void test() { + f0(0); + f1(0); + f2(0); // expected-warning{{'f2' is deprecated: first deprecated in Mac OS X 10.5}} + f3(0); + f4(0); // expected-error{{f4' is unavailable: obsoleted in Mac OS X 10.5}} +} diff --git a/test/Sema/attr-availability.c b/test/Sema/attr-availability.c new file mode 100644 index 0000000000..1314cf5a5b --- /dev/null +++ b/test/Sema/attr-availability.c @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +void f0() __attribute__((availability(macosx,introduced=10.4,deprecated=10.2))); // expected-warning{{feature cannot be deprecated in Mac OS X version 10.2 before it was introduced in version 10.4; attribute ignored}} +void f1() __attribute__((availability(ios,obsoleted=2.1,deprecated=3.0))); // expected-warning{{feature cannot be obsoleted in iOS version 2.1 before it was deprecated in version 3.0; attribute ignored}} + +void f2() __attribute__((availability(otheros,introduced=2.2))); // expected-warning{{unknown platform 'otheros' in availability macro}} diff --git a/test/SemaCXX/attr-unavailable.cpp b/test/SemaCXX/attr-unavailable.cpp index fe3e8b1470..5f34ed9904 100644 --- a/test/SemaCXX/attr-unavailable.cpp +++ b/test/SemaCXX/attr-unavailable.cpp @@ -25,6 +25,6 @@ namespace radar9046492 { void foo() FOO; // expected-note {{candidate function has been explicitly made unavailable}} void bar() { - foo(); // expected-error {{call to unavailable function 'foo' not available - replaced}} + foo(); // expected-error {{call to unavailable function 'foo': not available - replaced}} } } diff --git a/test/SemaObjC/protocol-attribute.m b/test/SemaObjC/protocol-attribute.m index 52c980396e..178774c664 100644 --- a/test/SemaObjC/protocol-attribute.m +++ b/test/SemaObjC/protocol-attribute.m @@ -40,7 +40,7 @@ Class clsP1 = 0; // expected-warning {{'MyProto1' is deprecated}} __attribute ((unavailable)) __attribute ((deprecated)) @protocol XProto; // expected-note{{marked unavailable}} -id idX = 0; // expected-error {{'XProto' is unavailable}} expected-warning {{'XProto' is deprecated}} +id idX = 0; // expected-error {{'XProto' is unavailable}} int main () { diff --git a/test/SemaObjC/special-dep-unavail-warning.m b/test/SemaObjC/special-dep-unavail-warning.m index b7a2d66344..8e03e1c4c2 100644 --- a/test/SemaObjC/special-dep-unavail-warning.m +++ b/test/SemaObjC/special-dep-unavail-warning.m @@ -36,10 +36,8 @@ void test(C *c) { [c unavailMeth1]; // expected-warning {{'unavailMeth1' maybe unavailable because receiver type is unknown}} [c depInA2]; // expected-warning {{'depInA2' maybe deprecated because receiver type is unknown}} [c unavailMeth2]; // expected-warning {{'unavailMeth2' maybe unavailable because receiver type is unknown}} - [c depunavailInA]; // expected-warning {{'depunavailInA' maybe deprecated because receiver type is unknown}} \ - // expected-warning {{'depunavailInA' maybe unavailable because receiver type is unknown}} - [c depunavailInA1]; // expected-warning {{'depunavailInA1' maybe deprecated because receiver type is unknown}} \ - // expected-warning {{'depunavailInA1' maybe unavailable because receiver type is unknown}} + [c depunavailInA]; // expected-warning {{'depunavailInA' maybe unavailable because receiver type is unknown}} + [c depunavailInA1];// expected-warning {{'depunavailInA1' maybe unavailable because receiver type is unknown}} [c FuzzyMeth]; // expected-warning {{'FuzzyMeth' maybe deprecated because receiver type is unknown}} [c FuzzyMeth1]; // expected-warning {{'FuzzyMeth1' maybe deprecated because receiver type is unknown}} diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index c9a923cfc5..b12e7fe41e 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -4897,14 +4897,22 @@ extern "C" { enum CXAvailabilityKind clang_getCursorAvailability(CXCursor cursor) { if (clang_isDeclaration(cursor.kind)) if (Decl *D = cxcursor::getCursorDecl(cursor)) { - if (D->hasAttr() || - (isa(D) && cast(D)->isDeleted())) + if (isa(D) && cast(D)->isDeleted()) return CXAvailability_Available; - if (D->hasAttr()) + switch (D->getAvailability()) { + case AR_Available: + case AR_NotYetIntroduced: + return CXAvailability_Available; + + case AR_Deprecated: return CXAvailability_Deprecated; + + case AR_Unavailable: + return CXAvailability_NotAvailable; + } } - + return CXAvailability_Available; } -- 2.40.0