r286519 - Accept nullability qualifiers on array parameters.

Jordan Rose via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 10 15:28:18 PST 2016


Author: jrose
Date: Thu Nov 10 17:28:17 2016
New Revision: 286519

URL: http://llvm.org/viewvc/llvm-project?rev=286519&view=rev
Log:
Accept nullability qualifiers on array parameters.

Since array parameters decay to pointers, '_Nullable' and friends
should be available for use there as well. This is especially
important for parameters that are typedefs of arrays. The unsugared
syntax for this follows the syntax for 'static'-sized arrays in C:

  void test(int values[_Nullable]);

This syntax was previously accepted but the '_Nullable' (and any other
attributes) were silently discarded. However, applying '_Nullable' to
a typedef was previously rejected and is now accepted; therefore, it
may be necessary to test for the presence of this feature:

  #if __has_feature(nullability_on_arrays)

One important change here is that DecayedTypes don't always
immediately contain PointerTypes anymore; they may contain an
AttributedType instead. This only affected one place in-tree, so I
would guess it's not likely to cause problems elsewhere.

This commit does not change -Wnullability-completeness just yet. I
want to think about whether it's worth doing something special to
avoid breaking existing clients that compile with -Werror. It also
doesn't change '#pragma clang assume_nonnull' behavior, which
currently treats the following two declarations as equivalent:

  #pragma clang assume_nonnull begin
  void test(void *pointers[]);
  #pragma clang assume_nonnull end

  void test(void * _Nonnull pointers[]);

This is not the desired behavior, but changing it would break
backwards-compatibility. Most likely the best answer is going to be
adding a new warning.

Part of rdar://problem/25846421

Modified:
    cfe/trunk/include/clang/AST/Type.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/AST/ASTContext.cpp
    cfe/trunk/lib/CodeGen/CGDebugInfo.cpp
    cfe/trunk/lib/Lex/PPMacroExpansion.cpp
    cfe/trunk/lib/Parse/ParseDecl.cpp
    cfe/trunk/lib/Sema/SemaType.cpp
    cfe/trunk/test/Parser/nullability.c
    cfe/trunk/test/Sema/nullability.c
    cfe/trunk/test/SemaCXX/nullability.cpp
    cfe/trunk/test/SemaObjC/nullability.m

Modified: cfe/trunk/include/clang/AST/Type.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Type.h?rev=286519&r1=286518&r2=286519&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Type.h (original)
+++ cfe/trunk/include/clang/AST/Type.h Thu Nov 10 17:28:17 2016
@@ -2266,19 +2266,15 @@ public:
 /// Represents a pointer type decayed from an array or function type.
 class DecayedType : public AdjustedType {
 
-  DecayedType(QualType OriginalType, QualType DecayedPtr, QualType CanonicalPtr)
-      : AdjustedType(Decayed, OriginalType, DecayedPtr, CanonicalPtr) {
-    assert(isa<PointerType>(getAdjustedType()));
-  }
+  inline
+  DecayedType(QualType OriginalType, QualType Decayed, QualType Canonical);
 
   friend class ASTContext;  // ASTContext creates these.
 
 public:
   QualType getDecayedType() const { return getAdjustedType(); }
 
-  QualType getPointeeType() const {
-    return cast<PointerType>(getDecayedType())->getPointeeType();
-  }
+  inline QualType getPointeeType() const;
 
   static bool classof(const Type *T) { return T->getTypeClass() == Decayed; }
 };
@@ -5962,6 +5958,23 @@ inline const ArrayType *Type::castAsArra
   return cast<ArrayType>(getUnqualifiedDesugaredType());
 }
 
+DecayedType::DecayedType(QualType OriginalType, QualType DecayedPtr,
+                         QualType CanonicalPtr)
+    : AdjustedType(Decayed, OriginalType, DecayedPtr, CanonicalPtr) {
+#ifndef NDEBUG
+  QualType Adjusted = getAdjustedType();
+  (void)AttributedType::stripOuterNullability(Adjusted);
+  assert(isa<PointerType>(Adjusted));
+#endif
+}
+
+QualType DecayedType::getPointeeType() const {
+  QualType Decayed = getDecayedType();
+  (void)AttributedType::stripOuterNullability(Decayed);
+  return cast<PointerType>(Decayed)->getPointeeType();
+}
+
+
 }  // end namespace clang
 
 #endif

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=286519&r1=286518&r2=286519&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Thu Nov 10 17:28:17 2016
@@ -3122,10 +3122,14 @@ public:
   /// method) or an Objective-C property attribute, rather than as an
   /// underscored type specifier.
   ///
+  /// \param allowArrayTypes Whether to accept nullability specifiers on an
+  /// array type (e.g., because it will decay to a pointer).
+  ///
   /// \returns true if nullability cannot be applied, false otherwise.
   bool checkNullabilityTypeSpecifier(QualType &type, NullabilityKind nullability,
                                      SourceLocation nullabilityLoc,
-                                     bool isContextSensitive);
+                                     bool isContextSensitive,
+                                     bool allowArrayTypes);
 
   /// \brief Stmt attributes - this routine is the top level dispatcher.
   StmtResult ProcessStmtAttributes(Stmt *Stmt, AttributeList *Attrs,

Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=286519&r1=286518&r2=286519&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Thu Nov 10 17:28:17 2016
@@ -4913,7 +4913,15 @@ QualType ASTContext::getArrayDecayedType
   QualType PtrTy = getPointerType(PrettyArrayType->getElementType());
 
   // int x[restrict 4] ->  int *restrict
-  return getQualifiedType(PtrTy, PrettyArrayType->getIndexTypeQualifiers());
+  QualType Result = getQualifiedType(PtrTy,
+                                     PrettyArrayType->getIndexTypeQualifiers());
+
+  // int x[_Nullable] -> int * _Nullable
+  if (auto Nullability = Ty->getNullability(*this)) {
+    Result = const_cast<ASTContext *>(this)->getAttributedType(
+        AttributedType::getNullabilityAttrKind(*Nullability), Result, Result);
+  }
+  return Result;
 }
 
 QualType ASTContext::getBaseElementType(const ArrayType *array) const {

Modified: cfe/trunk/lib/CodeGen/CGDebugInfo.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDebugInfo.cpp?rev=286519&r1=286518&r2=286519&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGDebugInfo.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGDebugInfo.cpp Thu Nov 10 17:28:17 2016
@@ -2416,12 +2416,18 @@ static QualType UnwrapTypeForDebugInfo(Q
     case Type::SubstTemplateTypeParm:
       T = cast<SubstTemplateTypeParmType>(T)->getReplacementType();
       break;
-    case Type::Auto:
+    case Type::Auto: {
       QualType DT = cast<AutoType>(T)->getDeducedType();
       assert(!DT.isNull() && "Undeduced types shouldn't reach here.");
       T = DT;
       break;
     }
+    case Type::Adjusted:
+    case Type::Decayed:
+      // Decayed and adjusted types use the adjusted type in LLVM and DWARF.
+      T = cast<AdjustedType>(T)->getAdjustedType();
+      break;
+    }
 
     assert(T != LastT && "Type unwrapping failed to unwrap!");
     (void)LastT;
@@ -2540,11 +2546,6 @@ llvm::DIType *CGDebugInfo::CreateTypeNod
     return CreateType(cast<ComplexType>(Ty));
   case Type::Pointer:
     return CreateType(cast<PointerType>(Ty), Unit);
-  case Type::Adjusted:
-  case Type::Decayed:
-    // Decayed and adjusted types use the adjusted type in LLVM and DWARF.
-    return CreateType(
-        cast<PointerType>(cast<AdjustedType>(Ty)->getAdjustedType()), Unit);
   case Type::BlockPointer:
     return CreateType(cast<BlockPointerType>(Ty), Unit);
   case Type::Typedef:
@@ -2580,6 +2581,8 @@ llvm::DIType *CGDebugInfo::CreateTypeNod
 
   case Type::Auto:
   case Type::Attributed:
+  case Type::Adjusted:
+  case Type::Decayed:
   case Type::Elaborated:
   case Type::Paren:
   case Type::SubstTemplateTypeParm:

Modified: cfe/trunk/lib/Lex/PPMacroExpansion.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PPMacroExpansion.cpp?rev=286519&r1=286518&r2=286519&view=diff
==============================================================================
--- cfe/trunk/lib/Lex/PPMacroExpansion.cpp (original)
+++ cfe/trunk/lib/Lex/PPMacroExpansion.cpp Thu Nov 10 17:28:17 2016
@@ -1108,6 +1108,7 @@ static bool HasFeature(const Preprocesso
       .Case("cxx_rtti", LangOpts.RTTI && LangOpts.RTTIData)
       .Case("enumerator_attributes", true)
       .Case("nullability", true)
+      .Case("nullability_on_arrays", true)
       .Case("memory_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Memory))
       .Case("thread_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Thread))
       .Case("dataflow_sanitizer", LangOpts.Sanitize.has(SanitizerKind::DataFlow))

Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=286519&r1=286518&r2=286519&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Thu Nov 10 17:28:17 2016
@@ -6250,8 +6250,7 @@ void Parser::ParseBracketDeclarator(Decl
 
   T.consumeClose();
 
-  ParsedAttributes attrs(AttrFactory);
-  MaybeParseCXX11Attributes(attrs);
+  MaybeParseCXX11Attributes(DS.getAttributes());
 
   // Remember that we parsed a array type, and remember its features.
   D.AddTypeInfo(DeclaratorChunk::getArray(DS.getTypeQualifiers(),
@@ -6259,7 +6258,7 @@ void Parser::ParseBracketDeclarator(Decl
                                           NumElements.get(),
                                           T.getOpenLocation(),
                                           T.getCloseLocation()),
-                attrs, T.getCloseLocation());
+                DS.getAttributes(), T.getCloseLocation());
 }
 
 /// Diagnose brackets before an identifier.

Modified: cfe/trunk/lib/Sema/SemaType.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=286519&r1=286518&r2=286519&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaType.cpp (original)
+++ cfe/trunk/lib/Sema/SemaType.cpp Thu Nov 10 17:28:17 2016
@@ -3484,6 +3484,39 @@ static void checkNullabilityConsistency(
     << static_cast<unsigned>(pointerKind);
 }
 
+/// Returns true if any of the declarator chunks before \p endIndex include a
+/// level of indirection: array, pointer, reference, or pointer-to-member.
+///
+/// Because declarator chunks are stored in outer-to-inner order, testing
+/// every chunk before \p endIndex is testing all chunks that embed the current
+/// chunk as part of their type.
+///
+/// It is legal to pass the result of Declarator::getNumTypeObjects() as the
+/// end index, in which case all chunks are tested.
+static bool hasOuterPointerLikeChunk(const Declarator &D, unsigned endIndex) {
+  unsigned i = endIndex;
+  while (i != 0) {
+    // Walk outwards along the declarator chunks.
+    --i;
+    const DeclaratorChunk &DC = D.getTypeObject(i);
+    switch (DC.Kind) {
+    case DeclaratorChunk::Paren:
+      break;
+    case DeclaratorChunk::Array:
+    case DeclaratorChunk::Pointer:
+    case DeclaratorChunk::Reference:
+    case DeclaratorChunk::MemberPointer:
+      return true;
+    case DeclaratorChunk::Function:
+    case DeclaratorChunk::BlockPointer:
+    case DeclaratorChunk::Pipe:
+      // These are invalid anyway, so just ignore.
+      break;
+    }
+  }
+  return false;
+}
+
 static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
                                                 QualType declSpecType,
                                                 TypeSourceInfo *TInfo) {
@@ -3935,31 +3968,13 @@ static TypeSourceInfo *GetFullTypeForDec
 
         // C99 6.7.5.2p1: ... and then only in the outermost array type
         // derivation.
-        unsigned x = chunkIndex;
-        while (x != 0) {
-          // Walk outwards along the declarator chunks.
-          x--;
-          const DeclaratorChunk &DC = D.getTypeObject(x);
-          switch (DC.Kind) {
-          case DeclaratorChunk::Paren:
-            continue;
-          case DeclaratorChunk::Array:
-          case DeclaratorChunk::Pointer:
-          case DeclaratorChunk::Reference:
-          case DeclaratorChunk::MemberPointer:
-            S.Diag(DeclType.Loc, diag::err_array_static_not_outermost) <<
-              (ASM == ArrayType::Static ? "'static'" : "type qualifier");
-            if (ASM == ArrayType::Static)
-              ASM = ArrayType::Normal;
-            ATI.TypeQuals = 0;
-            D.setInvalidType(true);
-            break;
-          case DeclaratorChunk::Function:
-          case DeclaratorChunk::BlockPointer:
-          case DeclaratorChunk::Pipe:
-            // These are invalid anyway, so just ignore.
-            break;
-          }
+        if (hasOuterPointerLikeChunk(D, chunkIndex)) {
+          S.Diag(DeclType.Loc, diag::err_array_static_not_outermost) <<
+            (ASM == ArrayType::Static ? "'static'" : "type qualifier");
+          if (ASM == ArrayType::Static)
+            ASM = ArrayType::Normal;
+          ATI.TypeQuals = 0;
+          D.setInvalidType(true);
         }
       }
       const AutoType *AT = T->getContainedAutoType();
@@ -5834,7 +5849,8 @@ static bool handleMSPointerTypeQualifier
 bool Sema::checkNullabilityTypeSpecifier(QualType &type,
                                          NullabilityKind nullability,
                                          SourceLocation nullabilityLoc,
-                                         bool isContextSensitive) {
+                                         bool isContextSensitive,
+                                         bool allowOnArrayType) {
   // We saw a nullability type specifier. If this is the first one for
   // this file, note that.
   FileID file = getNullabilityCompletenessCheckFileID(*this, nullabilityLoc);
@@ -5904,7 +5920,8 @@ bool Sema::checkNullabilityTypeSpecifier
   }
 
   // If this definitely isn't a pointer type, reject the specifier.
-  if (!desugared->canHaveNullability()) {
+  if (!desugared->canHaveNullability() &&
+      !(allowOnArrayType && desugared->isArrayType())) {
     Diag(nullabilityLoc, diag::err_nullability_nonpointer)
       << DiagNullabilityKind(nullability, isContextSensitive) << type;
     return true;
@@ -5913,7 +5930,13 @@ bool Sema::checkNullabilityTypeSpecifier
   // For the context-sensitive keywords/Objective-C property
   // attributes, require that the type be a single-level pointer.
   if (isContextSensitive) {
-    QualType pointeeType = desugared->getPointeeType();
+    // Make sure that the pointee isn't itself a pointer type.
+    const Type *pointeeType;
+    if (desugared->isArrayType())
+      pointeeType = desugared->getArrayElementTypeNoTypeQual();
+    else
+      pointeeType = desugared->getPointeeType().getTypePtr();
+
     if (pointeeType->isAnyPointerType() ||
         pointeeType->isObjCObjectPointerType() ||
         pointeeType->isMemberPointerType()) {
@@ -6668,12 +6691,22 @@ static void processTypeAttrs(TypeProcess
       // don't want to distribute the nullability specifier past any
       // dependent type, because that complicates the user model.
       if (type->canHaveNullability() || type->isDependentType() ||
+          type->isArrayType() ||
           !distributeNullabilityTypeAttr(state, type, attr)) {
+        unsigned endIndex;
+        if (TAL == TAL_DeclChunk)
+          endIndex = state.getCurrentChunkIndex();
+        else
+          endIndex = state.getDeclarator().getNumTypeObjects();
+        bool allowOnArrayType =
+            state.getDeclarator().isPrototypeContext() &&
+            !hasOuterPointerLikeChunk(state.getDeclarator(), endIndex);
         if (state.getSema().checkNullabilityTypeSpecifier(
               type,
               mapNullabilityAttrKind(attr.getKind()),
               attr.getLoc(),
-              attr.isContextSensitiveKeywordAttribute())) {
+              attr.isContextSensitiveKeywordAttribute(),
+              allowOnArrayType)) {
           attr.setInvalid();
         }
 

Modified: cfe/trunk/test/Parser/nullability.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/nullability.c?rev=286519&r1=286518&r2=286519&view=diff
==============================================================================
--- cfe/trunk/test/Parser/nullability.c (original)
+++ cfe/trunk/test/Parser/nullability.c Thu Nov 10 17:28:17 2016
@@ -11,6 +11,14 @@ _Nonnull int *ptr2; // no-warning
 #  error Nullability should always be supported
 #endif
 
+#if !__has_feature(nullability_on_arrays)
+#  error Nullability on array parameters should always be supported
+#endif
+
 #if !__has_extension(nullability)
 #  error Nullability should always be supported as an extension
 #endif
+
+#if !__has_extension(nullability_on_arrays)
+#  error Nullability on array parameters should always be supported as an extension
+#endif

Modified: cfe/trunk/test/Sema/nullability.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/nullability.c?rev=286519&r1=286518&r2=286519&view=diff
==============================================================================
--- cfe/trunk/test/Sema/nullability.c (original)
+++ cfe/trunk/test/Sema/nullability.c Thu Nov 10 17:28:17 2016
@@ -195,3 +195,55 @@ void binary_conditional_expr() {
   p = noneP ?: unspecifiedP;
   p = noneP ?: noneP;
 }
+
+extern int GLOBAL_LENGTH;
+
+// Nullability can appear on arrays when the arrays are in parameter lists.
+void arrays(int ints[_Nonnull],
+            void *ptrs[_Nullable],
+            void **nestedPtrs[_Nullable],
+            void * _Null_unspecified * _Nonnull nestedPtrs2[_Nullable],
+            int fixedSize[_Nonnull 2],
+            int staticSize[_Nonnull static 2],
+            int staticSize2[static _Nonnull 2],
+            int starSize[_Nonnull *],
+            int vla[_Nonnull GLOBAL_LENGTH],
+            void ** _Nullable reference);
+void testDecayedType() {
+  int produceAnErrorMessage = arrays; // expected-warning {{incompatible pointer to integer conversion initializing 'int' with an expression of type 'void (int * _Nonnull, void ** _Nullable, void *** _Nullable, void * _Null_unspecified * _Nonnull * _Nullable, int * _Nonnull, int * _Nonnull, int * _Nonnull, int * _Nonnull, int * _Nonnull, void ** _Nullable)'}}
+}
+
+int notInFunction[_Nullable 3]; // expected-error {{nullability specifier '_Nullable' cannot be applied to non-pointer type 'int [3]'}}
+
+void nestedArrays(int x[5][_Nonnull 1]) {} // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int [1]'}}
+void nestedArrays2(int x[5][_Nonnull 1][2]) {} // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int [1][2]'}}
+void nestedArraysOK(int x[_Nonnull 5][1]) {} // ok
+
+void nullabilityOnBase(_Nonnull int x[1], // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int'}}
+                       int _Nonnull y[1]); // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int'}}
+
+typedef int INTS[4];
+typedef int BAD_INTS[_Nonnull 4]; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int [4]'}}
+
+void typedefTest(INTS _Nonnull x,
+                 _Nonnull INTS xx,
+                 INTS _Nonnull y[2], // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}}
+                 INTS z[_Nonnull 2]);
+
+INTS _Nonnull x; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}}
+_Nonnull INTS x; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}}
+
+void arraysInBlocks() {
+  typedef int INTS[4];
+  void (^simple)(int [_Nonnull 2]) = ^(int x[_Nonnull 2]) {};
+  simple(0); // expected-warning {{null passed to a callee that requires a non-null argument}}
+  void (^nested)(void *_Nullable x[_Nonnull 2]) = ^(void *_Nullable x[_Nonnull 2]) {};
+  nested(0); // expected-warning {{null passed to a callee that requires a non-null argument}}
+  void (^nestedBad)(int x[2][_Nonnull 2]) = // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int [2]'}}
+    ^(int x[2][_Nonnull 2]) {}; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int [2]'}}
+
+  void (^withTypedef)(INTS _Nonnull) = ^(INTS _Nonnull x) {};
+  withTypedef(0); // expected-warning {{null passed to a callee that requires a non-null argument}}
+  void (^withTypedefBad)(INTS _Nonnull [2]) = // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}}
+      ^(INTS _Nonnull x[2]) {}; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}}
+}

Modified: cfe/trunk/test/SemaCXX/nullability.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/nullability.cpp?rev=286519&r1=286518&r2=286519&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/nullability.cpp (original)
+++ cfe/trunk/test/SemaCXX/nullability.cpp Thu Nov 10 17:28:17 2016
@@ -117,3 +117,16 @@ void ConditionalExpr(bool c) {
   p = c ? nullableD : nonnullB; // expected-warning{{implicit conversion from nullable pointer 'Base * _Nullable' to non-nullable pointer type 'Base * _Nonnull}}
   p = c ? nullableD : nullableB; // expected-warning{{implicit conversion from nullable pointer 'Base * _Nullable' to non-nullable pointer type 'Base * _Nonnull}}
 }
+
+void arraysInLambdas() {
+  typedef int INTS[4];
+  auto simple = [](int [_Nonnull 2]) {};
+  simple(nullptr); // expected-warning {{null passed to a callee that requires a non-null argument}}
+  auto nested = [](void *_Nullable [_Nonnull 2]) {};
+  nested(nullptr); // expected-warning {{null passed to a callee that requires a non-null argument}}
+  auto nestedBad = [](int [2][_Nonnull 2]) {}; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int [2]'}}
+
+  auto withTypedef = [](INTS _Nonnull) {};
+  withTypedef(nullptr); // expected-warning {{null passed to a callee that requires a non-null argument}}
+  auto withTypedefBad = [](INTS _Nonnull[2]) {}; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}}
+}

Modified: cfe/trunk/test/SemaObjC/nullability.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/nullability.m?rev=286519&r1=286518&r2=286519&view=diff
==============================================================================
--- cfe/trunk/test/SemaObjC/nullability.m (original)
+++ cfe/trunk/test/SemaObjC/nullability.m Thu Nov 10 17:28:17 2016
@@ -256,3 +256,26 @@ void conditional_expr(int c) {
   p = c ? noneP : unspecifiedP;
   p = c ? noneP : noneP;
 }
+
+typedef int INTS[4];
+ at interface ArraysInMethods
+- (void)simple:(int [_Nonnull 2])x;
+- (void)nested:(void *_Nullable [_Nonnull 2])x;
+- (void)nestedBad:(int [2][_Nonnull 2])x; // expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'int [2]'}}
+
+- (void)withTypedef:(INTS _Nonnull)x;
+- (void)withTypedefBad:(INTS _Nonnull[2])x; // expected-error{{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'INTS' (aka 'int [4]')}}
+
+- (void)simpleSugar:(nonnull int [2])x;
+- (void)nestedSugar:(nonnull void *_Nullable [2])x; // expected-error {{nullability keyword 'nonnull' cannot be applied to multi-level pointer type 'void * _Nullable [2]'}} expected-note {{use nullability type specifier '_Nonnull' to affect the innermost pointer type of 'void * _Nullable [2]'}}
+- (void)sugarWithTypedef:(nonnull INTS)x;
+ at end
+
+void test(ArraysInMethods *obj) {
+  [obj simple:0]; // expected-warning {{null passed to a callee that requires a non-null argument}}
+  [obj nested:0]; // expected-warning {{null passed to a callee that requires a non-null argument}}
+  [obj withTypedef:0]; // expected-warning {{null passed to a callee that requires a non-null argument}}
+
+  [obj simpleSugar:0]; // expected-warning {{null passed to a callee that requires a non-null argument}}
+  [obj sugarWithTypedef:0]; // expected-warning {{null passed to a callee that requires a non-null argument}}
+}




More information about the cfe-commits mailing list