Skip to content

Commit 3149e2d

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.
1 parent 82025f8 commit 3149e2d

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
@@ -858,6 +858,8 @@ namespace {
858858
TypeChecker &TC;
859859
DeclContext *DC;
860860

861+
Expr *ParentExpr;
862+
861863
/// A stack of expressions being walked, used to determine where to
862864
/// insert RebindSelfInConstructorExpr nodes.
863865
llvm::SmallVector<Expr *, 8> ExprStack;
@@ -886,7 +888,8 @@ namespace {
886888
void resolveKeyPathExpr(KeyPathExpr *KPE);
887889

888890
public:
889-
PreCheckExpression(TypeChecker &tc, DeclContext *dc) : TC(tc), DC(dc) { }
891+
PreCheckExpression(TypeChecker &tc, DeclContext *dc, Expr *parent)
892+
: TC(tc), DC(dc), ParentExpr(parent) {}
890893

891894
bool walkToClosureExprPre(ClosureExpr *expr);
892895

@@ -947,6 +950,43 @@ namespace {
947950
return finish(true, expr);
948951
}
949952

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

@@ -1683,7 +1723,7 @@ CleanupIllFormedExpressionRAII::~CleanupIllFormedExpressionRAII() {
16831723
/// Pre-check the expression, validating any types that occur in the
16841724
/// expression and folding sequence expressions.
16851725
bool TypeChecker::preCheckExpression(Expr *&expr, DeclContext *dc) {
1686-
PreCheckExpression preCheck(*this, dc);
1726+
PreCheckExpression preCheck(*this, dc, expr);
16871727
// Perform the pre-check.
16881728
if (auto result = expr->walk(preCheck)) {
16891729
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)