From: Akira Hatanaka Date: Wed, 20 Jul 2016 01:48:11 +0000 (+0000) Subject: [Sema] Compute the nullability of a conditional expression based on the X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8c7a86891a7122bc9b5f635be7017095cc679de5;p=clang [Sema] Compute the nullability of a conditional expression based on the nullabilities of its operands. This patch defines a function to compute the nullability of conditional expressions, which enables Sema to precisely detect implicit conversions of nullable conditional expressions to nonnull pointers. rdar://problem/25166556 Differential Revision: https://reviews.llvm.org/D22392 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@276076 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index e882f2e28c..d748b3c18e 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -7002,6 +7002,55 @@ static void DiagnoseConditionalPrecedence(Sema &Self, SourceRange(CondRHS->getLocStart(), RHSExpr->getLocEnd())); } +/// Compute the nullability of a conditional expression. +static QualType computeConditionalNullability(QualType ResTy, bool IsBin, + QualType LHSTy, QualType RHSTy, + ASTContext &Ctx) { + if (!ResTy->isPointerType()) + return ResTy; + + auto GetNullability = [&Ctx](QualType Ty) { + Optional Kind = Ty->getNullability(Ctx); + if (Kind) + return *Kind; + return NullabilityKind::Unspecified; + }; + + auto LHSKind = GetNullability(LHSTy), RHSKind = GetNullability(RHSTy); + NullabilityKind MergedKind; + + // Compute nullability of a binary conditional expression. + if (IsBin) { + if (LHSKind == NullabilityKind::NonNull) + MergedKind = NullabilityKind::NonNull; + else + MergedKind = RHSKind; + // Compute nullability of a normal conditional expression. + } else { + if (LHSKind == NullabilityKind::Nullable || + RHSKind == NullabilityKind::Nullable) + MergedKind = NullabilityKind::Nullable; + else if (LHSKind == NullabilityKind::NonNull) + MergedKind = RHSKind; + else if (RHSKind == NullabilityKind::NonNull) + MergedKind = LHSKind; + else + MergedKind = NullabilityKind::Unspecified; + } + + // Return if ResTy already has the correct nullability. + if (GetNullability(ResTy) == MergedKind) + return ResTy; + + // Strip all nullability from ResTy. + while (ResTy->getNullability(Ctx)) + ResTy = ResTy.getSingleStepDesugaredType(Ctx); + + // Create a new AttributedType with the new nullability kind. + auto NewAttr = AttributedType::getNullabilityAttrKind(MergedKind); + return Ctx.getAttributedType(NewAttr, ResTy, ResTy); +} + /// ActOnConditionalOp - Parse a ?: operation. Note that 'LHS' may be null /// in the case of a the GNU conditional expr extension. ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc, @@ -7069,6 +7118,7 @@ ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc, 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; @@ -7083,6 +7133,9 @@ ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc, CheckBoolLikeConversion(Cond.get(), QuestionLoc); + result = computeConditionalNullability(result, commonExpr, LHSTy, RHSTy, + Context); + if (!commonExpr) return new (Context) ConditionalOperator(Cond.get(), QuestionLoc, LHS.get(), ColonLoc, diff --git a/test/Sema/nullability.c b/test/Sema/nullability.c index 71e12734d1..9d3145d068 100644 --- a/test/Sema/nullability.c +++ b/test/Sema/nullability.c @@ -128,3 +128,70 @@ void nullable_to_nonnull(_Nullable int *ptr) { accepts_nonnull_1(ptr); // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} } + +// Check nullability of conditional expressions. +void conditional_expr(int c) { + int * _Nonnull p; + int * _Nonnull nonnullP; + int * _Nullable nullableP; + int * _Null_unspecified unspecifiedP; + int *noneP; + + p = c ? nonnullP : nonnullP; + p = c ? nonnullP : nullableP; // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} + p = c ? nonnullP : unspecifiedP; + p = c ? nonnullP : noneP; + p = c ? nullableP : nonnullP; // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} + p = c ? nullableP : nullableP; // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} + p = c ? nullableP : unspecifiedP; // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} + p = c ? nullableP : noneP; // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} + p = c ? unspecifiedP : nonnullP; + p = c ? unspecifiedP : nullableP; // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} + p = c ? unspecifiedP : unspecifiedP; + p = c ? unspecifiedP : noneP; + p = c ? noneP : nonnullP; + p = c ? noneP : nullableP; // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} + p = c ? noneP : unspecifiedP; + p = c ? noneP : noneP; + + // Check that we don't remove all sugar when creating a new QualType for the + // conditional expression. + typedef int *IntP; + typedef IntP _Nonnull NonnullIntP0; + typedef NonnullIntP0 _Nonnull NonnullIntP1; + typedef IntP _Nullable NullableIntP0; + typedef NullableIntP0 _Nullable NullableIntP1; + NonnullIntP1 nonnullP2; + NullableIntP1 nullableP2; + + p = c ? nonnullP2 : nonnullP2; + p = c ? nonnullP2 : nullableP2; // expected-warning{{implicit conversion from nullable pointer 'IntP _Nullable' (aka 'int *') to non-nullable pointer type 'int * _Nonnull'}} + p = c ? nullableP2 : nonnullP2; // expected-warning{{implicit conversion from nullable pointer 'NullableIntP1' (aka 'int *') to non-nullable pointer type 'int * _Nonnull'}} + p = c ? nullableP2 : nullableP2; // expected-warning{{implicit conversion from nullable pointer 'NullableIntP1' (aka 'int *') to non-nullable pointer type 'int * _Nonnull'}} +} + +// Check nullability of binary conditional expressions. +void binary_conditional_expr() { + int * _Nonnull p; + int * _Nonnull nonnullP; + int * _Nullable nullableP; + int * _Null_unspecified unspecifiedP; + int *noneP; + + p = nonnullP ?: nonnullP; + p = nonnullP ?: nullableP; + p = nonnullP ?: unspecifiedP; + p = nonnullP ?: noneP; + p = nullableP ?: nonnullP; + p = nullableP ?: nullableP; // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} + p = nullableP ?: unspecifiedP; + p = nullableP ?: noneP; + p = unspecifiedP ?: nonnullP; + p = unspecifiedP ?: nullableP; // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} + p = unspecifiedP ?: unspecifiedP; + p = unspecifiedP ?: noneP; + p = noneP ?: nonnullP; + p = noneP ?: nullableP; // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} + p = noneP ?: unspecifiedP; + p = noneP ?: noneP; +} diff --git a/test/SemaCXX/nullability.cpp b/test/SemaCXX/nullability.cpp index c73c01a0a8..2af25730b6 100644 --- a/test/SemaCXX/nullability.cpp +++ b/test/SemaCXX/nullability.cpp @@ -97,3 +97,23 @@ void AssignAndInitNonNullFromFn() { TakeNonnull(ReturnNullable()); //expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull}} } + +void ConditionalExpr(bool c) { + struct Base {}; + struct Derived : Base {}; + + Base * _Nonnull p; + Base * _Nonnull nonnullB; + Base * _Nullable nullableB; + Derived * _Nonnull nonnullD; + Derived * _Nullable nullableD; + + p = c ? nonnullB : nonnullD; + p = c ? nonnullB : nullableD; // expected-warning{{implicit conversion from nullable pointer 'Base * _Nullable' to non-nullable pointer type 'Base * _Nonnull}} + p = c ? nullableB : nonnullD; // expected-warning{{implicit conversion from nullable pointer 'Base * _Nullable' to non-nullable pointer type 'Base * _Nonnull}} + p = c ? nullableB : nullableD; // expected-warning{{implicit conversion from nullable pointer 'Base * _Nullable' to non-nullable pointer type 'Base * _Nonnull}} + p = c ? nonnullD : nonnullB; + p = c ? nonnullD : nullableB; // expected-warning{{implicit conversion from nullable pointer 'Base * _Nullable' to non-nullable pointer type 'Base * _Nonnull}} + 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}} +}