Skip to content

Commit 76accbb

Browse files
committed
[Sema] Diagnose misplaced InOutExpr in preCheckExpression
It's much easier to diagnose structural problems related to use of InOutExpr when AST is formed. (cherry picked from commit 3149e2d)
1 parent 54b6413 commit 76accbb

File tree

3 files changed

+67
-5
lines changed

3 files changed

+67
-5
lines changed

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,8 @@ namespace {
852852
TypeChecker &TC;
853853
DeclContext *DC;
854854

855+
Expr *ParentExpr;
856+
855857
/// A stack of expressions being walked, used to determine where to
856858
/// insert RebindSelfInConstructorExpr nodes.
857859
llvm::SmallVector<Expr *, 8> ExprStack;
@@ -880,7 +882,8 @@ namespace {
880882
void resolveKeyPathExpr(KeyPathExpr *KPE);
881883

882884
public:
883-
PreCheckExpression(TypeChecker &tc, DeclContext *dc) : TC(tc), DC(dc) { }
885+
PreCheckExpression(TypeChecker &tc, DeclContext *dc, Expr *parent)
886+
: TC(tc), DC(dc), ParentExpr(parent) {}
884887

885888
bool walkToClosureExprPre(ClosureExpr *expr);
886889

@@ -941,6 +944,43 @@ namespace {
941944
return finish(true, expr);
942945
}
943946

947+
// Let's try to figure out if `InOutExpr` is out of place early
948+
// otherwise there is a risk of producing solutions which can't
949+
// be later applied to AST and would result in the crash in some
950+
// cases. Such expressions are only allowed in argument positions
951+
// of function/operator calls.
952+
if (isa<InOutExpr>(expr)) {
953+
// If this is an implicit `inout` expression we assume that
954+
// compiler knowns what it's doing.
955+
if (expr->isImplicit())
956+
return finish(true, expr);
957+
958+
if (TC.isExprBeingDiagnosed(ParentExpr) ||
959+
TC.isExprBeingDiagnosed(expr))
960+
return finish(true, expr);
961+
962+
auto parents = ParentExpr->getParentMap();
963+
964+
auto result = parents.find(expr);
965+
if (result != parents.end()) {
966+
auto *parent = result->getSecond();
967+
968+
if (isa<SequenceExpr>(parent))
969+
return finish(true, expr);
970+
971+
if (isa<TupleExpr>(parent) || isa<ParenExpr>(parent)) {
972+
auto call = parents.find(parent);
973+
if (call != parents.end() &&
974+
(isa<ApplyExpr>(call->getSecond()) ||
975+
isa<UnresolvedMemberExpr>(call->getSecond())))
976+
return finish(true, expr);
977+
}
978+
}
979+
980+
TC.diagnose(expr->getStartLoc(), diag::extraneous_address_of);
981+
return finish(false, nullptr);
982+
}
983+
944984
return finish(true, expr);
945985
}
946986

@@ -1699,7 +1739,7 @@ CleanupIllFormedExpressionRAII::~CleanupIllFormedExpressionRAII() {
16991739
/// Pre-check the expression, validating any types that occur in the
17001740
/// expression and folding sequence expressions.
17011741
bool TypeChecker::preCheckExpression(Expr *&expr, DeclContext *dc) {
1702-
PreCheckExpression preCheck(*this, dc);
1742+
PreCheckExpression preCheck(*this, dc, expr);
17031743
// Perform the pre-check.
17041744
if (auto result = expr->walk(preCheck)) {
17051745
expr = result;

test/Constraints/rdar40945329.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
class A {
4+
static var a: Int = 0
5+
static var b: Int = 42
6+
7+
func foo(_ ptr: UnsafeMutableRawPointer?) {
8+
switch ptr {
9+
case (&A.a)?: break
10+
case (&A.b)?: break
11+
default: break
12+
}
13+
}
14+
15+
func bar(_ ptr: UnsafeRawPointer) {
16+
switch ptr {
17+
case &A.a: break
18+
case &A.b: break
19+
default: break
20+
}
21+
}
22+
}

test/expr/expressions.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -814,8 +814,8 @@ public struct TestPropMethodOverloadGroup {
814814
// <rdar://problem/18496742> Passing ternary operator expression as inout crashes Swift compiler
815815
func inoutTests(_ arr: inout Int) {
816816
var x = 1, y = 2
817-
(true ? &x : &y) // expected-error 2 {{use of extraneous '&'}}
818-
let a = (true ? &x : &y) // expected-error 2 {{use of extraneous '&'}}
817+
(true ? &x : &y) // expected-error {{use of extraneous '&'}}
818+
let a = (true ? &x : &y) // expected-error {{use of extraneous '&'}}
819819

820820
inoutTests(true ? &x : &y) // expected-error {{use of extraneous '&'}}
821821

@@ -827,7 +827,7 @@ func inoutTests(_ arr: inout Int) {
827827
inoutTests(&x)
828828

829829
// <rdar://problem/17489894> inout not rejected as operand to assignment operator
830-
&x += y // expected-error {{'&' can only appear immediately in a call argument list}}}
830+
&x += y // expected-error {{'&' can only appear immediately in a call argument list}}
831831

832832
// <rdar://problem/23249098>
833833
func takeAny(_ x: Any) {}

0 commit comments

Comments
 (0)