Re: [PATCH] D22392: [Sema] Fix an invalid nullability warning for binary conditional operators
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 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
Re: [PATCH] D22392: [Sema] Fix an invalid nullability warning for binary conditional operators
doug.gregor accepted this revision. doug.gregor added a comment. This revision is now accepted and ready to land. LGTM! https://reviews.llvm.org/D22392 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
Re: [PATCH] D22392: [Sema] Fix an invalid nullability warning for binary conditional operators
ahatanak updated this revision to Diff 64557. ahatanak added a comment. Remove the check "if (LHSKind == RHSKind)" in computeConditionalNullability as it's not needed. https://reviews.llvm.org/D22392 Files: lib/Sema/SemaExpr.cpp test/Sema/nullability.c test/SemaCXX/nullability.cpp Index: test/SemaCXX/nullability.cpp === --- test/SemaCXX/nullability.cpp +++ 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: test/Sema/nullability.c === --- test/Sema/nullability.c +++ 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;
Re: [PATCH] D22392: [Sema] Fix an invalid nullability warning for binary conditional operators
ahatanak updated this revision to Diff 64551. ahatanak added a comment. Address review comments. - Rename function to computeConditionalNullability. - Rewrite the function to compute the nullability of both normal and binary conditional expressions. - Add more test cases. https://reviews.llvm.org/D22392 Files: lib/Sema/SemaExpr.cpp test/Sema/nullability.c test/SemaCXX/nullability.cpp Index: test/SemaCXX/nullability.cpp === --- test/SemaCXX/nullability.cpp +++ 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: test/Sema/nullability.c === --- test/Sema/nullability.c +++ 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 co
Re: [PATCH] D22392: [Sema] Fix an invalid nullability warning for binary conditional operators
ahatanak added a comment. I'm thinking about taking the following steps to compute the nullability of ternary operators. The first step is to compute the "merged" nullability of the LHS and RHS. For normal ternary operators (not the shorthand version): - If both LHS and RHS have the same nullability (one of nonnull, nullable, null_unspecified, or none), pick that as the merged nullability. - Otherwise, if either side is nullable, the merged nullability is nullable too. - Otherwise, if either side is nonnull, pick the nullability of the other side. For example, if (LHS,RHS) == (nonnull, none), the merged nullability is none because we can't guarantee that the result is nonnull. - Otherwise, the nullability is either null_unspecified or none. I think we can pick either one in this case. For binary conditional operators like "p0 ?: p1": - If the LHS (p0) is nonnull, the merged nullability is nonnull. - Otherwise, the merged nullability is the nullability of the RHS (p1). Once we have the merged nullability, the next step is to compare it with the result's nullability. If the nullability kinds differ, a new type that has the merged nullability is created. Note that it looks like we might be able to simplify the steps described above if we take advantage of the fact that clang only warns when a nullable pointer is assigned/passed to a nonnull pointer (I don't think clang issues any warnings, for example, when a null_unspecified pointer is assigned to a nonnull pointer). https://reviews.llvm.org/D22392 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
Re: [PATCH] D22392: [Sema] Fix an invalid nullability warning for binary conditional operators
ahatanak added inline comments. Comment at: lib/Sema/SemaExpr.cpp:7007 @@ +7006,3 @@ +/// expression. +static QualType modifyNullability(QualType ResTy, Expr *RHSExpr, + ASTContext &Ctx) { doug.gregor wrote: > This name could be improved. You're not really 'modifying' nullability here; > in the general case, you're computing the appropriate nullability given the > LHS, RHS, and applying that to the result type. I'm thinking about renaming it to computeConditionalNullability or something, but I'm open to suggestions. Comment at: lib/Sema/SemaExpr.cpp:7024 @@ +7023,3 @@ + + NullabilityKind NewKind = GetNullability(RHSExpr->getType()); + doug.gregor wrote: > I'm fairly certain that more extensive testing will show that you need to > have the LHSExpr as well, to look at the nullability of both. This patch only tries to correct the case where you have a binary (shorthand version) conditional operator "p0 ?: p1" where p1 is nonnulll and p0 is nullable, in which case the result type should be nonnull. I think this function behaves correctly in this particular case, but it's possible that I have overlooked some corner cases. In any case, I think the focus of this patch was too narrow. I think I should fix the nullability of any conditional operators, not just the shorthand versions, in my next patch. Comment at: lib/Sema/SemaExpr.cpp:7030 @@ +7029,3 @@ + // Create a new AttributedType with the new nullability kind. + QualType NewTy = ResTy.getDesugaredType(Ctx); + auto NewAttr = AttributedType::getNullabilityAttrKind(NewKind); doug.gregor wrote: > It would be better to only unwrap sugar until we hit the > nullability-attributed type, then replace it. I think we need to unwrap sugar more than once until there are no more nullability-attributed types in some cases. For example, it's possible to have multiple levels of typedefs having nullability markers: ``` typedef int * IntP; typedef IntP _Nullable NullableIntP0; typedef NullableIntP0 _Nullable NullableIntP1; ``` Comment at: test/Sema/nullability.c:137 @@ +136,3 @@ + + int * _Nonnull p2 = p0 ?: p1; // no warnings here. +} doug.gregor wrote: > You really need much more testing coverage here, e.g., for ternary operators > where the types on the second and third arguments are different types (say, > superclass/subclass pointer), the nullability is on either argument, etc. The > ternary operator, especially in C++, has a ton of different cases that you > need to look at. Yes, I'll have more test cases in my next patch. https://reviews.llvm.org/D22392 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
Re: [PATCH] D22392: [Sema] Fix an invalid nullability warning for binary conditional operators
doug.gregor added a comment. A bunch of comments above. This needs much more extensive testing, because there are numerous paths through the ternary operator code and the results need to be symmetric. Comment at: lib/Sema/SemaExpr.cpp:7007 @@ +7006,3 @@ +/// expression. +static QualType modifyNullability(QualType ResTy, Expr *RHSExpr, + ASTContext &Ctx) { This name could be improved. You're not really 'modifying' nullability here; in the general case, you're computing the appropriate nullability given the LHS, RHS, and applying that to the result type. Comment at: lib/Sema/SemaExpr.cpp:7024 @@ +7023,3 @@ + + NullabilityKind NewKind = GetNullability(RHSExpr->getType()); + I'm fairly certain that more extensive testing will show that you need to have the LHSExpr as well, to look at the nullability of both. Comment at: lib/Sema/SemaExpr.cpp:7030 @@ +7029,3 @@ + // Create a new AttributedType with the new nullability kind. + QualType NewTy = ResTy.getDesugaredType(Ctx); + auto NewAttr = AttributedType::getNullabilityAttrKind(NewKind); It would be better to only unwrap sugar until we hit the nullability-attributed type, then replace it. Comment at: test/Sema/nullability.c:137 @@ +136,3 @@ + + int * _Nonnull p2 = p0 ?: p1; // no warnings here. +} You really need much more testing coverage here, e.g., for ternary operators where the types on the second and third arguments are different types (say, superclass/subclass pointer), the nullability is on either argument, etc. The ternary operator, especially in C++, has a ton of different cases that you need to look at. https://reviews.llvm.org/D22392 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
Re: [PATCH] D22392: [Sema] Fix an invalid nullability warning for binary conditional operators
ahatanak updated this revision to Diff 64077. ahatanak added a comment. Remove unused variable. https://reviews.llvm.org/D22392 Files: lib/Sema/SemaExpr.cpp test/Sema/nullability.c Index: test/Sema/nullability.c === --- test/Sema/nullability.c +++ test/Sema/nullability.c @@ -128,3 +128,11 @@ accepts_nonnull_1(ptr); // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} } + +// Check nullability of binary conditional expressions. +void modify_nullability(int c) { + int * _Nullable p0; + int * _Nonnull p1; + + int * _Nonnull p2 = p0 ?: p1; // no warnings here. +} Index: lib/Sema/SemaExpr.cpp === --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -7002,6 +7002,36 @@ SourceRange(CondRHS->getLocStart(), RHSExpr->getLocEnd())); } +/// Modify the nullability kind of the result type of a binary conditional +/// expression. +static QualType modifyNullability(QualType ResTy, Expr *RHSExpr, + ASTContext &Ctx) { + if (!ResTy->isPointerType()) +return ResTy; + + auto GetNullability = [&Ctx](QualType Ty) { +Optional Kind = Ty->getNullability(Ctx); +if (Kind) + return *Kind; +return NullabilityKind::Unspecified; + }; + + // Change the result type only if the conditional expression is nullable and + // the RHS is nonnull. + if (GetNullability(ResTy) != NullabilityKind::Nullable) +return ResTy; + + NullabilityKind NewKind = GetNullability(RHSExpr->getType()); + + if (NewKind != NullabilityKind::NonNull) +return ResTy; + + // Create a new AttributedType with the new nullability kind. + QualType NewTy = ResTy.getDesugaredType(Ctx); + auto NewAttr = AttributedType::getNullabilityAttrKind(NewKind); + return Ctx.getAttributedType(NewAttr, NewTy, NewTy); +} + /// 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, @@ -7088,6 +7118,7 @@ ConditionalOperator(Cond.get(), QuestionLoc, LHS.get(), ColonLoc, RHS.get(), result, VK, OK); + result = modifyNullability(result, RHSExpr, Context); return new (Context) BinaryConditionalOperator( commonExpr, opaqueValue, Cond.get(), LHS.get(), RHS.get(), QuestionLoc, ColonLoc, result, VK, OK); Index: test/Sema/nullability.c === --- test/Sema/nullability.c +++ test/Sema/nullability.c @@ -128,3 +128,11 @@ accepts_nonnull_1(ptr); // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} } + +// Check nullability of binary conditional expressions. +void modify_nullability(int c) { + int * _Nullable p0; + int * _Nonnull p1; + + int * _Nonnull p2 = p0 ?: p1; // no warnings here. +} Index: lib/Sema/SemaExpr.cpp === --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -7002,6 +7002,36 @@ SourceRange(CondRHS->getLocStart(), RHSExpr->getLocEnd())); } +/// Modify the nullability kind of the result type of a binary conditional +/// expression. +static QualType modifyNullability(QualType ResTy, Expr *RHSExpr, + ASTContext &Ctx) { + if (!ResTy->isPointerType()) +return ResTy; + + auto GetNullability = [&Ctx](QualType Ty) { +Optional Kind = Ty->getNullability(Ctx); +if (Kind) + return *Kind; +return NullabilityKind::Unspecified; + }; + + // Change the result type only if the conditional expression is nullable and + // the RHS is nonnull. + if (GetNullability(ResTy) != NullabilityKind::Nullable) +return ResTy; + + NullabilityKind NewKind = GetNullability(RHSExpr->getType()); + + if (NewKind != NullabilityKind::NonNull) +return ResTy; + + // Create a new AttributedType with the new nullability kind. + QualType NewTy = ResTy.getDesugaredType(Ctx); + auto NewAttr = AttributedType::getNullabilityAttrKind(NewKind); + return Ctx.getAttributedType(NewAttr, NewTy, NewTy); +} + /// 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, @@ -7088,6 +7118,7 @@ ConditionalOperator(Cond.get(), QuestionLoc, LHS.get(), ColonLoc, RHS.get(), result, VK, OK); + result = modifyNullability(result, RHSExpr, Context); return new (Context) BinaryConditionalOperator( commonExpr, opaqueValue, Cond.get(), LHS.get(), RHS.get(), QuestionLoc, ColonLoc, result, VK, OK); ___ cfe-commits mailing list cfe-commits@list
[PATCH] D22392: [Sema] Fix an invalid nullability warning for binary conditional operators
ahatanak created this revision. ahatanak added a reviewer: doug.gregor. ahatanak added a subscriber: cfe-commits. Currently clang issues a warning when the following code using a shorthand form of the conditional operator is compiled: $ cat test1.c ``` void foo() { int * _Nullable p0; int * _Nonnull p1; int * _Nonnull p2 = p0 ?: p1; } ``` test.c:4:23: warning: implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull' [-Wnullable-to-nonnull-conversion] int * _Nonnull p2 = p0 ?: p1; This happens because sema uses the type of the first operand (p0's type, which is "int * _Nullable") for the type of the binary condition expression and then complains because a nullable pointer is being assigned to a nonnull pointer. The warning is not valid since the expression "p0 ?: p1" cannot be null if p1 is nonnull. This patch fixes the bug by propagating the nullability of the last operand to the binary conditional expression. https://reviews.llvm.org/D22392 Files: lib/Sema/SemaExpr.cpp test/Sema/nullability.c Index: test/Sema/nullability.c === --- test/Sema/nullability.c +++ test/Sema/nullability.c @@ -128,3 +128,11 @@ accepts_nonnull_1(ptr); // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} } + +// Check nullability of binary conditional expressions. +void modify_nullability(int c) { + int * _Nullable p0; + int * _Nonnull p1; + + int * _Nonnull p2 = p0 ?: p1; // no warnings here. +} Index: lib/Sema/SemaExpr.cpp === --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -7002,6 +7002,38 @@ SourceRange(CondRHS->getLocStart(), RHSExpr->getLocEnd())); } +/// Modify the nullability kind of the result type of a binary conditional +/// expression. +static QualType modifyNullability(QualType ResTy, Expr *RHSExpr, + ASTContext &Ctx) { + if (!ResTy->isPointerType()) +return ResTy; + + auto GetNullability = [&Ctx](QualType Ty) { +Optional Kind = Ty->getNullability(Ctx); +if (Kind) + return *Kind; +return NullabilityKind::Unspecified; + }; + + // Change the result type only if the conditional expression is nullable and + // the RHS is nonnull. + NullabilityKind ResKind = GetNullability(ResTy); + + if (GetNullability(ResTy) != NullabilityKind::Nullable) +return ResTy; + + NullabilityKind NewKind = GetNullability(RHSExpr->getType()); + + if (NewKind != NullabilityKind::NonNull) +return ResTy; + + // Create a new AttributedType with the new nullability kind. + QualType NewTy = ResTy.getDesugaredType(Ctx); + auto NewAttr = AttributedType::getNullabilityAttrKind(NewKind); + return Ctx.getAttributedType(NewAttr, NewTy, NewTy); +} + /// 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, @@ -7088,6 +7120,7 @@ ConditionalOperator(Cond.get(), QuestionLoc, LHS.get(), ColonLoc, RHS.get(), result, VK, OK); + result = modifyNullability(result, RHSExpr, Context); return new (Context) BinaryConditionalOperator( commonExpr, opaqueValue, Cond.get(), LHS.get(), RHS.get(), QuestionLoc, ColonLoc, result, VK, OK); Index: test/Sema/nullability.c === --- test/Sema/nullability.c +++ test/Sema/nullability.c @@ -128,3 +128,11 @@ accepts_nonnull_1(ptr); // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}} } + +// Check nullability of binary conditional expressions. +void modify_nullability(int c) { + int * _Nullable p0; + int * _Nonnull p1; + + int * _Nonnull p2 = p0 ?: p1; // no warnings here. +} Index: lib/Sema/SemaExpr.cpp === --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -7002,6 +7002,38 @@ SourceRange(CondRHS->getLocStart(), RHSExpr->getLocEnd())); } +/// Modify the nullability kind of the result type of a binary conditional +/// expression. +static QualType modifyNullability(QualType ResTy, Expr *RHSExpr, + ASTContext &Ctx) { + if (!ResTy->isPointerType()) +return ResTy; + + auto GetNullability = [&Ctx](QualType Ty) { +Optional Kind = Ty->getNullability(Ctx); +if (Kind) + return *Kind; +return NullabilityKind::Unspecified; + }; + + // Change the result type only if the conditional expression is nullable and + // the RHS is nonnull. + NullabilityKind ResKind = GetNullability(ResTy); + + if (GetNullability(ResTy) != NullabilityKind::Nullable) +return ResTy; + + NullabilityKind NewKind = Ge