Skip to content

Commit

Permalink
[Sema] Compute the nullability of a conditional expression based on the
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ahatanaka committed Jul 20, 2016
1 parent 6d9ca18 commit 73118fd
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 0 deletions.
53 changes: 53 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand Down
67 changes: 67 additions & 0 deletions clang/test/Sema/nullability.c
Expand Up @@ -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;
}
20 changes: 20 additions & 0 deletions clang/test/SemaCXX/nullability.cpp
Expand Up @@ -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}}
}

0 comments on commit 73118fd

Please sign in to comment.