Skip to content

Commit f5f1d3c

Browse files
committed
AST: Parse #_hasSymbol
Introduce the compiler directive `#_hasSymbol` which will be used to detect whether weakly linked symbols are present at runtime. It is intended for use in combination with `@_weakLinked import` or `-weak-link-at-target`. ``` if #_hasSymbol(foo(_:)) { foo(42) } ``` Parsing only; SILGen is coming in a later commit. Resolves rdar://99342017
1 parent 1d52c9c commit f5f1d3c

17 files changed

+259
-54
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1280,7 +1280,7 @@ ERROR(string_literal_no_atsign,none,
12801280
ERROR(invalid_float_literal_missing_leading_zero,none,
12811281
"'.%0' is not a valid floating point literal; it must be written '0.%0'",
12821282
(StringRef))
1283-
ERROR(availability_query_outside_if_stmt_guard, none,
1283+
ERROR(special_condition_outside_if_stmt_guard, none,
12841284
"%0 may only be used as condition of an 'if', 'guard'"
12851285
" or 'while' statement", (StringRef))
12861286

@@ -1935,6 +1935,19 @@ ERROR(availability_cannot_be_mixed,none,
19351935
ERROR(false_available_is_called_unavailable,none,
19361936
"#available cannot be used as an expression, did you mean to use '#unavailable'?", ())
19371937

1938+
//------------------------------------------------------------------------------
1939+
// MARK: #_hasSymbol query parsing diagnostics
1940+
//------------------------------------------------------------------------------
1941+
1942+
ERROR(has_symbol_expected_lparen,PointsToFirstBadToken,
1943+
"expected '(' in #_hasSymbol directive", ())
1944+
1945+
ERROR(has_symbol_expected_expr,PointsToFirstBadToken,
1946+
"expected expression in #_hasSymbol", ())
1947+
1948+
ERROR(has_symbol_expected_rparen,PointsToFirstBadToken,
1949+
"expected ')' in #_hasSymbol condition", ())
1950+
19381951
ERROR(attr_requires_concurrency, none,
19391952
"'%0' %select{attribute|modifier}1 is only valid when experimental "
19401953
"concurrency is enabled",

include/swift/AST/Stmt.h

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,39 @@ class alignas(8) PoundAvailableInfo final :
389389
bool isUnavailability() const { return _isUnavailability; }
390390
};
391391

392+
/// An expression that guards execution based on whether the symbols for the
393+
/// declaration identified by the given expression are non-null at run-time, e.g.
394+
///
395+
/// if #_hasSymbol(foo(_:)) { foo(42) }
396+
///
397+
class PoundHasSymbolInfo final : public ASTAllocated<PoundHasSymbolInfo> {
398+
Expr *SymbolExpr;
399+
400+
SourceLoc PoundLoc;
401+
SourceLoc LParenLoc;
402+
SourceLoc RParenLoc;
403+
404+
PoundHasSymbolInfo(SourceLoc PoundLoc, SourceLoc LParenLoc, Expr *SymbolExpr,
405+
SourceLoc RParenLoc)
406+
: SymbolExpr(SymbolExpr), PoundLoc(PoundLoc), LParenLoc(LParenLoc),
407+
RParenLoc(RParenLoc){};
408+
409+
public:
410+
static PoundHasSymbolInfo *create(ASTContext &Ctx, SourceLoc PoundLoc,
411+
SourceLoc LParenLoc, Expr *SymbolExpr,
412+
SourceLoc RParenLoc);
413+
414+
Expr *getSymbolExpr() const { return SymbolExpr; }
415+
void setSymbolExpr(Expr *E) { SymbolExpr = E; }
416+
417+
SourceLoc getLParenLoc() const { return LParenLoc; }
418+
SourceLoc getRParenLoc() const { return RParenLoc; }
419+
SourceLoc getStartLoc() const { return PoundLoc; }
420+
SourceLoc getEndLoc() const { return RParenLoc; }
421+
SourceRange getSourceRange() const {
422+
return SourceRange(getStartLoc(), getEndLoc());
423+
}
424+
};
392425

393426
/// This represents an entry in an "if" or "while" condition. Pattern bindings
394427
/// can bind any number of names in the pattern binding decl, and may have an
@@ -413,47 +446,55 @@ class alignas(1 << PatternAlignInBits) StmtConditionElement {
413446
/// to this as an 'implicit' pattern.
414447
Pattern *ThePattern = nullptr;
415448

416-
/// This is either the boolean condition, the initializer for a pattern
417-
/// binding, or the #available information.
418-
llvm::PointerUnion<PoundAvailableInfo*, Expr *> CondInitOrAvailable;
449+
/// This is either the boolean condition, the #available information, or
450+
/// the #_hasSymbol information.
451+
llvm::PointerUnion<Expr *, PoundAvailableInfo *, PoundHasSymbolInfo *>
452+
Condition;
419453

420454
public:
421455
StmtConditionElement() {}
422-
StmtConditionElement(SourceLoc IntroducerLoc, Pattern *ThePattern,
423-
Expr *Init)
424-
: IntroducerLoc(IntroducerLoc), ThePattern(ThePattern),
425-
CondInitOrAvailable(Init) {}
426-
StmtConditionElement(Expr *cond) : CondInitOrAvailable(cond) {}
456+
StmtConditionElement(SourceLoc IntroducerLoc, Pattern *ThePattern, Expr *Init)
457+
: IntroducerLoc(IntroducerLoc), ThePattern(ThePattern), Condition(Init) {}
458+
StmtConditionElement(Expr *cond) : Condition(cond) {}
459+
460+
StmtConditionElement(PoundAvailableInfo *Info) : Condition(Info) {}
461+
462+
StmtConditionElement(PoundHasSymbolInfo *Info) : Condition(Info) {}
427463

428-
StmtConditionElement(PoundAvailableInfo *Info) : CondInitOrAvailable(Info) {}
429-
430464
SourceLoc getIntroducerLoc() const { return IntroducerLoc; }
431465
void setIntroducerLoc(SourceLoc loc) { IntroducerLoc = loc; }
432466

433467
/// ConditionKind - This indicates the sort of condition this is.
434468
enum ConditionKind {
435469
CK_Boolean,
436470
CK_PatternBinding,
437-
CK_Availability
471+
CK_Availability,
472+
CK_HasSymbol,
438473
};
439474

440475
ConditionKind getKind() const {
441-
if (ThePattern) return CK_PatternBinding;
442-
return CondInitOrAvailable.is<Expr*>() ? CK_Boolean : CK_Availability;
476+
if (ThePattern)
477+
return CK_PatternBinding;
478+
if (Condition.is<Expr *>())
479+
return CK_Boolean;
480+
if (Condition.is<PoundAvailableInfo *>())
481+
return CK_Availability;
482+
assert(Condition.is<PoundHasSymbolInfo *>());
483+
return CK_HasSymbol;
443484
}
444485

445486
/// Boolean Condition Accessors.
446487
Expr *getBooleanOrNull() const {
447-
return getKind() == CK_Boolean ? CondInitOrAvailable.get<Expr*>() : nullptr;
488+
return getKind() == CK_Boolean ? Condition.get<Expr *>() : nullptr;
448489
}
449490

450491
Expr *getBoolean() const {
451492
assert(getKind() == CK_Boolean && "Not a condition");
452-
return CondInitOrAvailable.get<Expr*>();
493+
return Condition.get<Expr *>();
453494
}
454495
void setBoolean(Expr *E) {
455496
assert(getKind() == CK_Boolean && "Not a condition");
456-
CondInitOrAvailable = E;
497+
Condition = E;
457498
}
458499

459500
/// Pattern Binding Accessors.
@@ -473,22 +514,33 @@ class alignas(1 << PatternAlignInBits) StmtConditionElement {
473514

474515
Expr *getInitializer() const {
475516
assert(getKind() == CK_PatternBinding && "Not a pattern binding condition");
476-
return CondInitOrAvailable.get<Expr*>();
517+
return Condition.get<Expr *>();
477518
}
478519
void setInitializer(Expr *E) {
479520
assert(getKind() == CK_PatternBinding && "Not a pattern binding condition");
480-
CondInitOrAvailable = E;
521+
Condition = E;
481522
}
482523

483524
// Availability Accessors
484525
PoundAvailableInfo *getAvailability() const {
485526
assert(getKind() == CK_Availability && "Not an #available condition");
486-
return CondInitOrAvailable.get<PoundAvailableInfo*>();
527+
return Condition.get<PoundAvailableInfo *>();
487528
}
488529

489530
void setAvailability(PoundAvailableInfo *Info) {
490531
assert(getKind() == CK_Availability && "Not an #available condition");
491-
CondInitOrAvailable = Info;
532+
Condition = Info;
533+
}
534+
535+
// #_hasSymbol Accessors
536+
PoundHasSymbolInfo *getHasSymbolInfo() const {
537+
assert(getKind() == CK_HasSymbol && "Not a #_hasSymbol condition");
538+
return Condition.get<PoundHasSymbolInfo *>();
539+
}
540+
541+
void setHasSymbolInfo(PoundHasSymbolInfo *Info) {
542+
assert(getKind() == CK_HasSymbol && "Not a #_hasSymbol condition");
543+
Condition = Info;
492544
}
493545

494546
SourceLoc getStartLoc() const;

include/swift/Parse/Parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1839,6 +1839,7 @@ class Parser {
18391839
ParserStatus parseStmtCondition(StmtCondition &Result, Diag<> ID,
18401840
StmtKind ParentKind);
18411841
ParserResult<PoundAvailableInfo> parseStmtConditionPoundAvailable();
1842+
ParserResult<PoundHasSymbolInfo> parseStmtConditionPoundHasSymbol();
18421843
ParserResult<Stmt> parseStmtIf(LabeledStmtInfo LabelInfo,
18431844
bool IfWasImplicitlyInserted = false);
18441845
ParserResult<Stmt> parseStmtGuard();

lib/AST/ASTDumper.cpp

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,15 @@ static void printGenericParameters(raw_ostream &OS, GenericParamList *Params) {
123123
Params->print(OS);
124124
}
125125

126+
static void printSourceRange(raw_ostream &OS, const SourceRange R,
127+
const ASTContext &Ctx) {
128+
if (!R.isValid())
129+
return;
130+
131+
PrintWithColorRAII(OS, RangeColor) << " range=";
132+
R.print(PrintWithColorRAII(OS, RangeColor).getOS(), Ctx.SourceMgr,
133+
/*PrintText=*/false);
134+
}
126135

127136
static StringRef
128137
getSILFunctionTypeRepresentationString(SILFunctionType::Representation value) {
@@ -497,12 +506,7 @@ namespace {
497506
if (D->isHoisted())
498507
PrintWithColorRAII(OS, DeclModifierColor) << " hoisted";
499508

500-
auto R = D->getSourceRange();
501-
if (R.isValid()) {
502-
PrintWithColorRAII(OS, RangeColor) << " range=";
503-
R.print(PrintWithColorRAII(OS, RangeColor).getOS(),
504-
D->getASTContext().SourceMgr, /*PrintText=*/false);
505-
}
509+
printSourceRange(OS, D->getSourceRange(), D->getASTContext());
506510

507511
if (D->TrailingSemiLoc.isValid())
508512
PrintWithColorRAII(OS, DeclModifierColor) << " trailing_semi";
@@ -1013,12 +1017,7 @@ namespace {
10131017
ctx = &params->get(0)->getASTContext();
10141018

10151019
if (ctx) {
1016-
auto R = params->getSourceRange();
1017-
if (R.isValid()) {
1018-
PrintWithColorRAII(OS, RangeColor) << " range=";
1019-
R.print(PrintWithColorRAII(OS, RangeColor).getOS(),
1020-
ctx->SourceMgr, /*PrintText=*/false);
1021-
}
1020+
printSourceRange(OS, params->getSourceRange(), *ctx);
10221021
}
10231022

10241023
Indent += 2;
@@ -1483,6 +1482,18 @@ class PrintStmt : public StmtVisitor<PrintStmt> {
14831482
PrintWithColorRAII(OS, ParenthesisColor) << ")";
14841483
Indent -= 2;
14851484
break;
1485+
case StmtConditionElement::CK_HasSymbol:
1486+
Indent += 2;
1487+
OS.indent(Indent);
1488+
PrintWithColorRAII(OS, ParenthesisColor) << '(';
1489+
OS << "#_hasSymbol";
1490+
if (Ctx)
1491+
printSourceRange(OS, C.getSourceRange(), *Ctx);
1492+
OS << "\n";
1493+
printRec(C.getHasSymbolInfo()->getSymbolExpr());
1494+
PrintWithColorRAII(OS, ParenthesisColor) << ")";
1495+
Indent -= 2;
1496+
break;
14861497
}
14871498
}
14881499

@@ -1494,14 +1505,8 @@ class PrintStmt : public StmtVisitor<PrintStmt> {
14941505
if (S->isImplicit())
14951506
OS << " implicit";
14961507

1497-
if (Ctx) {
1498-
auto R = S->getSourceRange();
1499-
if (R.isValid()) {
1500-
PrintWithColorRAII(OS, RangeColor) << " range=";
1501-
R.print(PrintWithColorRAII(OS, RangeColor).getOS(),
1502-
Ctx->SourceMgr, /*PrintText=*/false);
1503-
}
1504-
}
1508+
if (Ctx)
1509+
printSourceRange(OS, S->getSourceRange(), *Ctx);
15051510

15061511
if (S->TrailingSemiLoc.isValid())
15071512
OS << " trailing_semi";
@@ -1555,9 +1560,10 @@ class PrintStmt : public StmtVisitor<PrintStmt> {
15551560

15561561
void visitIfStmt(IfStmt *S) {
15571562
printCommon(S, "if_stmt") << '\n';
1558-
for (auto elt : S->getCond())
1563+
for (auto elt : S->getCond()) {
15591564
printRec(elt);
1560-
OS << '\n';
1565+
OS << "\n";
1566+
}
15611567
printRec(S->getThenStmt());
15621568
if (S->getElseStmt()) {
15631569
OS << '\n';
@@ -1826,12 +1832,7 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
18261832
L.print(PrintWithColorRAII(OS, LocationColor).getOS(), Ctx.SourceMgr);
18271833
}
18281834

1829-
auto R = E->getSourceRange();
1830-
if (R.isValid()) {
1831-
PrintWithColorRAII(OS, RangeColor) << " range=";
1832-
R.print(PrintWithColorRAII(OS, RangeColor).getOS(),
1833-
Ctx.SourceMgr, /*PrintText=*/false);
1834-
}
1835+
printSourceRange(OS, E->getSourceRange(), Ctx);
18351836
}
18361837

18371838
if (E->TrailingSemiLoc.isValid())

lib/AST/ASTScopeCreation.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,7 @@ ASTScopeImpl *LabeledConditionalStmtScope::createNestedConditionalClauseScopes(
11671167
for (auto &sec : stmt->getCond()) {
11681168
switch (sec.getKind()) {
11691169
case StmtConditionElement::CK_Availability:
1170+
case StmtConditionElement::CK_HasSymbol:
11701171
break;
11711172
case StmtConditionElement::CK_Boolean:
11721173
scopeCreator.addToScopeTree(sec.getBoolean(), insertionPoint);

lib/AST/ASTVerifier.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1030,7 +1030,9 @@ class Verifier : public ASTWalker {
10301030

10311031
void checkConditionElement(const StmtConditionElement &elt) {
10321032
switch (elt.getKind()) {
1033-
case StmtConditionElement::CK_Availability: break;
1033+
case StmtConditionElement::CK_Availability:
1034+
case StmtConditionElement::CK_HasSymbol:
1035+
break;
10341036
case StmtConditionElement::CK_Boolean: {
10351037
auto *E = elt.getBoolean();
10361038
if (shouldVerifyChecked(E))

lib/AST/ASTWalker.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1340,7 +1340,9 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
13401340
bool doIt(const StmtCondition &C) {
13411341
for (auto &elt : C) {
13421342
switch (elt.getKind()) {
1343-
case StmtConditionElement::CK_Availability: break;
1343+
case StmtConditionElement::CK_Availability:
1344+
case StmtConditionElement::CK_HasSymbol:
1345+
break;
13441346
case StmtConditionElement::CK_Boolean: {
13451347
auto E = elt.getBoolean();
13461348
// Walk an expression condition normally.

lib/AST/Stmt.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,8 @@ SourceRange StmtConditionElement::getSourceRange() const {
368368
return getBoolean()->getSourceRange();
369369
case StmtConditionElement::CK_Availability:
370370
return getAvailability()->getSourceRange();
371+
case StmtConditionElement::CK_HasSymbol:
372+
return getHasSymbolInfo()->getSourceRange();
371373
case StmtConditionElement::CK_PatternBinding:
372374
SourceLoc Start;
373375
if (IntroducerLoc.isValid())
@@ -386,6 +388,15 @@ SourceRange StmtConditionElement::getSourceRange() const {
386388
llvm_unreachable("Unhandled StmtConditionElement in switch.");
387389
}
388390

391+
PoundHasSymbolInfo *PoundHasSymbolInfo::create(ASTContext &Ctx,
392+
SourceLoc PoundLoc,
393+
SourceLoc LParenLoc,
394+
Expr *SymbolExpr,
395+
SourceLoc RParenLoc) {
396+
return new (Ctx)
397+
PoundHasSymbolInfo(PoundLoc, LParenLoc, SymbolExpr, RParenLoc);
398+
}
399+
389400
SourceLoc StmtConditionElement::getStartLoc() const {
390401
switch (getKind()) {
391402
case StmtConditionElement::CK_Boolean:
@@ -394,6 +405,8 @@ SourceLoc StmtConditionElement::getStartLoc() const {
394405
return getAvailability()->getStartLoc();
395406
case StmtConditionElement::CK_PatternBinding:
396407
return getSourceRange().Start;
408+
case StmtConditionElement::CK_HasSymbol:
409+
return getHasSymbolInfo()->getStartLoc();
397410
}
398411

399412
llvm_unreachable("Unhandled StmtConditionElement in switch.");
@@ -407,6 +420,8 @@ SourceLoc StmtConditionElement::getEndLoc() const {
407420
return getAvailability()->getEndLoc();
408421
case StmtConditionElement::CK_PatternBinding:
409422
return getSourceRange().End;
423+
case StmtConditionElement::CK_HasSymbol:
424+
return getHasSymbolInfo()->getEndLoc();
410425
}
411426

412427
llvm_unreachable("Unhandled StmtConditionElement in switch.");

0 commit comments

Comments
 (0)