summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAkira Hatanaka <ahatanaka@apple.com>2016-07-20 01:48:11 +0000
committerAkira Hatanaka <ahatanaka@apple.com>2016-07-20 01:48:11 +0000
commit73118fd10e593d56cce5dd47cdba98e769a120c5 (patch)
tree05b0e97179f1c9a3b7932415cd645452995e5cdc
parent6d9ca182fe70de60ed76e635fc7311fa8669a3cd (diff)
downloadbcm5719-llvm-73118fd10e593d56cce5dd47cdba98e769a120c5.tar.gz
bcm5719-llvm-73118fd10e593d56cce5dd47cdba98e769a120c5.zip
[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 llvm-svn: 276076
-rw-r--r--clang/lib/Sema/SemaExpr.cpp53
-rw-r--r--clang/test/Sema/nullability.c67
-rw-r--r--clang/test/SemaCXX/nullability.cpp20
3 files changed, 140 insertions, 0 deletions
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index e882f2e28c5..d748b3c18e5 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/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<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 @@ 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/clang/test/Sema/nullability.c b/clang/test/Sema/nullability.c
index 71e12734d1d..9d3145d068b 100644
--- a/clang/test/Sema/nullability.c
+++ b/clang/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/clang/test/SemaCXX/nullability.cpp b/clang/test/SemaCXX/nullability.cpp
index c73c01a0a86..2af25730b66 100644
--- a/clang/test/SemaCXX/nullability.cpp
+++ b/clang/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}}
+}
OpenPOWER on IntegriCloud