Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -343,6 +343,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 @@ -9156,6 +9156,10 @@ "zero as null pointer constant">, 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 @@ -10472,6 +10472,10 @@ static bool GetFormatNSStringIdx(const FormatAttr *Format, unsigned &Idx); + /// Returns true if the expression evaluates to null and doesn't have a + /// non-null type. + bool CheckNonNullExpr(const Expr *E) const; + private: bool CheckFormatArguments(const FormatAttr *Format, ArrayRef Args, @@ -10507,6 +10511,9 @@ const AttrVec *Attrs = nullptr, const FunctionDecl *FD = nullptr); + /// Check null constant to nonnull conversion. + void checkNullConstantToNonNull(QualType DstTy, Expr *RHSExpr); + public: void CheckFloatComparison(SourceLocation Loc, Expr *LHS, Expr *RHS); Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -2971,10 +2971,10 @@ /// Checks if a the given expression evaluates to null. /// /// Returns true if the value evaluates to null. -static bool CheckNonNullExpr(Sema &S, const Expr *Expr) { +bool Sema::CheckNonNullExpr(const Expr *Expr) const { // If the expression has non-null type, it doesn't evaluate to null. if (auto nullability - = Expr->IgnoreImplicit()->getType()->getNullability(S.Context)) { + = Expr->IgnoreImplicit()->getType()->getNullability(Context)) { if (*nullability == NullabilityKind::NonNull) return false; } @@ -2992,14 +2992,14 @@ bool Result; return (!Expr->isValueDependent() && - Expr->EvaluateAsBooleanCondition(Result, S.Context) && + Expr->EvaluateAsBooleanCondition(Result, Context) && !Result); } static void CheckNonNullArgument(Sema &S, const Expr *ArgExpr, SourceLocation CallSiteLoc) { - if (CheckNonNullExpr(S, ArgExpr)) + if (S.CheckNonNullExpr(ArgExpr)) S.DiagRuntimeBehavior(CallSiteLoc, ArgExpr, S.PDiag(diag::warn_null_arg) << ArgExpr->getSourceRange()); } @@ -8772,7 +8772,7 @@ // Check if the return value is null but should not be. if (((Attrs && hasSpecificAttr(*Attrs)) || (!isObjCMethod && isNonNullType(Context, lhsType))) && - CheckNonNullExpr(*this, RetValExp)) + CheckNonNullExpr(RetValExp)) Diag(ReturnLoc, diag::warn_null_ret) << (isObjCMethod ? 1 : 0) << RetValExp->getSourceRange(); @@ -8787,13 +8787,18 @@ const FunctionProtoType *Proto = FD->getType()->castAs(); if (!Proto->isNothrow(/*ResultIfDependent*/true) && - CheckNonNullExpr(*this, RetValExp)) + CheckNonNullExpr(RetValExp)) Diag(ReturnLoc, diag::warn_operator_new_returns_null) << FD << getLangOpts().CPlusPlus11; } } } +void Sema::checkNullConstantToNonNull(QualType DstTy, Expr *RHSExpr) { + if (isNonNullType(Context, DstTy) && CheckNonNullExpr(RHSExpr)) + Diag(RHSExpr->getLocStart(), diag::warn_null_constant_to_nonnull) << DstTy; +} + //===--- 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 @@ -10773,6 +10773,8 @@ Init = Result.getAs(); } + checkNullConstantToNonNull(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 @@ -7212,20 +7212,28 @@ } /// Compute the nullability of a conditional expression. -static QualType computeConditionalNullability(QualType ResTy, bool IsBin, - QualType LHSTy, QualType RHSTy, +static QualType computeConditionalNullability(Sema &S, QualType ResTy, + bool IsBin, const Expr *LHSExpr, + const Expr *RHSExpr, ASTContext &Ctx) { if (!ResTy->isAnyPointerType()) return ResTy; - auto GetNullability = [&Ctx](QualType Ty) { + auto GetNullability = [&S, &Ctx](QualType Ty, const Expr *E = nullptr) { + // If E evaluates to a null constant and doesn't have a non-null type, + // 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. @@ -7337,7 +7345,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; @@ -7352,8 +7359,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) @@ -11187,6 +11194,7 @@ return QualType(); CheckForNullPointerDereference(*this, LHSExpr); + checkNullConstantToNonNull(LHSExpr->getType(), RHS.get()); // C99 6.5.16p3: The type of an assignment expression is the type of the // left operand unless the left operand has qualified type, in which case Index: lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -1023,11 +1023,6 @@ // uninitialized in Sema's UninitializedValues analysis to warn when a use of // the zero-initialized definition will unexpectedly yield nil. - // Locals are only zero-initialized when automated reference counting - // is turned on. - if (!C.getASTContext().getLangOpts().ObjCAutoRefCount) - return false; - auto *DS = dyn_cast(S); if (!DS || !DS->isSingleDecl()) return false; @@ -1036,17 +1031,9 @@ if (!VD) return false; - // Sema only zero-initializes locals with ObjCLifetimes. - if(!VD->getType().getQualifiers().hasObjCLifetime()) - return false; - const Expr *Init = VD->getInit(); assert(Init && "ObjC local under ARC without initializer"); - // Return false if the local is explicitly initialized (e.g., with '= nil'). - if (!isa(Init)) - return false; - return true; } 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 {{implicitly casting a null constant to non-nullable pointer type 'TestObject * _Nonnull'}} } @interface ClassWithInitializers : NSObject Index: test/Analysis/nullability.mm =================================================================== --- test/Analysis/nullability.mm +++ test/Analysis/nullability.mm @@ -180,7 +180,7 @@ // Since we've already had an invariant violation along this path, // we shouldn't warn here. - nonnullLocalWithAssignmentInInitializer = 0; + nonnullLocalWithAssignmentInInitializer = 0; // expected-warning {{implicitly casting a null constant to non-nullable pointer type}} (void)nonnullLocalWithAssignmentInInitializer; } @@ -192,7 +192,7 @@ // Since we've already had an invariant violation along this path, // we shouldn't warn here. - nonnullLocalWithAssignment = 0; + nonnullLocalWithAssignment = 0; // expected-warning {{implicitly casting a null constant to non-nullable pointer type}} (void)nonnullLocalWithAssignment; } 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{{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,15 @@ +// RUN: %clang_cc1 -fsyntax-only -Wnullable-to-nonnull-conversion %s -verify + +struct S { + int * _Nonnull f; +}; + +void null_const_to_nonnull(int c, struct S *s) { + int * _Nonnull p0 = 0; // expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}} + p0 = 0; // expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}} + p0 = (int * _Nonnull)0; // explicit cast silences warnings + 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'}} + p2 = c ? p1 : (int * _Nonnull)0; // explicit cast silences warnings + s->f = 0; // expected-warning{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}} +} Index: test/SemaObjC/nullability.m =================================================================== --- test/SemaObjC/nullability.m +++ test/SemaObjC/nullability.m @@ -38,6 +38,7 @@ // expected-note@-1{{use nullability type specifier '_Nonnull' to affect the innermost pointer type of 'NSFoo **'}} - (nonnull NSFoo * _Nullable)conflictingMethod1; // expected-error{{nullability specifier '_Nullable' conflicts with existing specifier '_Nonnull'}} - (nonnull NSFoo * _Nonnull)redundantMethod1; // expected-warning{{duplicate nullability specifier '_Nonnull'}} +- (void)setObject:(id _Nonnull)object atIndexedSubscript:(int)index; @property(nonnull,retain) NSFoo *property1; @property(nullable,assign) NSFoo ** invalidProperty1; // expected-error{{nullability keyword 'nullable' cannot be applied to multi-level pointer type 'NSFoo **'}} @@ -65,6 +66,7 @@ [bar setProperty1: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} [bar setProperty2: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}} int *ptr = bar.property1; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'NSFoo * _Nonnull'}} + bar[1] = 0; // expected-warning{{null passed to a callee that requires a non-null argument}} } // Check returning nil from a nonnull-returning method. @@ -84,6 +86,9 @@ int *ip = 0; return ip; // expected-warning{{result type 'NSFoo * _Nonnull'}} } +- (void)setObject:(id _Nonnull)object atIndexedSubscript:(int)index { +} + @end __attribute__((objc_root_class))