This revision was automatically updated to reflect the committed changes. Closed by commit rL276076: [Sema] Compute the nullability of a conditional expression based on the (authored by ahatanak).
Changed prior to commit: https://reviews.llvm.org/D22392?vs=64557&id=64628#toc Repository: rL LLVM https://reviews.llvm.org/D22392 Files: cfe/trunk/lib/Sema/SemaExpr.cpp cfe/trunk/test/Sema/nullability.c cfe/trunk/test/SemaCXX/nullability.cpp
Index: cfe/trunk/lib/Sema/SemaExpr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaExpr.cpp +++ cfe/trunk/lib/Sema/SemaExpr.cpp @@ -7002,6 +7002,55 @@ 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<NullabilityKind> 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 @@ 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 @@ CheckBoolLikeConversion(Cond.get(), QuestionLoc); + result = computeConditionalNullability(result, commonExpr, LHSTy, RHSTy, + Context); + if (!commonExpr) return new (Context) ConditionalOperator(Cond.get(), QuestionLoc, LHS.get(), ColonLoc, Index: cfe/trunk/test/SemaCXX/nullability.cpp =================================================================== --- cfe/trunk/test/SemaCXX/nullability.cpp +++ cfe/trunk/test/SemaCXX/nullability.cpp @@ -97,3 +97,23 @@ 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}} +} Index: cfe/trunk/test/Sema/nullability.c =================================================================== --- cfe/trunk/test/Sema/nullability.c +++ cfe/trunk/test/Sema/nullability.c @@ -128,3 +128,70 @@ 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; +}
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits