Skip to content

Commit edd8d1c

Browse files
authored
Merge pull request swiftlang#61148 from tshortli/type-check-has-symbol
Sema: Type check #_hasSymbol()
2 parents 81c3a11 + 9c62890 commit edd8d1c

File tree

7 files changed

+308
-12
lines changed

7 files changed

+308
-12
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6577,5 +6577,17 @@ ERROR(type_wrapper_ignored_on_static_properties,none,
65776577
ERROR(type_wrapper_ignored_on_lazy_properties,none,
65786578
"%0 must not be used on lazy properties", (DeclAttribute))
65796579

6580+
//------------------------------------------------------------------------------
6581+
// MARK: #_hasSymbol
6582+
//------------------------------------------------------------------------------
6583+
6584+
WARNING(has_symbol_decl_must_be_weak,none,
6585+
"%0 %1 is not a weakly linked declaration",
6586+
(DescriptiveDeclKind, DeclName))
6587+
ERROR(has_symbol_invalid_expr,none,
6588+
"#_hasSymbol condition must refer to a declaration", ())
6589+
ERROR(has_symbol_unsupported_in_closures,none,
6590+
"#_hasSymbol is not supported in closures", ())
6591+
65806592
#define UNDEFINE_DIAGNOSTIC_MACROS
65816593
#include "DefineDiagnosticMacros.h"

include/swift/AST/Stmt.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,15 +397,16 @@ class alignas(8) PoundAvailableInfo final :
397397
///
398398
class PoundHasSymbolInfo final : public ASTAllocated<PoundHasSymbolInfo> {
399399
Expr *SymbolExpr;
400+
ConcreteDeclRef ReferencedDecl;
400401

401402
SourceLoc PoundLoc;
402403
SourceLoc LParenLoc;
403404
SourceLoc RParenLoc;
404405

405406
PoundHasSymbolInfo(SourceLoc PoundLoc, SourceLoc LParenLoc, Expr *SymbolExpr,
406407
SourceLoc RParenLoc)
407-
: SymbolExpr(SymbolExpr), PoundLoc(PoundLoc), LParenLoc(LParenLoc),
408-
RParenLoc(RParenLoc){};
408+
: SymbolExpr(SymbolExpr), ReferencedDecl(), PoundLoc(PoundLoc),
409+
LParenLoc(LParenLoc), RParenLoc(RParenLoc){};
409410

410411
public:
411412
static PoundHasSymbolInfo *create(ASTContext &Ctx, SourceLoc PoundLoc,
@@ -415,6 +416,9 @@ class PoundHasSymbolInfo final : public ASTAllocated<PoundHasSymbolInfo> {
415416
Expr *getSymbolExpr() const { return SymbolExpr; }
416417
void setSymbolExpr(Expr *E) { SymbolExpr = E; }
417418

419+
ConcreteDeclRef getReferencedDecl() { return ReferencedDecl; }
420+
void setReferencedDecl(ConcreteDeclRef CDR) { ReferencedDecl = CDR; }
421+
418422
SourceLoc getLParenLoc() const { return LParenLoc; }
419423
SourceLoc getRParenLoc() const { return RParenLoc; }
420424
SourceLoc getStartLoc() const { return PoundLoc; }

lib/Sema/CSGen.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4365,10 +4365,16 @@ bool ConstraintSystem::generateConstraints(StmtCondition condition,
43654365
for (const auto &condElement : condition) {
43664366
switch (condElement.getKind()) {
43674367
case StmtConditionElement::CK_Availability:
4368-
case StmtConditionElement::CK_HasSymbol:
43694368
// Nothing to do here.
43704369
continue;
43714370

4371+
case StmtConditionElement::CK_HasSymbol: {
4372+
ASTContext &ctx = getASTContext();
4373+
ctx.Diags.diagnose(condElement.getStartLoc(),
4374+
diag::has_symbol_unsupported_in_closures);
4375+
return true;
4376+
}
4377+
43724378
case StmtConditionElement::CK_Boolean: {
43734379
Expr *condExpr = condElement.getBoolean();
43744380
setContextualType(condExpr, TypeLoc::withoutLoc(boolTy), CTP_Condition);

lib/Sema/TypeCheckStmt.cpp

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,52 @@ LabeledStmt *swift::findBreakOrContinueStmtTarget(
419419
return nullptr;
420420
}
421421

422+
static Expr *getDeclRefProvidingExpressionForHasSymbol(Expr *E) {
423+
// Strip coercions, which are necessary in source to disambiguate overloaded
424+
// functions or generic functions, e.g.
425+
//
426+
// if #_hasSymbol(foo as () -> ()) { ... }
427+
//
428+
if (auto CE = dyn_cast<CoerceExpr>(E))
429+
return getDeclRefProvidingExpressionForHasSymbol(CE->getSubExpr());
430+
431+
// Unwrap curry thunks which are injected into the AST to wrap some forms of
432+
// unapplied method references, e.g.
433+
//
434+
// if #_hasSymbol(SomeStruct.foo(_:)) { ... }
435+
//
436+
if (auto ACE = dyn_cast<AutoClosureExpr>(E))
437+
return getDeclRefProvidingExpressionForHasSymbol(
438+
ACE->getUnwrappedCurryThunkExpr());
439+
440+
// Drill into the function expression for a DotSyntaxCallExpr. These sometimes
441+
// wrap or are wrapped by an AutoClosureExpr.
442+
//
443+
// if #_hasSymbol(someStruct.foo(_:)) { ... }
444+
//
445+
if (auto DSCE = dyn_cast<DotSyntaxCallExpr>(E))
446+
return getDeclRefProvidingExpressionForHasSymbol(DSCE->getFn());
447+
448+
return E;
449+
}
450+
451+
static ConcreteDeclRef
452+
getReferencedDeclForHasSymbolCondition(ASTContext &Context, Expr *E) {
453+
// Match DotSelfExprs (e.g. `SomeStruct.self`) when the type is static.
454+
if (auto DSE = dyn_cast<DotSelfExpr>(E)) {
455+
if (DSE->isStaticallyDerivedMetatype())
456+
return DSE->getType()->getMetatypeInstanceType()->getAnyNominal();
457+
}
458+
459+
if (auto declRefExpr = getDeclRefProvidingExpressionForHasSymbol(E)) {
460+
if (auto CDR = declRefExpr->getReferencedDecl())
461+
return CDR;
462+
}
463+
464+
Context.Diags.diagnose(E->getLoc(), diag::has_symbol_invalid_expr);
465+
return ConcreteDeclRef();
466+
}
467+
422468
bool TypeChecker::typeCheckStmtConditionElement(StmtConditionElement &elt,
423469
bool &isFalsable,
424470
DeclContext *dc) {
@@ -450,15 +496,29 @@ bool TypeChecker::typeCheckStmtConditionElement(StmtConditionElement &elt,
450496

451497
// Typecheck a #_hasSymbol condition.
452498
if (elt.getKind() == StmtConditionElement::CK_HasSymbol) {
499+
isFalsable = true;
500+
453501
auto Info = elt.getHasSymbolInfo();
454502
auto E = Info->getSymbolExpr();
455-
if (E) {
456-
// FIXME: Implement #_hasSymbol typechecking.
457-
(void)TypeChecker::typeCheckExpression(E, dc);
458-
Info->setSymbolExpr(E);
459-
}
460-
isFalsable = true;
503+
if (!E)
504+
return false;
461505

506+
auto exprTy = TypeChecker::typeCheckExpression(E, dc);
507+
Info->setSymbolExpr(E);
508+
509+
if (!exprTy)
510+
return true;
511+
512+
auto CDR = getReferencedDeclForHasSymbolCondition(Context, E);
513+
if (!CDR)
514+
return true;
515+
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);
462522
return false;
463523
}
464524

test/Parse/has_symbol.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
1-
// RUN: %target-typecheck-verify-swift
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/Library.swiftmodule -parse-as-library %t/Library.swift -enable-library-evolution
4+
// RUN: %target-swift-frontend -typecheck -verify %t/Client.swift -I %t
25

3-
func foo() {}
4-
func bar() {}
6+
// UNSUPPORTED: OS=windows-msvc
7+
8+
//--- Library.swift
9+
10+
public func foo() {}
11+
public func bar() {}
12+
13+
//--- Client.swift
14+
15+
@_weakLinked import Library
516

617
if #_hasSymbol(foo) {}
718
if #_hasSymbol(foo), #_hasSymbol(bar) {}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
public let global: Int = 0
2+
3+
public func noArgFunc() {}
4+
public func function(with argument: Int) {}
5+
public func throwingFunc() throws {}
6+
public func ambiguousFunc() {}
7+
public func ambiguousFunc() -> Int { return 0 }
8+
public func genericFunc<T: P>(_ t: T) {}
9+
10+
public protocol P {
11+
func requirement()
12+
func requirementWithDefaultImpl()
13+
}
14+
15+
extension P {
16+
public func requirementWithDefaultImpl() {}
17+
}
18+
19+
public struct S {
20+
public static var staticMember: Int = 0
21+
public static func staticFunc() {}
22+
23+
public var member: Int
24+
25+
public init(member: Int) {
26+
self.member = member
27+
}
28+
public func noArgsMethod() {}
29+
public func method(with argument: Int) {}
30+
public func genericFunc<T: P>(_ t: T) {}
31+
}
32+
33+
extension S: P {
34+
public func requirement() {}
35+
}
36+
37+
public struct GenericS<T: P> {
38+
public var member: T
39+
40+
public init(member: T) {
41+
self.member = member
42+
}
43+
public func noArgsMethod() {}
44+
public func method(with argument: T) {}
45+
}
46+
47+
public class C {
48+
public static var staticMember: Int = 0
49+
public class func classFunc() {}
50+
51+
public var member: Int
52+
53+
public init(member: Int) {
54+
self.member = member
55+
}
56+
public func noArgsMethod() {}
57+
public func method(with argument: Int) {}
58+
}
59+
60+
extension C: P {
61+
public func requirement() {}
62+
}
63+
64+
public enum E {
65+
case basicCase
66+
case payloadCase(_: S)
67+
68+
public func method() {}
69+
}

test/Sema/has_symbol.swift

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/has_symbol_helper.swiftmodule -parse-as-library %S/Inputs/has_symbol_helper.swift -enable-library-evolution
3+
// RUN: %target-typecheck-verify-swift -disable-availability-checking -I %t
4+
5+
// UNSUPPORTED: OS=windows-msvc
6+
7+
@_weakLinked import has_symbol_helper
8+
9+
func testGlobalFunctions() {
10+
if #_hasSymbol(noArgFunc) {}
11+
if #_hasSymbol(function(with:)) {}
12+
if #_hasSymbol(throwingFunc) {}
13+
if #_hasSymbol(ambiguousFunc) {} // expected-error {{ambiguous use of 'ambiguousFunc()'}}
14+
if #_hasSymbol(ambiguousFunc as () -> Int) {}
15+
if #_hasSymbol(genericFunc(_:) as (S) -> Void) {}
16+
}
17+
18+
func testVars() {
19+
if #_hasSymbol(global) {}
20+
if #_hasSymbol(global as Int) {}
21+
}
22+
23+
func testStruct(_ s: S) {
24+
if #_hasSymbol(s.member) {}
25+
if #_hasSymbol(s.noArgsMethod) {}
26+
if #_hasSymbol(s.method(with:)) {}
27+
if #_hasSymbol(s.genericFunc(_:)) {} // expected-error {{generic parameter 'T' could not be inferred}}
28+
if #_hasSymbol(s.genericFunc(_:) as (S) -> ()) {}
29+
if #_hasSymbol(s.requirement) {}
30+
if #_hasSymbol(s.requirementWithDefaultImpl) {}
31+
32+
if #_hasSymbol(S.staticFunc) {}
33+
if #_hasSymbol(S.staticMember) {}
34+
if #_hasSymbol(S.init(member:)) {}
35+
}
36+
37+
func testGenericStruct(_ s: GenericS<S>) {
38+
if #_hasSymbol(s.member) {}
39+
if #_hasSymbol(s.noArgsMethod) {}
40+
if #_hasSymbol(s.method(with:)) {}
41+
}
42+
43+
func testClass(_ c: C) {
44+
if #_hasSymbol(c.member) {}
45+
if #_hasSymbol(c.noArgsMethod) {}
46+
if #_hasSymbol(c.method(with:)) {}
47+
if #_hasSymbol(c.requirement) {}
48+
if #_hasSymbol(c.requirementWithDefaultImpl) {}
49+
50+
if #_hasSymbol(C.classFunc) {}
51+
if #_hasSymbol(C.staticMember) {}
52+
if #_hasSymbol(C.init(member:)) {}
53+
}
54+
55+
func testEnum(_ e: E) {
56+
if #_hasSymbol(E.basicCase) {}
57+
if #_hasSymbol(E.payloadCase) {}
58+
if #_hasSymbol(E.payloadCase(_:)) {}
59+
if #_hasSymbol(e.method) {}
60+
}
61+
62+
func testMetatypes() {
63+
if #_hasSymbol(P.self) {}
64+
if #_hasSymbol(S.self) {}
65+
if #_hasSymbol(GenericS.self) {} // expected-error {{generic parameter 'T' could not be inferred}} expected-note {{explicitly specify the generic arguments to fix this issue}}
66+
if #_hasSymbol(GenericS<S>.self) {}
67+
if #_hasSymbol(C.self) {}
68+
if #_hasSymbol(E.self) {}
69+
}
70+
71+
var localGlobal: Int = 0
72+
73+
func localFunc() {}
74+
75+
struct LocalStruct {
76+
var member: Int = 0
77+
}
78+
79+
protocol LocalProtocol {}
80+
81+
enum LocalEnum {
82+
case a
83+
}
84+
85+
func testNotWeakDeclDiagnostics(_ s: LocalStruct) {
86+
if #_hasSymbol(localFunc) {} // expected-warning {{global function 'localFunc()' is not a weakly linked declaration}}
87+
if #_hasSymbol(localGlobal) {} // expected-warning {{var 'localGlobal' is not a weakly linked declaration}}
88+
if #_hasSymbol(s) {} // expected-warning {{parameter 's' is not a weakly linked declaration}}
89+
if #_hasSymbol(s.member) {} // expected-warning {{property 'member' is not a weakly linked declaration}}
90+
if #_hasSymbol(LocalEnum.a) {} // expected-warning {{enum case 'a' is not a weakly linked declaration}}
91+
if #_hasSymbol(LocalStruct.self) {} // expected-warning {{struct 'LocalStruct' is not a weakly linked declaration}}
92+
if #_hasSymbol(LocalProtocol.self) {} // expected-warning {{protocol 'LocalProtocol' is not a weakly linked declaration}}
93+
}
94+
95+
func testInvalidExpressionsDiagnostics() {
96+
if #_hasSymbol(noArgFunc()) {} // expected-error {{#_hasSymbol condition must refer to a declaration}}
97+
if #_hasSymbol(global - 1) {} // expected-error {{#_hasSymbol condition must refer to a declaration}}
98+
if #_hasSymbol(S.staticFunc()) {} // expected-error {{#_hasSymbol condition must refer to a declaration}}
99+
if #_hasSymbol(C.classFunc()) {} // expected-error {{#_hasSymbol condition must refer to a declaration}}
100+
if #_hasSymbol(1 as Int) {} // expected-error {{#_hasSymbol condition must refer to a declaration}}
101+
if #_hasSymbol(1 as S) {} // expected-error {{cannot convert value of type 'Int' to type 'S' in coercion}}
102+
}
103+
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+
}
113+
}
114+
115+
protocol View {}
116+
117+
@resultBuilder struct ViewBuilder {
118+
static func buildBlock<Content>(_ content: Content) -> Content where Content : View { fatalError() }
119+
static func buildEither<Content>(first content: Content) -> Content where Content : View { fatalError() }
120+
static func buildEither<Content>(second content: Content) -> Content where Content : View { fatalError() }
121+
}
122+
123+
struct Image : View {
124+
}
125+
126+
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+
}
133+
}
134+
}

0 commit comments

Comments
 (0)