]> granicus.if.org Git - clang/commitdiff
Introduce type nullability specifiers for C/C++.
authorDouglas Gregor <dgregor@apple.com>
Fri, 19 Jun 2015 17:51:05 +0000 (17:51 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 19 Jun 2015 17:51:05 +0000 (17:51 +0000)
Introduces the type specifiers __nonnull, __nullable, and
__null_unspecified that describe the nullability of the pointer type
to which the specifier appertains. Nullability type specifiers improve
on the existing nonnull attributes in a few ways:
  - They apply to types, so one can represent a pointer to a non-null
    pointer, use them in function pointer types, etc.
  - As type specifiers, they are syntactically more lightweight than
    __attribute__s or [[attribute]]s.
  - They can express both the notion of 'should never be null' and
  also 'it makes sense for this to be null', and therefore can more
  easily catch errors of omission where one forgot to annotate the
  nullability of a particular pointer (this will come in a subsequent
  patch).

Nullability type specifiers are maintained as type sugar, and
therefore have no effect on mangling, encoding, overloading,
etc. Nonetheless, they will be used for warnings about, e.g., passing
'null' to a method that does not accept it.

This is the C/C++ part of rdar://problem/18868820.

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

24 files changed:
include/clang/AST/Type.h
include/clang/Basic/Attr.td
include/clang/Basic/DiagnosticCommonKinds.td
include/clang/Basic/DiagnosticGroups.td
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Basic/Specifiers.h
include/clang/Basic/TokenKinds.def
include/clang/Parse/Parser.h
lib/AST/Type.cpp
lib/AST/TypePrinter.cpp
lib/Lex/PPMacroExpansion.cpp
lib/Parse/ParseDecl.cpp
lib/Parse/ParseTentative.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaType.cpp
lib/Sema/TreeTransform.h
test/FixIt/fixit-nullability-declspec.cpp [new file with mode: 0644]
test/Parser/nullability.c [new file with mode: 0644]
test/Sema/non-null-warning.c [new file with mode: 0644]
test/Sema/nullability.c [new file with mode: 0644]
test/SemaCXX/nullability-declspec.cpp [new file with mode: 0644]
test/SemaCXX/nullability.cpp [new file with mode: 0644]
test/SemaObjC/nullability.m [new file with mode: 0644]

index 8cd29b7b917eafe5898100fff9ef6770d6bd1370..d821258736237686496635c76ec7a4b581755389 100644 (file)
@@ -1818,6 +1818,19 @@ public:
   /// checking. Should always return true.
   bool isLinkageValid() const;
 
+  /// Determine the nullability of the given type.
+  ///
+  /// Note that nullability is only captured as sugar within the type
+  /// system, not as part of the canonical type, so nullability will
+  /// be lost by canonicalization and desugaring.
+  Optional<NullabilityKind> getNullability(const ASTContext &context) const;
+
+  /// Determine whether the given type can have a nullability
+  /// specifier applied to it, i.e., if it is any kind of pointer type
+  /// or a dependent type that could instantiate to any kind of
+  /// pointer type.
+  bool canHaveNullability() const;
+
   const char *getTypeClassName() const;
 
   QualType getCanonicalTypeInternal() const {
@@ -3479,7 +3492,10 @@ public:
     attr_ptr32,
     attr_ptr64,
     attr_sptr,
-    attr_uptr
+    attr_uptr,
+    attr_nonnull,
+    attr_nullable,
+    attr_null_unspecified,
   };
 
 private:
@@ -3513,6 +3529,34 @@ public:
 
   bool isCallingConv() const;
 
+  llvm::Optional<NullabilityKind> getImmediateNullability() const;
+
+  /// Retrieve the attribute kind corresponding to the given
+  /// nullability kind.
+  static Kind getNullabilityAttrKind(NullabilityKind kind) {
+    switch (kind) {
+    case NullabilityKind::NonNull:
+      return attr_nonnull;
+     
+    case NullabilityKind::Nullable:
+      return attr_nullable;
+
+    case NullabilityKind::Unspecified:
+      return attr_null_unspecified;
+    }
+  }
+
+  /// Strip off the top-level nullability annotation on the given
+  /// type, if it's there.
+  ///
+  /// \param T The type to strip. If the type is exactly an
+  /// AttributedType specifying nullability (without looking through
+  /// type sugar), the nullability is returned and this type changed
+  /// to the underlying modified type.
+  ///
+  /// \returns the top-level nullability, if present.
+  static Optional<NullabilityKind> stripOuterNullability(QualType &T);
+
   void Profile(llvm::FoldingSetNodeID &ID) {
     Profile(ID, getAttrKind(), ModifiedType, EquivalentType);
   }
index 8394655c4b665c3ff6989b0d5448164c98e0e3b7..f36f654ff6301daa39132f63788dd5113bd210c4 100644 (file)
@@ -960,6 +960,22 @@ def ReturnsNonNull : InheritableAttr {
   let Documentation = [Undocumented];
 }
 
+// Nullability type attributes.
+def TypeNonNull : TypeAttr {
+  let Spellings = [Keyword<"__nonnull">];
+  let Documentation = [Undocumented];
+}
+
+def TypeNullable : TypeAttr {
+  let Spellings = [Keyword<"__nullable">];
+  let Documentation = [Undocumented];
+}
+
+def TypeNullUnspecified : TypeAttr {
+  let Spellings = [Keyword<"__null_unspecified">];
+  let Documentation = [Undocumented];
+}
+
 def AssumeAligned : InheritableAttr {
   let Spellings = [GCC<"assume_aligned">];
   let Subjects = SubjectList<[ObjCMethod, Function]>;
index deb23f3149cba97f149b5de6f49b63c75db7192e..853259434245de0080bf6562cb9166c8c6ffc214 100644 (file)
@@ -99,6 +99,20 @@ def err_enum_template : Error<"enumeration cannot be a template">;
 
 }
 
+let CategoryName = "Nullability Issue" in {
+
+def warn_mismatched_nullability_attr : Warning<
+  "nullability specifier "
+  "'__%select{nonnull|nullable|null_unspecified}0' "
+  "conflicts with existing specifier "
+  "'__%select{nonnull|nullable|null_unspecified}1'">,
+  InGroup<Nullability>;
+
+def note_nullability_here : Note<
+  "'%select{__nonnull|__nullable|__null_unspecified}0' specified here">;
+
+}
+
 // Sema && Lex
 def ext_c99_longlong : Extension<
   "'long long' is an extension when C99 mode is not enabled">,
index 13dcc73dd1330c9809dfa86b48d6e9c2fb0aa8bd..84ffe09bbe62c5c026ad9578098db9f36f16f052 100644 (file)
@@ -248,6 +248,8 @@ def MissingFieldInitializers : DiagGroup<"missing-field-initializers">;
 def ModuleBuild : DiagGroup<"module-build">;
 def ModuleConflict : DiagGroup<"module-conflict">;
 def NewlineEOF : DiagGroup<"newline-eof">;
+def Nullability : DiagGroup<"nullability">;
+def NullabilityDeclSpec : DiagGroup<"nullability-declspec">;
 def NullArithmetic : DiagGroup<"null-arithmetic">;
 def NullCharacter : DiagGroup<"null-character">;
 def NullDereference : DiagGroup<"null-dereference">;
index 3df8b949a1611d37738d218089b3b6f9eb0e074d..6a11d240c63ce3f7e6d74d622004fead12897fd5 100644 (file)
@@ -65,6 +65,10 @@ def ext_keyword_as_ident : ExtWarn<
   "%select{here|for the remainder of the translation unit}1">,
   InGroup<KeywordCompat>;
 
+def ext_nullability : Extension<
+  "type nullability specifier %0 is a Clang extension">,
+  InGroup<DiagGroup<"nullability-extension">>;
+
 def error_empty_enum : Error<"use of empty enum">;
 def err_invalid_sign_spec : Error<"'%0' cannot be signed or unsigned">;
 def err_invalid_short_spec : Error<"'short %0' is invalid">;
index b8fadda8547049bf2a1a3883a1d6073f224ac9a2..ac534436e761d1e7d4d53876190496c0be27358b 100644 (file)
@@ -7674,4 +7674,32 @@ def warn_profile_data_unprofiled : Warning<
 
 } // end of instrumentation issue category
 
+let CategoryName = "Nullability Issue" in {
+
+def warn_duplicate_nullability : Warning<
+  "duplicate nullability specifier "
+  "'%select{__nonnull|__nullable|__null_unspecified}0'">,
+  InGroup<Nullability>;
+
+def warn_nullability_declspec : Warning<
+  "nullability specifier "
+  "'%select{__nonnull|__nullable|__null_unspecified}0' cannot be applied "
+  "to non-pointer type %1; did you mean to apply the specifier to the "
+  "%select{pointer|block pointer|member pointer|function pointer|"
+  "member function pointer}2?">,
+  InGroup<NullabilityDeclSpec>,
+  DefaultError;
+
+def err_nullability_nonpointer : Error<
+  "nullability specifier "
+  "'%select{__nonnull|__nullable|__null_unspecified}0' cannot be applied to "
+  "non-pointer type %1">;
+
+def err_nullability_conflicting : Error<
+  "nullability specifier "
+  "'%select{__nonnull|__nullable|__null_unspecified}0' conflicts with existing "
+  "specifier '%select{__nonnull|__nullable|__null_unspecified}1'">;
+
+}
+
 } // end of sema component.
index 7569c16412b222a469af36b838a30f1cf00ab2bb..2a872b277ea2ed69c578dc0cd24589dd354e7bde 100644 (file)
@@ -16,6 +16,8 @@
 #ifndef LLVM_CLANG_BASIC_SPECIFIERS_H
 #define LLVM_CLANG_BASIC_SPECIFIERS_H
 
+#include "llvm/Support/DataTypes.h"
+
 namespace clang {
   /// \brief Specifies the width of a type, e.g., short, long, or long long.
   enum TypeSpecifierWidth {
@@ -239,6 +241,19 @@ namespace clang {
     SD_Static,         ///< Static storage duration.
     SD_Dynamic         ///< Dynamic storage duration.
   };
+
+  /// Describes the nullability of a particular type.
+  enum class NullabilityKind : uint8_t {
+    /// Values of this type can never be null.
+    NonNull = 0,
+    /// Values of this type can be null.
+    Nullable,
+    /// Whether values of this type can be null is (explicitly)
+    /// unspecified. This captures a (fairly rare) case where we
+    /// can't conclude anything about the nullability of the type even
+    /// though it has been considered.
+    Unspecified
+  };
 } // end namespace clang
 
 #endif // LLVM_CLANG_BASIC_SPECIFIERS_H
index b6d983b552452d90062b8e92a820136ecc6349c9..67b9933562ebb357c5df57f80d6fa67a22d2f8ea 100644 (file)
@@ -548,6 +548,11 @@ ALIAS("__typeof__"   , typeof     , KEYALL)
 ALIAS("__volatile"   , volatile   , KEYALL)
 ALIAS("__volatile__" , volatile   , KEYALL)
 
+// Type nullability.
+KEYWORD(__nonnull                 , KEYALL)
+KEYWORD(__nullable                , KEYALL)
+KEYWORD(__null_unspecified        , KEYALL)
+
 // Microsoft extensions which should be disabled in strict conformance mode
 KEYWORD(__ptr64                       , KEYMS)
 KEYWORD(__ptr32                       , KEYMS)
index caba77b74c673eeeb1e8954f88824c463acf9a73..3d48f0ad5c930028369a581645b1f67f31c8d2bb 100644 (file)
@@ -2106,6 +2106,7 @@ private:
   void ParseBorlandTypeAttributes(ParsedAttributes &attrs);
   void ParseOpenCLAttributes(ParsedAttributes &attrs);
   void ParseOpenCLQualifiers(ParsedAttributes &Attrs);
+  void ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs);
 
   VersionTuple ParseVersionTuple(SourceRange &Range);
   void ParseAvailabilityAttribute(IdentifierInfo &Availability,
index 09bb7692596c696d0315fa68c0f5dd611a26b99a..15f3d39634d3ab850dff0e1ae97f3137c66c9a31 100644 (file)
@@ -1919,7 +1919,10 @@ bool AttributedType::isCallingConv() const {
   case attr_objc_gc:
   case attr_objc_ownership:
   case attr_noreturn:
-      return false;
+  case attr_nonnull:
+  case attr_nullable:
+  case attr_null_unspecified:
+    return false;
   case attr_pcs:
   case attr_pcs_vfp:
   case attr_cdecl:
@@ -2340,6 +2343,152 @@ LinkageInfo Type::getLinkageAndVisibility() const {
   return LV;
 }
 
+Optional<NullabilityKind> Type::getNullability(const ASTContext &context) const {
+  QualType type(this, 0);
+  do {
+    // Check whether this is an attributed type with nullability
+    // information.
+    if (auto attributed = dyn_cast<AttributedType>(type.getTypePtr())) {
+      if (auto nullability = attributed->getImmediateNullability())
+        return nullability;
+    }
+
+    // Desugar the type. If desugaring does nothing, we're done.
+    QualType desugared = type.getSingleStepDesugaredType(context);
+    if (desugared.getTypePtr() == type.getTypePtr())
+      return None;
+    
+    type = desugared;
+  } while (true);
+}
+
+bool Type::canHaveNullability() const {
+  QualType type = getCanonicalTypeInternal();
+  
+  switch (type->getTypeClass()) {
+  // We'll only see canonical types here.
+#define NON_CANONICAL_TYPE(Class, Parent)       \
+  case Type::Class:                             \
+    llvm_unreachable("non-canonical type");
+#define TYPE(Class, Parent)
+#include "clang/AST/TypeNodes.def"
+
+  // Pointer types.
+  case Type::Pointer:
+  case Type::BlockPointer:
+  case Type::MemberPointer:
+  case Type::ObjCObjectPointer:
+    return true;
+
+  // Dependent types that could instantiate to pointer types.
+  case Type::UnresolvedUsing:
+  case Type::TypeOfExpr:
+  case Type::TypeOf:
+  case Type::Decltype:
+  case Type::UnaryTransform:
+  case Type::TemplateTypeParm:
+  case Type::SubstTemplateTypeParmPack:
+  case Type::DependentName:
+  case Type::DependentTemplateSpecialization:
+    return true;
+
+  // Dependent template specializations can instantiate to pointer
+  // types unless they're known to be specializations of a class
+  // template.
+  case Type::TemplateSpecialization:
+    if (TemplateDecl *templateDecl
+          = cast<TemplateSpecializationType>(type.getTypePtr())
+              ->getTemplateName().getAsTemplateDecl()) {
+      if (isa<ClassTemplateDecl>(templateDecl))
+        return false;
+    }
+    return true;
+
+  // auto is considered dependent when it isn't deduced.
+  case Type::Auto:
+    return !cast<AutoType>(type.getTypePtr())->isDeduced();
+
+  case Type::Builtin:
+    switch (cast<BuiltinType>(type.getTypePtr())->getKind()) {
+      // Signed, unsigned, and floating-point types cannot have nullability.
+#define SIGNED_TYPE(Id, SingletonId) case BuiltinType::Id:
+#define UNSIGNED_TYPE(Id, SingletonId) case BuiltinType::Id:
+#define FLOATING_TYPE(Id, SingletonId) case BuiltinType::Id:
+#define BUILTIN_TYPE(Id, SingletonId)
+#include "clang/AST/BuiltinTypes.def"
+      return false;
+
+    // Dependent types that could instantiate to a pointer type.
+    case BuiltinType::Dependent:
+    case BuiltinType::Overload:
+    case BuiltinType::BoundMember:
+    case BuiltinType::PseudoObject:
+    case BuiltinType::UnknownAny:
+    case BuiltinType::ARCUnbridgedCast:
+      return true;
+
+    case BuiltinType::Void:
+    case BuiltinType::ObjCId:
+    case BuiltinType::ObjCClass:
+    case BuiltinType::ObjCSel:
+    case BuiltinType::OCLImage1d:
+    case BuiltinType::OCLImage1dArray:
+    case BuiltinType::OCLImage1dBuffer:
+    case BuiltinType::OCLImage2d:
+    case BuiltinType::OCLImage2dArray:
+    case BuiltinType::OCLImage3d:
+    case BuiltinType::OCLSampler:
+    case BuiltinType::OCLEvent:
+    case BuiltinType::BuiltinFn:
+    case BuiltinType::NullPtr:
+      return false;
+    }
+
+  // Non-pointer types.
+  case Type::Complex:
+  case Type::LValueReference:
+  case Type::RValueReference:
+  case Type::ConstantArray:
+  case Type::IncompleteArray:
+  case Type::VariableArray:
+  case Type::DependentSizedArray:
+  case Type::DependentSizedExtVector:
+  case Type::Vector:
+  case Type::ExtVector:
+  case Type::FunctionProto:
+  case Type::FunctionNoProto:
+  case Type::Record:
+  case Type::Enum:
+  case Type::InjectedClassName:
+  case Type::PackExpansion:
+  case Type::ObjCObject:
+  case Type::ObjCInterface:
+  case Type::Atomic:
+    return false;
+  }
+}
+
+llvm::Optional<NullabilityKind> AttributedType::getImmediateNullability() const {
+  if (getAttrKind() == AttributedType::attr_nonnull)
+    return NullabilityKind::NonNull;
+  if (getAttrKind() == AttributedType::attr_nullable)
+    return NullabilityKind::Nullable;
+  if (getAttrKind() == AttributedType::attr_null_unspecified)
+    return NullabilityKind::Unspecified;
+  return None;
+}
+
+Optional<NullabilityKind> AttributedType::stripOuterNullability(QualType &T) {
+  if (auto attributed = dyn_cast<AttributedType>(T.getTypePtr())) {
+    if (auto nullability = attributed->getImmediateNullability()) {
+      T = attributed->getModifiedType();
+      return nullability;
+    }
+  }
+
+  return None;
+}
+
 Qualifiers::ObjCLifetime Type::getObjCARCImplicitLifetime() const {
   if (isObjCARCImplicitlyUnretainedType())
     return Qualifiers::OCL_ExplicitNone;
index 3928fe8f89420705b5b57964b3da78360ff715f0..ebe09d85495e15dc25fba8468d2a7696831a474d 100644 (file)
@@ -1141,6 +1141,21 @@ void TypePrinter::printAttributedBefore(const AttributedType *T,
     }
     spaceBeforePlaceHolder(OS);
   }
+
+  // Print nullability type specifiers.
+  if (T->getAttrKind() == AttributedType::attr_nonnull ||
+      T->getAttrKind() == AttributedType::attr_nullable ||
+      T->getAttrKind() == AttributedType::attr_null_unspecified) {
+    if (T->getAttrKind() == AttributedType::attr_nonnull)
+      OS << " __nonnull";
+    else if (T->getAttrKind() == AttributedType::attr_nullable)
+      OS << " __nullable";
+    else if (T->getAttrKind() == AttributedType::attr_null_unspecified)
+      OS << " __null_unspecified";
+    else
+      llvm_unreachable("unhandled nullability");
+    spaceBeforePlaceHolder(OS);
+  }
 }
 
 void TypePrinter::printAttributedAfter(const AttributedType *T,
@@ -1154,12 +1169,34 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
   if (T->isMSTypeSpec())
     return;
 
+  // Nothing to print after.
+  if (T->getAttrKind() == AttributedType::attr_nonnull ||
+      T->getAttrKind() == AttributedType::attr_nullable ||
+      T->getAttrKind() == AttributedType::attr_null_unspecified)
+    return printAfter(T->getModifiedType(), OS);
+
   // If this is a calling convention attribute, don't print the implicit CC from
   // the modified type.
   SaveAndRestore<bool> MaybeSuppressCC(InsideCCAttribute, T->isCallingConv());
 
   printAfter(T->getModifiedType(), OS);
 
+  // Print nullability type specifiers that occur after
+  if (T->getAttrKind() == AttributedType::attr_nonnull ||
+      T->getAttrKind() == AttributedType::attr_nullable ||
+      T->getAttrKind() == AttributedType::attr_null_unspecified) {
+    if (T->getAttrKind() == AttributedType::attr_nonnull)
+      OS << " __nonnull";
+    else if (T->getAttrKind() == AttributedType::attr_nullable)
+      OS << " __nullable";
+    else if (T->getAttrKind() == AttributedType::attr_null_unspecified)
+      OS << " __null_unspecified";
+    else
+      llvm_unreachable("unhandled nullability");
+
+    return;
+  }
+
   OS << " __attribute__((";
   switch (T->getAttrKind()) {
   default: llvm_unreachable("This attribute should have been handled already");
index ccfb30240b0bf8b571bb31ebd9901abcccf0be2e..62498c6446473a931b331113d74aec7a46022737 100644 (file)
@@ -1075,6 +1075,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
       .Case("cxx_exceptions", LangOpts.CXXExceptions)
       .Case("cxx_rtti", LangOpts.RTTI)
       .Case("enumerator_attributes", true)
+      .Case("nullability", LangOpts.ObjC1 || LangOpts.GNUMode)
       .Case("memory_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Memory))
       .Case("thread_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Thread))
       .Case("dataflow_sanitizer", LangOpts.Sanitize.has(SanitizerKind::DataFlow))
@@ -1222,6 +1223,7 @@ static bool HasExtension(const Preprocessor &PP, const IdentifierInfo *II) {
   // Because we inherit the feature list from HasFeature, this string switch
   // must be less restrictive than HasFeature's.
   return llvm::StringSwitch<bool>(Extension)
+           .Case("nullability", true)
            // C11 features supported by other languages as extensions.
            .Case("c_alignas", true)
            .Case("c_alignof", true)
index eb6800d65c9e51e3ed4074fc0622d7599cae275e..96555fcf8773b8a41f54193e5430d37f16480cf3 100644 (file)
@@ -687,6 +687,28 @@ void Parser::ParseOpenCLQualifiers(ParsedAttributes &Attrs) {
                AttributeList::AS_Keyword);
 }
 
+void Parser::ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs) {
+  // Treat these like attributes, even though they're type specifiers.
+  while (true) {
+    switch (Tok.getKind()) {
+    case tok::kw___nonnull:
+    case tok::kw___nullable:
+    case tok::kw___null_unspecified: {
+      IdentifierInfo *AttrName = Tok.getIdentifierInfo();
+      SourceLocation AttrNameLoc = ConsumeToken();
+      if (!getLangOpts().ObjC1)
+        Diag(AttrNameLoc, diag::ext_nullability)
+          << AttrName;
+      attrs.addNew(AttrName, AttrNameLoc, nullptr, AttrNameLoc, nullptr, 0, 
+                   AttributeList::AS_Keyword);
+      break;
+    }
+    default:
+      return;
+    }
+  }
+}
+
 static bool VersionNumberSeparator(const char Separator) {
   return (Separator == '.' || Separator == '_');
 }
@@ -3040,6 +3062,13 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
       ParseOpenCLAttributes(DS.getAttributes());
       continue;
 
+    // Nullability type specifiers.
+    case tok::kw___nonnull:
+    case tok::kw___nullable:
+    case tok::kw___null_unspecified:
+      ParseNullabilityTypeSpecifiers(DS.getAttributes());
+      continue;
+
     // storage-class-specifier
     case tok::kw_typedef:
       isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_typedef, Loc,
@@ -4284,6 +4313,10 @@ bool Parser::isTypeSpecifierQualifier() {
   case tok::kw___pascal:
   case tok::kw___unaligned:
 
+  case tok::kw___nonnull:
+  case tok::kw___nullable:
+  case tok::kw___null_unspecified:
+
   case tok::kw___private:
   case tok::kw___local:
   case tok::kw___global:
@@ -4457,6 +4490,10 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
   case tok::kw___pascal:
   case tok::kw___unaligned:
 
+  case tok::kw___nonnull:
+  case tok::kw___nullable:
+  case tok::kw___null_unspecified:
+
   case tok::kw___private:
   case tok::kw___local:
   case tok::kw___global:
@@ -4686,6 +4723,14 @@ void Parser::ParseTypeQualifierListOpt(DeclSpec &DS, unsigned AttrReqs,
         continue;
       }
       goto DoneWithTypeQuals;
+
+    // Nullability type specifiers.
+    case tok::kw___nonnull:
+    case tok::kw___nullable:
+    case tok::kw___null_unspecified:
+      ParseNullabilityTypeSpecifiers(DS.getAttributes());
+      continue;
+
     case tok::kw___attribute:
       if (AttrReqs & AR_GNUAttributesParsedAndRejected)
         // When GNU attributes are expressly forbidden, diagnose their usage.
index e21409a62fcba0ac0ac9b722a96b08c6bbd9058e..d63cf24bcdb997940949ce98d9879a85b1563d7f 100644 (file)
@@ -631,7 +631,9 @@ Parser::TPResult Parser::TryParsePtrOperatorSeq() {
         (Tok.is(tok::annot_cxxscope) && NextToken().is(tok::star))) {
       // ptr-operator
       ConsumeToken();
-      while (Tok.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict))
+      while (Tok.isOneOf(tok::kw_const, tok::kw_volatile, tok::kw_restrict,
+                         tok::kw___nonnull, tok::kw___nullable,
+                         tok::kw___null_unspecified))
         ConsumeToken();
     } else {
       return TPResult::True;
@@ -1274,6 +1276,9 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
   case tok::kw___ptr32:
   case tok::kw___forceinline:
   case tok::kw___unaligned:
+  case tok::kw___nonnull:
+  case tok::kw___nullable:
+  case tok::kw___null_unspecified:
     return TPResult::True;
 
     // Borland
index 73bdd984c0beb516ed43ed0c501f259eac10a5a1..f4af3f320a8ee1107cee660dd28bad3e0b06c11a 100644 (file)
@@ -2465,6 +2465,28 @@ static void mergeParamDeclAttributes(ParmVarDecl *newDecl,
   if (!foundAny) newDecl->dropAttrs();
 }
 
+static void mergeParamDeclTypes(ParmVarDecl *NewParam,
+                                const ParmVarDecl *OldParam,
+                                Sema &S) {
+  if (auto Oldnullability = OldParam->getType()->getNullability(S.Context)) {
+    if (auto Newnullability = NewParam->getType()->getNullability(S.Context)) {
+      if (*Oldnullability != *Newnullability) {
+        S.Diag(NewParam->getLocation(), diag::warn_mismatched_nullability_attr)
+          << static_cast<unsigned>(*Newnullability)
+          << static_cast<unsigned>(*Oldnullability);
+        S.Diag(OldParam->getLocation(), diag::note_previous_declaration);
+      }
+    }
+    else {
+      QualType NewT = NewParam->getType();
+      NewT = S.Context.getAttributedType(
+                         AttributedType::getNullabilityAttrKind(*Oldnullability),
+                         NewT, NewT);
+      NewParam->setType(NewT);
+    }
+  }
+}
+
 namespace {
 
 /// Used in MergeFunctionDecl to keep track of function parameters in
@@ -3101,9 +3123,12 @@ bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old,
   // Merge attributes from the parameters.  These can mismatch with K&R
   // declarations.
   if (New->getNumParams() == Old->getNumParams())
-    for (unsigned i = 0, e = New->getNumParams(); i != e; ++i)
-      mergeParamDeclAttributes(New->getParamDecl(i), Old->getParamDecl(i),
-                               *this);
+      for (unsigned i = 0, e = New->getNumParams(); i != e; ++i) {
+        ParmVarDecl *NewParam = New->getParamDecl(i);
+        ParmVarDecl *OldParam = Old->getParamDecl(i);
+        mergeParamDeclAttributes(NewParam, OldParam, *this);
+        mergeParamDeclTypes(NewParam, OldParam, *this);
+      }
 
   if (getLangOpts().CPlusPlus)
     return MergeCXXFunctionDecl(New, Old, S);
index d3787ec44b12da4b1a59a66559804076e88aaf91..ea749a46e09b8b854c0cd81a2e32ca2458cbd6ea 100644 (file)
@@ -22,6 +22,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/AST/TypeLocVisitor.h"
+#include "clang/Lex/Preprocessor.h"
 #include "clang/Basic/PartialDiagnostic.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Parse/ParseDiagnostic.h"
@@ -121,6 +122,12 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr,
     case AttributeList::AT_SPtr: \
     case AttributeList::AT_UPtr
 
+// Nullability qualifiers.
+#define NULLABILITY_TYPE_ATTRS_CASELIST         \
+    case AttributeList::AT_TypeNonNull:         \
+    case AttributeList::AT_TypeNullable:        \
+    case AttributeList::AT_TypeNullUnspecified
+
 namespace {
   /// An object which stores processing state for the entire
   /// GetTypeForDeclarator process.
@@ -307,8 +314,12 @@ static bool handleObjCPointerTypeAttr(TypeProcessingState &state,
 ///
 /// \param i - a notional index which the search will start
 ///   immediately inside
+///
+/// \param onlyBlockPointers Whether we should only look into block
+/// pointer types (vs. all pointer types).
 static DeclaratorChunk *maybeMovePastReturnType(Declarator &declarator,
-                                                unsigned i) {
+                                                unsigned i,
+                                                bool onlyBlockPointers) {
   assert(i <= declarator.getNumTypeObjects());
 
   DeclaratorChunk *result = nullptr;
@@ -329,20 +340,26 @@ static DeclaratorChunk *maybeMovePastReturnType(Declarator &declarator,
       return result;
 
     // If we do find a function declarator, scan inwards from that,
-    // looking for a block-pointer declarator.
+    // looking for a (block-)pointer declarator.
     case DeclaratorChunk::Function:
       for (--i; i != 0; --i) {
-        DeclaratorChunk &blockChunk = declarator.getTypeObject(i-1);
-        switch (blockChunk.Kind) {
+        DeclaratorChunk &ptrChunk = declarator.getTypeObject(i-1);
+        switch (ptrChunk.Kind) {
         case DeclaratorChunk::Paren:
-        case DeclaratorChunk::Pointer:
         case DeclaratorChunk::Array:
         case DeclaratorChunk::Function:
         case DeclaratorChunk::Reference:
-        case DeclaratorChunk::MemberPointer:
           continue;
+
+        case DeclaratorChunk::MemberPointer:
+        case DeclaratorChunk::Pointer:
+          if (onlyBlockPointers)
+            continue;
+
+          // fallthrough
+
         case DeclaratorChunk::BlockPointer:
-          result = &blockChunk;
+          result = &ptrChunk;
           goto continue_outer;
         }
         llvm_unreachable("bad declarator chunk kind");
@@ -382,7 +399,8 @@ static void distributeObjCPointerTypeAttr(TypeProcessingState &state,
       DeclaratorChunk *destChunk = nullptr;
       if (state.isProcessingDeclSpec() &&
           attr.getKind() == AttributeList::AT_ObjCOwnership)
-        destChunk = maybeMovePastReturnType(declarator, i - 1);
+        destChunk = maybeMovePastReturnType(declarator, i - 1,
+                                            /*onlyBlockPointers=*/true);
       if (!destChunk) destChunk = &chunk;
 
       moveAttrFromListToList(attr, state.getCurrentAttrListRef(),
@@ -398,7 +416,9 @@ static void distributeObjCPointerTypeAttr(TypeProcessingState &state,
     case DeclaratorChunk::Function:
       if (state.isProcessingDeclSpec() &&
           attr.getKind() == AttributeList::AT_ObjCOwnership) {
-        if (DeclaratorChunk *dest = maybeMovePastReturnType(declarator, i)) {
+        if (DeclaratorChunk *dest = maybeMovePastReturnType(
+                                      declarator, i,
+                                      /*onlyBlockPointers=*/true)) {
           moveAttrFromListToList(attr, state.getCurrentAttrListRef(),
                                  dest->getAttrListRef());
           return;
@@ -620,6 +640,10 @@ static void distributeTypeAttrsFromDeclarator(TypeProcessingState &state,
       // Microsoft type attributes cannot go after the declarator-id.
       continue;
 
+    NULLABILITY_TYPE_ATTRS_CASELIST:
+      // Nullability specifiers cannot go after the declarator-id.
+      continue;
+
     default:
       break;
     }
@@ -3495,6 +3519,12 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) {
     return AttributeList::AT_SPtr;
   case AttributedType::attr_uptr:
     return AttributeList::AT_UPtr;
+  case AttributedType::attr_nonnull:
+    return AttributeList::AT_TypeNonNull;
+  case AttributedType::attr_nullable:
+    return AttributeList::AT_TypeNullable;
+  case AttributedType::attr_null_unspecified:
+    return AttributeList::AT_TypeNullUnspecified;
   }
   llvm_unreachable("unexpected attribute kind!");
 }
@@ -4114,7 +4144,8 @@ static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state,
     // just be the return type of a block pointer.
     if (state.isProcessingDeclSpec()) {
       Declarator &D = state.getDeclarator();
-      if (maybeMovePastReturnType(D, D.getNumTypeObjects()))
+      if (maybeMovePastReturnType(D, D.getNumTypeObjects(),
+                                  /*onlyBlockPointers=*/true))
         return false;
     }
   }
@@ -4491,6 +4522,205 @@ static bool handleMSPointerTypeQualifierAttr(TypeProcessingState &State,
   return false;
 }
 
+/// Map a nullability attribute kind to a nullability kind.
+static NullabilityKind mapNullabilityAttrKind(AttributeList::Kind kind) {
+  switch (kind) {
+  case AttributeList::AT_TypeNonNull:
+    return NullabilityKind::NonNull;
+
+  case AttributeList::AT_TypeNullable:
+    return NullabilityKind::Nullable;
+
+  case AttributeList::AT_TypeNullUnspecified:
+    return NullabilityKind::Unspecified;
+
+  default:
+    llvm_unreachable("not a nullability attribute kind");
+  }
+}
+
+/// Handle a nullability type attribute.
+static bool handleNullabilityTypeAttr(TypeProcessingState &state,
+                                      AttributeList &attr,
+                                      QualType &type) {
+  Sema &S = state.getSema();
+  ASTContext &Context = S.Context;
+
+  // Determine the nullability.
+  AttributeList::Kind kind = attr.getKind();
+  NullabilityKind nullability = mapNullabilityAttrKind(kind);
+
+  // Check for existing nullability attributes on the type.
+  QualType desugared = type;
+  while (auto attributed = dyn_cast<AttributedType>(desugared.getTypePtr())) {
+    // Check whether there is already a null
+    if (auto existingNullability = attributed->getImmediateNullability()) {
+      // Duplicated nullability.
+      if (nullability == *existingNullability) {
+        S.Diag(attr.getLoc(), diag::warn_duplicate_nullability)
+          << static_cast<unsigned>(nullability);
+        return true;
+      }
+
+      // Conflicting nullability.
+      S.Diag(attr.getLoc(), diag::err_nullability_conflicting)
+        <<  static_cast<unsigned>(nullability) 
+        << static_cast<unsigned>(*existingNullability);
+      return true;
+    }
+
+    desugared = attributed->getEquivalentType();
+  }
+
+  // If there is already a different nullability specifier, complain.
+  // This (unlike the code above) looks through typedefs that might
+  // have nullability specifiers on them, which means we cannot
+  // provide a useful Fix-It.
+  if (auto existingNullability = desugared->getNullability(Context)) {
+    if (nullability != *existingNullability) {
+      S.Diag(attr.getLoc(), diag::err_nullability_conflicting)
+        << static_cast<unsigned>(nullability)
+        << static_cast<unsigned>(*existingNullability);
+
+      // Try to find the typedef with the existing nullability specifier.
+      if (auto typedefType = desugared->getAs<TypedefType>()) {
+        TypedefNameDecl *typedefDecl = typedefType->getDecl();
+        QualType underlyingType = typedefDecl->getUnderlyingType();
+        if (auto typedefNullability
+              = AttributedType::stripOuterNullability(underlyingType)) {
+          if (*typedefNullability == *existingNullability) {
+            S.Diag(typedefDecl->getLocation(), diag::note_nullability_here)
+              << static_cast<unsigned>(*existingNullability);
+          }
+        }
+      }
+
+      return true;
+    }
+  }
+
+  // If this definitely isn't a pointer type, reject the specifier.
+  if (!type->canHaveNullability()) {
+    S.Diag(attr.getLoc(), diag::err_nullability_nonpointer)
+      << static_cast<unsigned>(nullability) << type;
+    return true;
+  }
+
+  // Form the attributed type.
+  AttributedType::Kind typeAttrKind;
+  switch (kind) {
+  case AttributeList::AT_TypeNonNull: 
+    typeAttrKind = AttributedType::attr_nonnull; 
+    break;
+
+  case AttributeList::AT_TypeNullable: 
+    typeAttrKind = AttributedType::attr_nullable; 
+    break;
+
+  case AttributeList::AT_TypeNullUnspecified: 
+    typeAttrKind = AttributedType::attr_null_unspecified; 
+    break;
+
+  default:
+    llvm_unreachable("Not a nullability specifier");
+  }
+  type = S.Context.getAttributedType(typeAttrKind, type, type);
+  return false;
+}
+
+/// Check whether there is a nullability attribute of any kind in the given
+/// attribute list.
+static bool hasNullabilityAttr(const AttributeList *attrs) {
+  for (const AttributeList *attr = attrs; attr;
+       attr = attr->getNext()) {
+    if (attr->getKind() == AttributeList::AT_TypeNonNull ||
+        attr->getKind() == AttributeList::AT_TypeNullable ||
+        attr->getKind() == AttributeList::AT_TypeNullUnspecified)
+      return true;
+  }
+
+  return false;
+}
+
+/// Distribute a nullability type attribute that cannot be applied to
+/// the type specifier to a pointer, block pointer, or member pointer
+/// declarator, complaining if necessary.
+///
+/// \returns true if the nullability annotation was distributed, false
+/// otherwise.
+static bool distributeNullabilityTypeAttr(TypeProcessingState &state,
+                                          QualType type,
+                                          AttributeList &attr) {
+  Declarator &declarator = state.getDeclarator();
+
+  /// Attempt to move the attribute to the specified chunk.
+  auto moveToChunk = [&](DeclaratorChunk &chunk, bool inFunction) -> bool {
+    // If there is already a nullability attribute there, don't add
+    // one.
+    if (hasNullabilityAttr(chunk.getAttrListRef()))
+      return false;
+
+    // Complain about the nullability qualifier being in the wrong
+    // place.
+    unsigned pointerKind
+      = chunk.Kind == DeclaratorChunk::Pointer ? (inFunction ? 3 : 0)
+        : chunk.Kind == DeclaratorChunk::BlockPointer ? 1
+        : inFunction? 4 : 2;
+
+    auto diag = state.getSema().Diag(attr.getLoc(),
+                                     diag::warn_nullability_declspec)
+      << static_cast<unsigned>(mapNullabilityAttrKind(attr.getKind()))
+      << type
+      << pointerKind;
+
+    // FIXME: MemberPointer chunks don't carry the location of the *.
+    if (chunk.Kind != DeclaratorChunk::MemberPointer) {
+      diag << FixItHint::CreateRemoval(attr.getLoc())
+           << FixItHint::CreateInsertion(
+                state.getSema().getPreprocessor()
+                  .getLocForEndOfToken(chunk.Loc),
+                " " + attr.getName()->getName().str() + " ");
+    }
+
+    moveAttrFromListToList(attr, state.getCurrentAttrListRef(),
+                           chunk.getAttrListRef());
+    return true;
+  };
+
+  // Move it to the outermost pointer, member pointer, or block
+  // pointer declarator.
+  for (unsigned i = state.getCurrentChunkIndex(); i != 0; --i) {
+    DeclaratorChunk &chunk = declarator.getTypeObject(i-1);
+    switch (chunk.Kind) {
+    case DeclaratorChunk::Pointer:
+    case DeclaratorChunk::BlockPointer:
+    case DeclaratorChunk::MemberPointer:
+      return moveToChunk(chunk, false);
+
+    case DeclaratorChunk::Paren:
+    case DeclaratorChunk::Array:
+      continue;
+
+    case DeclaratorChunk::Function:
+      // Try to move past the return type to a function/block/member
+      // function pointer.
+      if (DeclaratorChunk *dest = maybeMovePastReturnType(
+                                    declarator, i,
+                                    /*onlyBlockPointers=*/false)) {
+        return moveToChunk(*dest, true);
+      }
+
+      return false;
+      
+    // Don't walk through these.
+    case DeclaratorChunk::Reference:
+      return false;
+    }
+  }
+
+  return false;
+}
+
 static AttributedType::Kind getCCTypeAttrKind(AttributeList &Attr) {
   assert(!Attr.isInvalid());
   switch (Attr.getKind()) {
@@ -4997,6 +5227,20 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
         attr.setUsedAsTypeAttr();
       break;
 
+    NULLABILITY_TYPE_ATTRS_CASELIST:
+      // Either add nullability here or try to distribute it.  We
+      // don't want to distribute the nullability specifier past any
+      // dependent type, because that complicates the user model.
+      if (type->canHaveNullability() || type->isDependentType() ||
+          !distributeNullabilityTypeAttr(state, type, attr)) {
+        if (handleNullabilityTypeAttr(state, attr, type)) {
+          attr.setInvalid();
+        }
+
+        attr.setUsedAsTypeAttr();
+      }
+      break;
+
     case AttributeList::AT_NSReturnsRetained:
       if (!state.getSema().getLangOpts().ObjCAutoRefCount)
         break;
index 878300ebc9f81208f1ad15ff8153bdd95bf34ba0..c4cd22609b7a3d6ffc58b54779aea3cc6034a216 100644 (file)
@@ -5386,6 +5386,17 @@ QualType TreeTransform<Derived>::TransformAttributedType(
       = getDerived().TransformType(oldType->getEquivalentType());
     if (equivalentType.isNull())
       return QualType();
+
+    // Check whether we can add nullability; it is only represented as
+    // type sugar, and therefore cannot be diagnosed in any other way.
+    if (auto nullability = oldType->getImmediateNullability()) {
+      if (!modifiedType->canHaveNullability()) {
+        SemaRef.Diag(TL.getAttrNameLoc(), diag::err_nullability_nonpointer)
+          << static_cast<unsigned>(*nullability) << modifiedType;
+        return QualType();
+      }
+    }
+
     result = SemaRef.Context.getAttributedType(oldType->getAttrKind(),
                                                modifiedType,
                                                equivalentType);
diff --git a/test/FixIt/fixit-nullability-declspec.cpp b/test/FixIt/fixit-nullability-declspec.cpp
new file mode 100644 (file)
index 0000000..2ac20b9
--- /dev/null
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -fblocks -Werror=nullability-declspec -x c++ -verify %s
+
+// RUN: cp %s %t
+// RUN: not %clang_cc1 -fixit -fblocks -Werror=nullability-declspec -x c++ %t
+// RUN: %clang_cc1 -fblocks -Werror=nullability-declspec -x c++ %t
+
+__nullable int *ip1; // expected-error{{nullability specifier '__nullable' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the pointer?}}
+__nullable int (*fp1)(int); // expected-error{{nullability specifier '__nullable' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the function pointer?}}
+__nonnull int (^bp1)(int); // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the block pointer?}}
diff --git a/test/Parser/nullability.c b/test/Parser/nullability.c
new file mode 100644 (file)
index 0000000..f2b6abf
--- /dev/null
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -fsyntax-only -std=c99 -Wno-nullability-declspec -pedantic %s -verify
+
+__nonnull int *ptr; // expected-warning{{type nullability specifier '__nonnull' is a Clang extension}}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnullability-extension"
+__nonnull int *ptr2; // no-warning
+#pragma clang diagnostic pop
+
+#if __has_feature(nullability)
+#  error Nullability should not be supported in C under -pedantic -std=c99
+#endif
+
+#if !__has_extension(nullability)
+#  error Nullability should always be supported as an extension
+#endif
diff --git a/test/Sema/non-null-warning.c b/test/Sema/non-null-warning.c
new file mode 100644 (file)
index 0000000..0cabc71
--- /dev/null
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -fsyntax-only -Wnonnull -Wnullability %s -verify
+// rdar://19160762
+
+#if __has_feature(nullability)
+#else
+#  error nullability feature should be defined
+#endif
+
+
+int * __nullable foo(int * __nonnull x);
+
+int *__nonnull ret_nonnull();
+
+int *foo(int *x) {
+  return 0;
+}
+
+int * __nullable foo1(int * __nonnull x); // expected-note {{previous declaration is here}}
+
+int *foo1(int * __nullable x) { // expected-warning {{nullability specifier '__nullable' conflicts with existing specifier '__nonnull'}}
+  return 0;
+}
+
+int * __nullable foo2(int * __nonnull x);
+
+int *foo2(int * __nonnull x) {
+  return 0;
+}
+
+int * __nullable foo3(int * __nullable x); // expected-note {{previous declaration is here}}
+
+int *foo3(int * __nonnull x) { // expected-warning {{nullability specifier '__nonnull' conflicts with existing specifier '__nullable'}}
+  return 0;
+}
+
diff --git a/test/Sema/nullability.c b/test/Sema/nullability.c
new file mode 100644 (file)
index 0000000..bf35b4c
--- /dev/null
@@ -0,0 +1,87 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -Wno-nullability-declspec %s -verify
+
+#if __has_feature(nullability)
+#else
+#  error nullability feature should be defined
+#endif
+
+typedef int * int_ptr;
+
+// Parse nullability type specifiers.
+typedef int * __nonnull nonnull_int_ptr; // expected-note{{'__nonnull' specified here}}
+typedef int * __nullable nullable_int_ptr;
+typedef int * __null_unspecified null_unspecified_int_ptr;
+
+// Redundant nullability type specifiers.
+typedef int * __nonnull __nonnull redundant_1; // expected-warning{{duplicate nullability specifier '__nonnull'}}
+
+// Conflicting nullability type specifiers.
+typedef int * __nonnull __nullable conflicting_1; // expected-error{{nullability specifier '__nonnull' conflicts with existing specifier '__nullable'}}
+typedef int * __null_unspecified __nonnull conflicting_2; // expected-error{{nullability specifier '__null_unspecified' conflicts with existing specifier '__nonnull'}}
+
+// Redundant nullability specifiers via a typedef are okay.
+typedef nonnull_int_ptr __nonnull redundant_okay_1;
+
+// Conflicting nullability specifiers via a typedef are not.
+typedef nonnull_int_ptr __nullable conflicting_2; // expected-error{{nullability specifier '__nullable' conflicts with existing specifier '__nonnull'}}
+typedef nonnull_int_ptr nonnull_int_ptr_typedef;
+typedef nonnull_int_ptr_typedef __nullable conflicting_2; // expected-error{{nullability specifier '__nullable' conflicts with existing specifier '__nonnull'}}
+typedef nonnull_int_ptr_typedef nonnull_int_ptr_typedef_typedef;
+typedef nonnull_int_ptr_typedef_typedef __null_unspecified conflicting_3; // expected-error{{nullability specifier '__null_unspecified' conflicts with existing specifier '__nonnull'}}
+
+// Nullability applies to all pointer types.
+typedef int (* __nonnull function_pointer_type_1)(int, int);
+typedef int (^ __nonnull block_type_1)(int, int);
+
+// Nullability must be on a pointer type.
+typedef int __nonnull int_type_1; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'}}
+
+// Nullability can move out to a pointer/block pointer declarator
+// (with a suppressed warning).
+typedef __nonnull int * nonnull_int_ptr_2;
+typedef int __nullable * nullable_int_ptr_2;
+typedef __nonnull int (* function_pointer_type_2)(int, int);
+typedef __nonnull int (^ block_type_2)(int, int);
+typedef __nonnull int * * __nullable nonnull_int_ptr_ptr_1;
+typedef __nonnull int *(^ block_type_3)(int, int);
+typedef __nonnull int *(* function_pointer_type_3)(int, int);
+typedef __nonnull int_ptr (^ block_type_4)(int, int);
+typedef __nonnull int_ptr (* function_pointer_type_4)(int, int);
+
+void acceptFunctionPtr(__nonnull int *(*)(void));
+void acceptBlockPtr(__nonnull int *(^)(void));
+
+void testBlockFunctionPtrNullability() {
+  float *fp;
+  fp = (function_pointer_type_3)0; // expected-warning{{from 'function_pointer_type_3' (aka 'int * __nonnull (*)(int, int)')}}
+  fp = (block_type_3)0; // expected-error{{from incompatible type 'block_type_3' (aka 'int * __nonnull (^)(int, int)')}}
+  fp = (function_pointer_type_4)0; // expected-warning{{from 'function_pointer_type_4' (aka 'int_ptr  __nonnull (*)(int, int)')}}
+  fp = (block_type_4)0; // expected-error{{from incompatible type 'block_type_4' (aka 'int_ptr  __nonnull (^)(int, int)')}}
+
+  acceptFunctionPtr(0); // no-warning
+  acceptBlockPtr(0); // no-warning
+}
+
+// Moving nullability where it creates a conflict.
+typedef __nonnull int * __nullable *  conflict_int_ptr_ptr_2; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'}}
+
+// Nullability is not part of the canonical type.
+typedef int * __nonnull ambiguous_int_ptr;
+typedef int * ambiguous_int_ptr;
+typedef int * __nullable ambiguous_int_ptr;
+
+// Printing of nullability.
+float f;
+int * __nonnull ip_1 = &f; // expected-warning{{incompatible pointer types initializing 'int * __nonnull' with an expression of type 'float *'}}
+
+// Check printing of nullability specifiers.
+void printing_nullability(void) {
+  int * __nonnull iptr;
+  float *fptr = iptr; // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'int * __nonnull'}}
+
+  int * * __nonnull iptrptr;
+  float **fptrptr = iptrptr; // expected-warning{{incompatible pointer types initializing 'float **' with an expression of type 'int ** __nonnull'}}
+
+  int * __nullable * __nonnull iptrptr2;
+  float * *fptrptr2 = iptrptr2; // expected-warning{{incompatible pointer types initializing 'float **' with an expression of type 'int * __nullable * __nonnull'}}
+}
diff --git a/test/SemaCXX/nullability-declspec.cpp b/test/SemaCXX/nullability-declspec.cpp
new file mode 100644 (file)
index 0000000..ef1a171
--- /dev/null
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -fblocks -Werror=nullability-declspec -verify %s
+
+struct X { };
+
+__nullable int *ip1; // expected-error{{nullability specifier '__nullable' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the pointer?}}
+__nullable int (*fp1)(int); // expected-error{{nullability specifier '__nullable' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the function pointer?}}
+__nonnull int (^bp1)(int); // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the block pointer?}}
+__nonnull int X::*pmd1; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the member pointer?}}
+__nonnull int (X::*pmf1)(int); // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'; did you mean to apply the specifier to the member function pointer?}}
diff --git a/test/SemaCXX/nullability.cpp b/test/SemaCXX/nullability.cpp
new file mode 100644 (file)
index 0000000..391675f
--- /dev/null
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -Wno-nullability-declspec %s -verify
+
+typedef decltype(nullptr) nullptr_t;
+
+class X {
+};
+
+// Nullability applies to all pointer types.
+typedef int (X::* __nonnull member_function_type_1)(int);
+typedef int X::* __nonnull member_data_type_1;
+typedef nullptr_t __nonnull nonnull_nullptr_t; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'nullptr_t'}}
+
+// Nullability can move into member pointers (this is suppressing a warning).
+typedef __nonnull int (X::* member_function_type_2)(int);
+typedef int (X::* __nonnull member_function_type_3)(int);
+typedef __nonnull int X::* member_data_type_2;
+
+// Adding non-null via a template.
+template<typename T>
+struct AddNonNull {
+  typedef __nonnull T type; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'int'}}
+  // expected-error@-1{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'nullptr_t'}}
+};
+
+typedef AddNonNull<int *>::type nonnull_int_ptr_1;
+typedef AddNonNull<int * __nullable>::type nonnull_int_ptr_2; // FIXME: check that it was overridden
+typedef AddNonNull<nullptr_t>::type nonnull_int_ptr_3; // expected-note{{in instantiation of template class}}
+
+typedef AddNonNull<int>::type nonnull_non_pointer_1; // expected-note{{in instantiation of template class 'AddNonNull<int>' requested here}}
+
+// Non-null checking within a template.
+template<typename T>
+struct AddNonNull2 {
+  typedef __nonnull AddNonNull<T> invalid1; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'AddNonNull<T>'}}
+  typedef __nonnull AddNonNull2 invalid2; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'AddNonNull2<T>'}}
+  typedef __nonnull AddNonNull2<T> invalid3; // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'AddNonNull2<T>'}}
+  typedef __nonnull typename AddNonNull<T>::type okay1;
+
+  // Don't move past a dependent type even if we know that nullability
+  // cannot apply to that specific dependent type.
+  typedef __nonnull AddNonNull<T> (*invalid4); // expected-error{{nullability specifier '__nonnull' cannot be applied to non-pointer type 'AddNonNull<T>'}}
+};
diff --git a/test/SemaObjC/nullability.m b/test/SemaObjC/nullability.m
new file mode 100644 (file)
index 0000000..a93072f
--- /dev/null
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -fsyntax-only -fblocks -Woverriding-method-mismatch -Wno-nullability-declspec %s -verify
+
+@interface NSFoo
+@end
+
+// Nullability applies to all pointer types.
+typedef NSFoo * __nonnull nonnull_NSFoo_ptr;
+typedef id __nonnull nonnull_id;
+typedef SEL __nonnull nonnull_SEL;
+
+// Nullability can move into Objective-C pointer types.
+typedef __nonnull NSFoo * nonnull_NSFoo_ptr_2;
+
+// Conflicts from nullability moving into Objective-C pointer type.
+typedef __nonnull NSFoo * __nullable conflict_NSFoo_ptr_2; // expected-error{{'__nonnull' cannot be applied to non-pointer type 'NSFoo'}}
+
+void testBlocksPrinting(NSFoo * __nullable (^bp)(int)) {
+  int *ip = bp; // expected-error{{'NSFoo * __nullable (^)(int)'}}
+}