jordan_rose created this revision.
jordan_rose added reviewers: aprantl, doug.gregor.
jordan_rose added a subscriber: cfe-commits.
jordan_rose set the repository for this revision to rL LLVM.

Last year Apple added new qualifiers to pointer types: `_Nullable`, `_Nonnull`, 
and `_Null_unspecified`. This patch extends that to array types used in 
function declarations, which should have always been supported since they 
immediately decay to pointers.

I'm not really happy with the invariants I've had to break for this to work, 
though. Previously a DecayedType always contained a PointerType; now it may 
contain an AttributedType wrapped around a PointerType. Are there any other 
places where this is going to cause problems besides debug info?

Still to do:

- Tests (I've been ad-hoc testing with -ast-dump)
- Probably fix up cases involving typedefs
- Figure out how this affects `#pragma clang assume_nonnull` (in a follow-up 
commit)

Part of rdar://problem/25846421


Repository:
  rL LLVM

https://reviews.llvm.org/D25850

Files:
  include/clang/AST/Type.h
  include/clang/Sema/Sema.h
  lib/AST/ASTContext.cpp
  lib/CodeGen/CGDebugInfo.cpp
  lib/Parse/ParseDecl.cpp
  lib/Sema/SemaAPINotes.cpp
  lib/Sema/SemaType.cpp

Index: lib/Sema/SemaType.cpp
===================================================================
--- lib/Sema/SemaType.cpp
+++ lib/Sema/SemaType.cpp
@@ -5808,6 +5808,7 @@
                                          NullabilityKind nullability,
                                          SourceLocation nullabilityLoc,
                                          bool isContextSensitive,
+                                         bool isPrototypeContext,
                                          bool implicit,
                                          bool overrideExisting) {
   if (!implicit) {
@@ -5889,7 +5890,8 @@
   }
 
   // If this definitely isn't a pointer type, reject the specifier.
-  if (!desugared->canHaveNullability()) {
+  if (!desugared->canHaveNullability() &&
+      !(isPrototypeContext && desugared->isArrayType())) {
     if (!implicit) {
       Diag(nullabilityLoc, diag::err_nullability_nonpointer)
         << DiagNullabilityKind(nullability, isContextSensitive) << type;
@@ -6647,12 +6649,14 @@
       // 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)) {
         if (state.getSema().checkNullabilityTypeSpecifier(
               type,
               mapNullabilityAttrKind(attr.getKind()),
               attr.getLoc(),
               attr.isContextSensitiveKeywordAttribute(),
+              state.getDeclarator().isPrototypeContext(),
               /*implicit=*/false)) {
           attr.setInvalid();
         }
Index: lib/Sema/SemaAPINotes.cpp
===================================================================
--- lib/Sema/SemaAPINotes.cpp
+++ lib/Sema/SemaAPINotes.cpp
@@ -46,25 +46,30 @@
   }
 
   QualType type;
+  bool isPrototypeContext;
 
   // Nullability for a function/method appertains to the retain type.
   if (auto function = dyn_cast<FunctionDecl>(decl)) {
     type = function->getReturnType();
+    isPrototypeContext = true;
   } else if (auto method = dyn_cast<ObjCMethodDecl>(decl)) {
     type = method->getReturnType();
+    isPrototypeContext = true;
   } else if (auto value = dyn_cast<ValueDecl>(decl)) {
     type = value->getType();
+    isPrototypeContext = isa<ParmVarDecl>(value);
   } else if (auto property = dyn_cast<ObjCPropertyDecl>(decl)) {
     type = property->getType();
+    isPrototypeContext = false;
   } else {
     return;
   }
 
   // Check the nullability specifier on this type.
   QualType origType = type;
   S.checkNullabilityTypeSpecifier(type, nullability, decl->getLocation(),
                                   /*isContextSensitive=*/false,
-                                  /*implicit=*/true,
+                                  isPrototypeContext, /*implicit=*/true,
                                   overrideExisting);
   if (type.getTypePtr() == origType.getTypePtr())
     return;
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp
+++ lib/Parse/ParseDecl.cpp
@@ -6275,16 +6275,15 @@
 
   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(),
                                           StaticLoc.isValid(), isStar,
                                           NumElements.get(),
                                           T.getOpenLocation(),
                                           T.getCloseLocation()),
-                attrs, T.getCloseLocation());
+                DS.getAttributes(), T.getCloseLocation());
 }
 
 /// Diagnose brackets before an identifier.
Index: lib/CodeGen/CGDebugInfo.cpp
===================================================================
--- lib/CodeGen/CGDebugInfo.cpp
+++ lib/CodeGen/CGDebugInfo.cpp
@@ -2491,10 +2491,12 @@
   case Type::Pointer:
     return CreateType(cast<PointerType>(Ty), Unit);
   case Type::Adjusted:
-  case Type::Decayed:
+  case Type::Decayed: {
     // Decayed and adjusted types use the adjusted type in LLVM and DWARF.
-    return CreateType(
-        cast<PointerType>(cast<AdjustedType>(Ty)->getAdjustedType()), Unit);
+    QualType Adjusted = cast<AdjustedType>(Ty)->getAdjustedType();
+    (void)AttributedType::stripOuterNullability(Adjusted);
+    return CreateType(cast<PointerType>(Adjusted), Unit);
+  }
   case Type::BlockPointer:
     return CreateType(cast<BlockPointerType>(Ty), Unit);
   case Type::Typedef:
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -4771,7 +4771,15 @@
   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 {
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -3097,13 +3097,17 @@
   /// method) or an Objective-C property attribute, rather than as an
   /// underscored type specifier.
   ///
+  /// \param isPrototypeContext Whether we're in a function-like context where
+  /// an array type will decay to a pointer.
+  ///
   /// \param overrideExisting Whether to override an existing, locally-specified
   /// nullability specifier rather than complaining about the conflict.
   ///
   /// \returns true if nullability cannot be applied, false otherwise.
   bool checkNullabilityTypeSpecifier(QualType &type, NullabilityKind nullability,
                                      SourceLocation nullabilityLoc,
                                      bool isContextSensitive,
+                                     bool isPrototypeContext,
                                      bool implicit,
                                      bool overrideExisting = false);
 
Index: include/clang/AST/Type.h
===================================================================
--- include/clang/AST/Type.h
+++ include/clang/AST/Type.h
@@ -2263,19 +2263,15 @@
 /// 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; }
 };
@@ -5947,6 +5943,23 @@
   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
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to