Skip to content

Commit 034e53a

Browse files
committed
Sema: Accept if #_hasSymbol() conditions in closure contexts.
Resolves rdar://100129165
1 parent af276b7 commit 034e53a

File tree

7 files changed

+135
-43
lines changed

7 files changed

+135
-43
lines changed

lib/AST/ASTWalker.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1394,8 +1394,17 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
13941394
for (auto &elt : C) {
13951395
switch (elt.getKind()) {
13961396
case StmtConditionElement::CK_Availability:
1397-
case StmtConditionElement::CK_HasSymbol:
13981397
break;
1398+
case StmtConditionElement::CK_HasSymbol: {
1399+
auto E = elt.getHasSymbolInfo()->getSymbolExpr();
1400+
if (!E)
1401+
return true;
1402+
E = doIt(E);
1403+
if (!E)
1404+
return true;
1405+
elt.getHasSymbolInfo()->setSymbolExpr(E);
1406+
break;
1407+
}
13991408
case StmtConditionElement::CK_Boolean: {
14001409
auto E = elt.getBoolean();
14011410
// Walk an expression condition normally.

lib/Sema/CSApply.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8923,9 +8923,23 @@ ExprWalker::rewriteTarget(SolutionApplicationTarget target) {
89238923
for (auto &condElement : *stmtCondition) {
89248924
switch (condElement.getKind()) {
89258925
case StmtConditionElement::CK_Availability:
8926-
case StmtConditionElement::CK_HasSymbol:
89278926
continue;
89288927

8928+
case StmtConditionElement::CK_HasSymbol: {
8929+
ConstraintSystem &cs = solution.getConstraintSystem();
8930+
auto target = *cs.getSolutionApplicationTarget(&condElement);
8931+
auto resolvedTarget = rewriteTarget(target);
8932+
if (!resolvedTarget)
8933+
return None;
8934+
8935+
auto info = condElement.getHasSymbolInfo();
8936+
auto rewrittenExpr = resolvedTarget->getAsExpr();
8937+
info->setSymbolExpr(rewrittenExpr);
8938+
info->setReferencedDecl(
8939+
TypeChecker::getReferencedDeclForHasSymbolCondition(rewrittenExpr));
8940+
continue;
8941+
}
8942+
89298943
case StmtConditionElement::CK_Boolean: {
89308944
auto condExpr = condElement.getBoolean();
89318945
auto finalCondExpr = condExpr->walk(*this);

lib/Sema/CSGen.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4369,10 +4369,15 @@ bool ConstraintSystem::generateConstraints(StmtCondition condition,
43694369
continue;
43704370

43714371
case StmtConditionElement::CK_HasSymbol: {
4372-
ASTContext &ctx = getASTContext();
4373-
ctx.Diags.diagnose(condElement.getStartLoc(),
4374-
diag::has_symbol_unsupported_in_closures);
4375-
return true;
4372+
Expr *symbolExpr = condElement.getHasSymbolInfo()->getSymbolExpr();
4373+
auto target = SolutionApplicationTarget(symbolExpr, dc, CTP_Unused,
4374+
Type(), /*isDiscarded=*/false);
4375+
4376+
if (generateConstraints(target, FreeTypeVariableBinding::Disallow))
4377+
return true;
4378+
4379+
setSolutionApplicationTarget(&condElement, target);
4380+
continue;
43764381
}
43774382

43784383
case StmtConditionElement::CK_Boolean: {

lib/Sema/MiscDiagnostics.cpp

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4302,6 +4302,50 @@ checkImplicitPromotionsInCondition(const StmtConditionElement &cond,
43024302
}
43034303
}
43044304

4305+
/// Perform MiscDiagnostics for the conditions belonging to a \c
4306+
/// LabeledConditionalStmt.
4307+
static void checkLabeledStmtConditions(ASTContext &ctx,
4308+
const LabeledConditionalStmt *stmt,
4309+
DeclContext *DC) {
4310+
for (auto elt : stmt->getCond()) {
4311+
// Check for implicit optional promotions in stmt-condition patterns.
4312+
checkImplicitPromotionsInCondition(elt, ctx);
4313+
4314+
switch (elt.getKind()) {
4315+
case StmtConditionElement::CK_Boolean:
4316+
case StmtConditionElement::CK_PatternBinding:
4317+
case StmtConditionElement::CK_Availability:
4318+
break;
4319+
4320+
case StmtConditionElement::CK_HasSymbol: {
4321+
auto info = elt.getHasSymbolInfo();
4322+
auto symbolExpr = info->getSymbolExpr();
4323+
if (!symbolExpr)
4324+
break;
4325+
4326+
if (!symbolExpr->getType())
4327+
break;
4328+
4329+
if (auto decl = info->getReferencedDecl().getDecl()) {
4330+
// `if #_hasSymbol(someStronglyLinkedSymbol)` is functionally a no-op
4331+
// and may indicate the developer has mis-identified the declaration
4332+
// they want to check (or forgot to import the module weakly).
4333+
if (!decl->isWeakImported(DC->getParentModule())) {
4334+
ctx.Diags.diagnose(symbolExpr->getLoc(),
4335+
diag::has_symbol_decl_must_be_weak,
4336+
decl->getDescriptiveKind(), decl->getName());
4337+
}
4338+
} else {
4339+
// Diagnose because we weren't able to interpret the expression as one
4340+
// that uniquely identifies a single declaration.
4341+
ctx.Diags.diagnose(symbolExpr->getLoc(), diag::has_symbol_invalid_expr);
4342+
}
4343+
break;
4344+
}
4345+
}
4346+
}
4347+
}
4348+
43054349
static void diagnoseUnintendedOptionalBehavior(const Expr *E,
43064350
const DeclContext *DC) {
43074351
if (!E || isa<ErrorExpr>(E) || !E->getType())
@@ -5290,11 +5334,9 @@ void swift::performStmtDiagnostics(const Stmt *S, DeclContext *DC) {
52905334
checkSwitch(ctx, switchStmt, DC);
52915335

52925336
checkStmtConditionTrailingClosure(ctx, S);
5293-
5294-
// Check for implicit optional promotions in stmt-condition patterns.
5337+
52955338
if (auto *lcs = dyn_cast<LabeledConditionalStmt>(S))
5296-
for (const auto &elt : lcs->getCond())
5297-
checkImplicitPromotionsInCondition(elt, ctx);
5339+
checkLabeledStmtConditions(ctx, lcs, DC);
52985340

52995341
if (!ctx.LangOpts.DisableAvailabilityChecking)
53005342
diagnoseStmtAvailability(S, const_cast<DeclContext*>(DC));

lib/Sema/TypeCheckStmt.cpp

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -448,8 +448,7 @@ static Expr *getDeclRefProvidingExpressionForHasSymbol(Expr *E) {
448448
return E;
449449
}
450450

451-
static ConcreteDeclRef
452-
getReferencedDeclForHasSymbolCondition(ASTContext &Context, Expr *E) {
451+
ConcreteDeclRef TypeChecker::getReferencedDeclForHasSymbolCondition(Expr *E) {
453452
// Match DotSelfExprs (e.g. `SomeStruct.self`) when the type is static.
454453
if (auto DSE = dyn_cast<DotSelfExpr>(E)) {
455454
if (DSE->isStaticallyDerivedMetatype())
@@ -461,7 +460,6 @@ getReferencedDeclForHasSymbolCondition(ASTContext &Context, Expr *E) {
461460
return CDR;
462461
}
463462

464-
Context.Diags.diagnose(E->getLoc(), diag::has_symbol_invalid_expr);
465463
return ConcreteDeclRef();
466464
}
467465

@@ -506,20 +504,10 @@ bool TypeChecker::typeCheckStmtConditionElement(StmtConditionElement &elt,
506504
auto exprTy = TypeChecker::typeCheckExpression(E, dc);
507505
Info->setSymbolExpr(E);
508506

509-
if (!exprTy)
510-
return true;
511-
512-
auto CDR = getReferencedDeclForHasSymbolCondition(Context, E);
513-
if (!CDR)
514-
return true;
507+
if (exprTy)
508+
Info->setReferencedDecl(getReferencedDeclForHasSymbolCondition(E));
515509

516-
auto decl = CDR.getDecl();
517-
if (!decl->isWeakImported(dc->getParentModule())) {
518-
Context.Diags.diagnose(E->getLoc(), diag::has_symbol_decl_must_be_weak,
519-
decl->getDescriptiveKind(), decl->getName());
520-
}
521-
Info->setReferencedDecl(CDR);
522-
return false;
510+
return !exprTy;
523511
}
524512

525513
if (auto E = elt.getBooleanOrNull()) {

lib/Sema/TypeChecker.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,10 @@ Expr *substituteInputSugarTypeForResult(ApplyExpr *E);
441441
bool typeCheckStmtConditionElement(StmtConditionElement &elt, bool &isFalsable,
442442
DeclContext *dc);
443443

444+
/// Returns the unique decl ref identified by the expr according to the
445+
/// requirements of the \c #_hasSymbol() condition type.
446+
ConcreteDeclRef getReferencedDeclForHasSymbolCondition(Expr *E);
447+
444448
void typeCheckASTNode(ASTNode &node, DeclContext *DC,
445449
bool LeaveBodyUnchecked = false);
446450

test/Sema/has_symbol.swift

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func testNotWeakDeclDiagnostics(_ s: LocalStruct) {
9393
}
9494

9595
func testInvalidExpressionsDiagnostics() {
96+
if #_hasSymbol(unknownDecl) {} // expected-error {{cannot find 'unknownDecl' in scope}}
9697
if #_hasSymbol(noArgFunc()) {} // expected-error {{#_hasSymbol condition must refer to a declaration}}
9798
if #_hasSymbol(global - 1) {} // expected-error {{#_hasSymbol condition must refer to a declaration}}
9899
if #_hasSymbol(S.staticFunc()) {} // expected-error {{#_hasSymbol condition must refer to a declaration}}
@@ -101,15 +102,36 @@ func testInvalidExpressionsDiagnostics() {
101102
if #_hasSymbol(1 as S) {} // expected-error {{cannot convert value of type 'Int' to type 'S' in coercion}}
102103
}
103104

104-
func testMultiStatementClosure() {
105-
let _: () -> Void = { // expected-error {{unable to infer closure type in the current context}}
106-
if #_hasSymbol(global) {} // expected-error 2 {{#_hasSymbol is not supported in closures}}
107-
}
108-
109-
let _: () -> Void = { // expected-error {{unable to infer closure type in the current context}}
110-
if #_hasSymbol(global) {} // expected-error 2 {{#_hasSymbol is not supported in closures}}
111-
localFunc()
112-
}
105+
func testGuard() {
106+
guard #_hasSymbol(global) else { return }
107+
guard #_hasSymbol(unknownDecl) else { return } // expected-error {{cannot find 'unknownDecl' in scope}}
108+
guard #_hasSymbol(localFunc) else { return } // expected-warning {{global function 'localFunc()' is not a weakly linked declaration}}
109+
}
110+
111+
func testWhile() {
112+
while #_hasSymbol(global) { break }
113+
while #_hasSymbol(unknownDecl) { break } // expected-error {{cannot find 'unknownDecl' in scope}}
114+
while #_hasSymbol(localFunc) { break } // expected-warning {{global function 'localFunc()' is not a weakly linked declaration}}
115+
}
116+
117+
func doIt(_ closure: () -> ()) {
118+
closure()
119+
}
120+
121+
func testClosure() {
122+
doIt { if #_hasSymbol(global) {} }
123+
doIt { if #_hasSymbol(noArgFunc) {} }
124+
doIt { if #_hasSymbol(ambiguousFunc as () -> Int) {} }
125+
doIt { if #_hasSymbol(S.self) {} }
126+
doIt { if #_hasSymbol(ambiguousFunc) {} } // expected-error {{ambiguous use of 'ambiguousFunc()'}}
127+
doIt { if #_hasSymbol(localFunc) {} } // expected-warning {{global function 'localFunc()' is not a weakly linked declaration}}
128+
doIt { if #_hasSymbol(unknownDecl) {} } // expected-error {{cannot find 'unknownDecl' in scope}}
129+
doIt { if #_hasSymbol(noArgFunc()) {} } // expected-error {{#_hasSymbol condition must refer to a declaration}}
130+
doIt { if #_hasSymbol(global - 1) {} } // expected-error {{#_hasSymbol condition must refer to a declaration}}
131+
doIt { if #_hasSymbol(S.staticFunc()) {} } // expected-error {{#_hasSymbol condition must refer to a declaration}}
132+
doIt { if #_hasSymbol(C.classFunc()) {} } // expected-error {{#_hasSymbol condition must refer to a declaration}}
133+
doIt { if #_hasSymbol(1 as Int) {} } // expected-error {{#_hasSymbol condition must refer to a declaration}}
134+
doIt { if #_hasSymbol(1 as S) {} } // expected-error {{cannot convert value of type 'Int' to type 'S' in coercion}}
113135
}
114136

115137
protocol View {}
@@ -120,15 +142,23 @@ protocol View {}
120142
static func buildEither<Content>(second content: Content) -> Content where Content : View { fatalError() }
121143
}
122144

123-
struct Image : View {
124-
}
145+
struct Image : View {}
125146

126147
struct MyView {
127-
@ViewBuilder var body: some View {
128-
if #_hasSymbol(global) { // expected-error {{#_hasSymbol is not supported in closures}}
129-
Image()
130-
} else {
131-
Image()
132-
}
148+
let image = Image()
149+
150+
@ViewBuilder var globalView: some View {
151+
if #_hasSymbol(global) { image }
152+
else { image }
153+
}
154+
155+
@ViewBuilder var ambiguousFuncView: some View {
156+
if #_hasSymbol(ambiguousFunc) { image } // expected-error {{ambiguous use of 'ambiguousFunc()'}}
157+
else { image }
158+
}
159+
160+
@ViewBuilder var localFuncView: some View {
161+
if #_hasSymbol(localFunc) { image } // expected-warning {{global function 'localFunc()' is not a weakly linked declaration}}
162+
else { image }
133163
}
134164
}

0 commit comments

Comments
 (0)