37 changes: 37 additions & 0 deletions clang/lib/AST/TypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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");
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Lex/PPMacroExpansion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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)
Expand Down
45 changes: 45 additions & 0 deletions clang/lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 == '_');
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand Down
7 changes: 6 additions & 1 deletion clang/lib/Parse/ParseTentative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
31 changes: 28 additions & 3 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
264 changes: 254 additions & 10 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand All @@ -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");
Expand Down Expand Up @@ -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(),
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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!");
}
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions clang/test/FixIt/fixit-nullability-declspec.cpp
Original file line number Diff line number Diff line change
@@ -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?}}
16 changes: 16 additions & 0 deletions clang/test/Parser/nullability.c
Original file line number Diff line number Diff line change
@@ -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
35 changes: 35 additions & 0 deletions clang/test/Sema/non-null-warning.c
Original file line number Diff line number Diff line change
@@ -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;
}

87 changes: 87 additions & 0 deletions clang/test/Sema/nullability.c
Original file line number Diff line number Diff line change
@@ -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'}}
}
9 changes: 9 additions & 0 deletions clang/test/SemaCXX/nullability-declspec.cpp
Original file line number Diff line number Diff line change
@@ -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?}}
42 changes: 42 additions & 0 deletions clang/test/SemaCXX/nullability.cpp
Original file line number Diff line number Diff line change
@@ -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>'}}
};
19 changes: 19 additions & 0 deletions clang/test/SemaObjC/nullability.m
Original file line number Diff line number Diff line change
@@ -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)'}}
}