Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -290,6 +290,7 @@ def NullabilityDeclSpec : DiagGroup<"nullability-declspec">; def NullabilityInferredOnNestedType : DiagGroup<"nullability-inferred-on-nested-type">; def NullableToNonNullConversion : DiagGroup<"nullable-to-nonnull-conversion">; +def NullConstantToNonnull : DiagGroup<"null-constant-to-nonnull">; def NullabilityCompletenessOnArrays : DiagGroup<"nullability-completeness-on-arrays">; def NullabilityCompleteness : DiagGroup<"nullability-completeness", [NullabilityCompletenessOnArrays]>; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -8919,6 +8919,10 @@ "type %1">, InGroup, DefaultIgnore; +def warn_null_constant_to_nonnull : Warning< + "implicitly casting a null constant to non-nullable pointer type %0">, + InGroup; + def err_nullability_cs_multilevel : Error< "nullability keyword %0 cannot be applied to multi-level pointer type %1">; def note_nullability_type_specifier : Note< Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -10065,6 +10065,8 @@ static bool GetFormatNSStringIdx(const FormatAttr *Format, unsigned &Idx); + bool checkNonNullExpr(const Expr *E) const; + private: bool CheckFormatArguments(const FormatAttr *Format, ArrayRef Args, @@ -10100,6 +10102,8 @@ const AttrVec *Attrs = nullptr, const FunctionDecl *FD = nullptr); + void checkVarDeclInit(QualType DeclTy, Expr *Init); + void CheckFloatComparison(SourceLocation Loc, Expr* LHS, Expr* RHS); void CheckImplicitConversions(Expr *E, SourceLocation CC = SourceLocation()); void CheckBoolLikeConversion(Expr *E, SourceLocation CC); Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -2353,7 +2353,7 @@ /// Checks if a the given expression evaluates to null. /// /// \brief Returns true if the value evaluates to null. -static bool CheckNonNullExpr(Sema &S, const Expr *Expr) { +static bool CheckNonNullExpr(const Sema &S, const Expr *Expr) { // If the expression has non-null type, it doesn't evaluate to null. if (auto nullability = Expr->IgnoreImplicit()->getType()->getNullability(S.Context)) { @@ -2378,6 +2378,10 @@ !Result); } +bool Sema::checkNonNullExpr(const Expr *E) const { + return CheckNonNullExpr(*this, E); +} + static void CheckNonNullArgument(Sema &S, const Expr *ArgExpr, SourceLocation CallSiteLoc) { @@ -7864,6 +7868,11 @@ } } +void Sema::checkVarDeclInit(QualType DeclTy, Expr *Init) { + if (isNonNullType(Context, DeclTy) && CheckNonNullExpr(*this, Init)) + Diag(Init->getLocStart(), diag::warn_null_constant_to_nonnull) << DeclTy; +} + //===--- CHECK: Floating-Point comparisons (-Wfloat-equal) ---------------===// /// Check for comparisons of floating point operands using != and ==. Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -10151,6 +10151,8 @@ Init = Result.getAs(); } + checkVarDeclInit(DclT, Init); + // Check for self-references within variable initializers. // Variables declared within a function/method body (except for references) // are handled by a dataflow analysis. Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -7105,20 +7105,26 @@ } /// Compute the nullability of a conditional expression. -static QualType computeConditionalNullability(QualType ResTy, bool IsBin, - QualType LHSTy, QualType RHSTy, - ASTContext &Ctx) { +static QualType computeConditionalNullability(Sema &S, QualType ResTy, + bool IsBin, Expr *LHSExpr, + Expr *RHSExpr, ASTContext &Ctx) { if (!ResTy->isAnyPointerType()) return ResTy; - auto GetNullability = [&Ctx](QualType Ty) { + auto GetNullability = [&S, &Ctx](QualType Ty, Expr *E = nullptr) { + // If E evaluates to a null constant, return nullable. + if (E && S.checkNonNullExpr(E)) + return NullabilityKind::Nullable; + Optional Kind = Ty->getNullability(Ctx); if (Kind) return *Kind; return NullabilityKind::Unspecified; }; - auto LHSKind = GetNullability(LHSTy), RHSKind = GetNullability(RHSTy); + QualType LHSTy = LHSExpr->getType(), RHSTy = RHSExpr->getType(); + auto LHSKind = GetNullability(LHSTy, LHSExpr); + auto RHSKind = GetNullability(RHSTy, RHSExpr); NullabilityKind MergedKind; // Compute nullability of a binary conditional expression. @@ -7220,7 +7226,6 @@ LHSExpr = CondExpr = opaqueValue; } - QualType LHSTy = LHSExpr->getType(), RHSTy = RHSExpr->getType(); ExprValueKind VK = VK_RValue; ExprObjectKind OK = OK_Ordinary; ExprResult Cond = CondExpr, LHS = LHSExpr, RHS = RHSExpr; @@ -7235,8 +7240,8 @@ CheckBoolLikeConversion(Cond.get(), QuestionLoc); - result = computeConditionalNullability(result, commonExpr, LHSTy, RHSTy, - Context); + result = computeConditionalNullability(*this, result, commonExpr, LHSExpr, + RHSExpr, Context); if (!commonExpr) return new (Context) Index: test/Analysis/nullability-no-arc.mm =================================================================== --- test/Analysis/nullability-no-arc.mm +++ test/Analysis/nullability-no-arc.mm @@ -43,7 +43,7 @@ } void testObjCNonARCExplicitZeroInitialization() { - TestObject * _Nonnull explicitlyZeroInitialized = nil; // expected-warning {{nil assigned to a pointer which is expected to have non-null value}} + TestObject * _Nonnull explicitlyZeroInitialized = nil; // expected-warning {{nil assigned to a pointer which is expected to have non-null value}} expected-warning {{implicitly casting a null constant to non-nullable pointer type 'TestObject * _Nonnull'}} } @interface ClassWithInitializers : NSObject Index: test/Analysis/nullability_nullonly.mm =================================================================== --- test/Analysis/nullability_nullonly.mm +++ test/Analysis/nullability_nullonly.mm @@ -100,7 +100,7 @@ } void testObjCARCExplicitZeroInitialization() { - TestObject * _Nonnull explicitlyZeroInitialized = nil; // expected-warning {{nil assigned to a pointer which is expected to have non-null value}} + TestObject * _Nonnull explicitlyZeroInitialized = nil; // expected-warning {{nil assigned to a pointer which is expected to have non-null value}} expected-warning{{implicitly casting a null constant to non-nullable pointer type 'TestObject * _Nonnull __strong'}} } // Under ARC, returned expressions of ObjC objects types are are implicitly Index: test/Sema/conditional-expr.c =================================================================== --- test/Sema/conditional-expr.c +++ test/Sema/conditional-expr.c @@ -17,7 +17,7 @@ dp = ip; // expected-warning {{incompatible pointer types assigning to 'double *' from 'int *'}} dp = 0 ? (double *)0 : (void *)0; vp = 0 ? (double *)0 : (void *)0; - ip = 0 ? (double *)0 : (void *)0; // expected-warning {{incompatible pointer types assigning to 'int *' from 'double *'}} + ip = 0 ? (double *)0 : (void *)0; // expected-warning {{incompatible pointer types assigning to 'int *' from 'double * _Nullable'}} const int *cip; vp = (0 ? vp : cip); // expected-warning {{discards qualifiers}} @@ -90,7 +90,7 @@ int f0(int a) { // GCC considers this a warning. - return a ? f1() : nil; // expected-warning {{pointer/integer type mismatch in conditional expression ('int' and 'void *')}} expected-warning {{incompatible pointer to integer conversion returning 'void *' from a function with result type 'int'}} + return a ? f1() : nil; // expected-warning {{pointer/integer type mismatch in conditional expression ('int' and 'void *')}} expected-warning {{incompatible pointer to integer conversion returning 'void * _Nullable' from a function with result type 'int'}} } int f2(int x) { Index: test/Sema/null_constant_to_nonnull.c =================================================================== --- /dev/null +++ test/Sema/null_constant_to_nonnull.c @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -fsyntax-only -Wnullable-to-nonnull-conversion %s -verify + +void null_const_to_nonnull(int c) { + int * _Nonnull p0 = 0; // expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}} + int * _Nonnull p1; + int * _Nonnull p2 = c ? p1 : 0; // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} +}