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(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(getDecayedType())->getPointeeType(); - } + inline QualType getPointeeType() const; static bool classof(const Type *T) { return T->getTypeClass() == Decayed; } }; @@ -5947,6 +5943,23 @@ return cast(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(Adjusted)); +#endif +} + +QualType DecayedType::getPointeeType() const { + QualType Decayed = getDecayedType(); + (void)AttributedType::stripOuterNullability(Decayed); + return cast(Decayed)->getPointeeType(); +} + + } // end namespace clang #endif Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -3097,6 +3097,9 @@ /// 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). + /// /// \param overrideExisting Whether to override an existing, locally-specified /// nullability specifier rather than complaining about the conflict. /// @@ -3104,6 +3107,7 @@ bool checkNullabilityTypeSpecifier(QualType &type, NullabilityKind nullability, SourceLocation nullabilityLoc, bool isContextSensitive, + bool allowArrayTypes, bool implicit, bool overrideExisting = false); 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(this)->getAttributedType( + AttributedType::getNullabilityAttrKind(*Nullability), Result, Result); + } + return Result; } QualType ASTContext::getBaseElementType(const ArrayType *array) const { Index: lib/CodeGen/CGDebugInfo.cpp =================================================================== --- lib/CodeGen/CGDebugInfo.cpp +++ lib/CodeGen/CGDebugInfo.cpp @@ -2366,12 +2366,18 @@ case Type::SubstTemplateTypeParm: T = cast(T)->getReplacementType(); break; - case Type::Auto: + case Type::Auto: { QualType DT = cast(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(T)->getAdjustedType(); + break; + } assert(T != LastT && "Type unwrapping failed to unwrap!"); (void)LastT; @@ -2490,11 +2496,6 @@ return CreateType(cast(Ty)); case Type::Pointer: return CreateType(cast(Ty), Unit); - case Type::Adjusted: - case Type::Decayed: - // Decayed and adjusted types use the adjusted type in LLVM and DWARF. - return CreateType( - cast(cast(Ty)->getAdjustedType()), Unit); case Type::BlockPointer: return CreateType(cast(Ty), Unit); case Type::Typedef: @@ -2530,6 +2531,8 @@ case Type::Auto: case Type::Attributed: + case Type::Adjusted: + case Type::Decayed: case Type::Elaborated: case Type::Paren: case Type::SubstTemplateTypeParm: Index: lib/Lex/PPMacroExpansion.cpp =================================================================== --- lib/Lex/PPMacroExpansion.cpp +++ lib/Lex/PPMacroExpansion.cpp @@ -1091,6 +1091,7 @@ .Case("enumerator_attributes", true) .Case("generalized_swift_name", 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)) Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -6275,8 +6275,7 @@ 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(), @@ -6284,7 +6283,7 @@ NumElements.get(), T.getOpenLocation(), T.getCloseLocation()), - attrs, T.getCloseLocation()); + DS.getAttributes(), T.getCloseLocation()); } /// Diagnose brackets before an identifier. Index: lib/Sema/SemaAPINotes.cpp =================================================================== --- lib/Sema/SemaAPINotes.cpp +++ lib/Sema/SemaAPINotes.cpp @@ -64,7 +64,7 @@ QualType origType = type; S.checkNullabilityTypeSpecifier(type, nullability, decl->getLocation(), /*isContextSensitive=*/false, - /*implicit=*/true, + isa(decl), /*implicit=*/true, overrideExisting); if (type.getTypePtr() == origType.getTypePtr()) return; Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -3461,6 +3461,39 @@ << static_cast(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) { @@ -3902,31 +3935,13 @@ // 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(); @@ -5808,6 +5823,7 @@ NullabilityKind nullability, SourceLocation nullabilityLoc, bool isContextSensitive, + bool allowOnArrayType, bool implicit, bool overrideExisting) { if (!implicit) { @@ -5889,7 +5905,8 @@ } // If this definitely isn't a pointer type, reject the specifier. - if (!desugared->canHaveNullability()) { + if (!desugared->canHaveNullability() && + !(allowOnArrayType && desugared->isArrayType())) { if (!implicit) { Diag(nullabilityLoc, diag::err_nullability_nonpointer) << DiagNullabilityKind(nullability, isContextSensitive) << type; @@ -5901,7 +5918,12 @@ // attributes, require that the type be a single-level pointer. if (isContextSensitive) { // Make sure that the pointee isn't itself a pointer type. - QualType pointeeType = desugared->getPointeeType(); + const Type *pointeeType; + if (desugared->isArrayType()) + pointeeType = desugared->getArrayElementTypeNoTypeQual(); + else + pointeeType = desugared->getPointeeType().getTypePtr(); + if (pointeeType->isAnyPointerType() || pointeeType->isObjCObjectPointerType() || pointeeType->isMemberPointerType()) { @@ -6647,12 +6669,22 @@ // 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(), + allowOnArrayType, /*implicit=*/false)) { attr.setInvalid(); } Index: test/Parser/nullability.c =================================================================== --- test/Parser/nullability.c +++ test/Parser/nullability.c @@ -11,6 +11,14 @@ # 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 Index: test/Sema/nullability.c =================================================================== --- test/Sema/nullability.c +++ test/Sema/nullability.c @@ -195,3 +195,55 @@ 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]')}} +} Index: test/SemaCXX/nullability.cpp =================================================================== --- test/SemaCXX/nullability.cpp +++ test/SemaCXX/nullability.cpp @@ -117,3 +117,16 @@ 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]')}} +} Index: test/SemaObjC/nullability.m =================================================================== --- test/SemaObjC/nullability.m +++ test/SemaObjC/nullability.m @@ -256,3 +256,26 @@ p = c ? noneP : unspecifiedP; p = c ? noneP : noneP; } + +typedef int INTS[4]; +@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; +@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}} +}