Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -287,6 +287,7 @@ def NullabilityDeclSpec : DiagGroup<"nullability-declspec">; def NullabilityInferredOnNestedType : DiagGroup<"nullability-inferred-on-nested-type">; def NullableToNonNullConversion : DiagGroup<"nullable-to-nonnull-conversion">; +def NullConstToNonnull : DiagGroup<"null-const-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 @@ -8802,6 +8802,10 @@ "type %1">, InGroup, DefaultIgnore; +def warn_null_const_to_nonnull : Warning< + "implicitly casting a null constant to non-nullable pointer type %0">, + InGroup, DefaultIgnore; + 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 @@ -3729,6 +3729,11 @@ void diagnoseNullableToNonnullConversion(QualType DstType, QualType SrcType, SourceLocation Loc); + /// Warn if we're implicitly casting from a null pointer constant to a + /// _Nonnull pointer type. + void diagnoseNullPtrToNonnullCast(QualType DstType, Expr *E, + SourceLocation Loc); + ParsingDeclState PushParsingDeclaration(sema::DelayedDiagnosticPool &pool) { return DelayedDiagnostics.push(pool); } Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -441,6 +441,7 @@ } } + diagnoseNullPtrToNonnullCast(Ty, E, E->getExprLoc()); return ImplicitCastExpr::Create(Context, Ty, Kind, E, BasePath, VK); } Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -9475,6 +9475,17 @@ return false; } +void Sema::diagnoseNullPtrToNonnullCast(QualType DstType, Expr *E, + SourceLocation Loc) { + if (!DstType->isAnyPointerType() || CurContext->isDependentContext()) + return; + + if (Optional Kind = DstType->getNullability(Context)) + if (*Kind == NullabilityKind::NonNull && + E->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNotNull)) + Diag(Loc, diag::warn_null_const_to_nonnull) << DstType; +} + /// \brief Diagnose pointers that are always non-null. /// \param E the expression containing the pointer /// \param NullKind NPCK_NotNull if E is a cast to bool, otherwise, E is Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -717,6 +717,7 @@ E->getType().getObjCLifetime() == Qualifiers::OCL_Weak) Cleanup.setExprNeedsCleanups(true); + diagnoseNullPtrToNonnullCast(T, E, E->getExprLoc()); ExprResult Res = ImplicitCastExpr::Create(Context, T, CK_LValueToRValue, E, nullptr, VK_RValue); Index: test/Sema/null_const_to_nonnull.c =================================================================== --- /dev/null +++ test/Sema/null_const_to_nonnull.c @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -fsyntax-only -Wnull-const-to-nonnull %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{{implicitly casting a null constant to non-nullable pointer type 'int * _Nonnull'}} +}