Skip to content

Commit 37959de

Browse files
committed
Establish type refinement contexts for pattern binding decls directly
The type refinement context builder had a bunch of logic to try to model type refinement contexts for the first variable declaration that shows up within a pattern binding declaration. Instead, model this more syntactically by creating a type refinement context for the pattern binding declaration itself. This both addresses a regression in the handling of `if #available` within a closure that's part of an initializer, and fixes a bug in the same area where similar code has explicit availability annotations.
1 parent 7f031df commit 37959de

File tree

5 files changed

+91
-78
lines changed

5 files changed

+91
-78
lines changed

include/swift/AST/Availability.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,13 @@ class AvailabilityInference {
377377
annotatedAvailableRangeForAttr(const SpecializeAttr *attr, ASTContext &ctx);
378378
};
379379

380+
/// Given a declaration upon which an availability attribute would appear in
381+
/// concrete syntax, return a declaration to which the parser
382+
/// actually attaches the attribute in the abstract syntax tree. We use this
383+
/// function to determine whether the concrete syntax already has an
384+
/// availability attribute.
385+
const Decl *abstractSyntaxDeclForAvailableAttribute(const Decl *D);
386+
380387
} // end namespace swift
381388

382389
#endif

lib/AST/Availability.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,8 @@ AvailabilityInference::attrForAnnotatedAvailableRange(const Decl *D,
225225
ASTContext &Ctx) {
226226
const AvailableAttr *bestAvailAttr = nullptr;
227227

228+
D = abstractSyntaxDeclForAvailableAttribute(D);
229+
228230
for (auto Attr : D->getAttrs()) {
229231
auto *AvailAttr = dyn_cast<AvailableAttr>(Attr);
230232
if (AvailAttr == nullptr || !AvailAttr->Introduced.has_value() ||
@@ -749,3 +751,31 @@ ASTContext::getSwift5PlusAvailability(llvm::VersionTuple swiftVersion) {
749751
bool ASTContext::supportsVersionedAvailability() const {
750752
return minimumAvailableOSVersionForTriple(LangOpts.Target).has_value();
751753
}
754+
755+
const Decl *
756+
swift::abstractSyntaxDeclForAvailableAttribute(const Decl *ConcreteSyntaxDecl) {
757+
// This function needs to be kept in sync with its counterpart,
758+
// concreteSyntaxDeclForAvailableAttribute().
759+
760+
if (auto *PBD = dyn_cast<PatternBindingDecl>(ConcreteSyntaxDecl)) {
761+
// Existing @available attributes in the AST are attached to VarDecls
762+
// rather than PatternBindingDecls, so we return the first VarDecl for
763+
// the pattern binding declaration.
764+
// This is safe, even though there may be multiple VarDecls, because
765+
// all parsed attribute that appear in the concrete syntax upon on the
766+
// PatternBindingDecl are added to all of the VarDecls for the pattern
767+
// binding.
768+
for (auto index : range(PBD->getNumPatternEntries())) {
769+
if (auto VD = PBD->getAnchoringVarDecl(index))
770+
return VD;
771+
}
772+
} else if (auto *ECD = dyn_cast<EnumCaseDecl>(ConcreteSyntaxDecl)) {
773+
// Similar to the PatternBindingDecl case above, we return the
774+
// first EnumElementDecl.
775+
if (auto *Elem = ECD->getFirstElement()) {
776+
return Elem;
777+
}
778+
}
779+
780+
return ConcreteSyntaxDecl;
781+
}

lib/AST/TypeRefinementContext.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,10 @@ void TypeRefinementContext::print(raw_ostream &OS, SourceManager &SrcMgr,
367367
OS << "extension." << ED->getExtendedType().getString();
368368
} else if (isa<TopLevelCodeDecl>(D)) {
369369
OS << "<top-level-code>";
370+
} else if (auto PBD = dyn_cast<PatternBindingDecl>(D)) {
371+
if (auto VD = PBD->getAnchoringVarDecl(0)) {
372+
OS << VD->getName();
373+
}
370374
}
371375
}
372376

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 28 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,8 @@ ExportContext::getExportabilityReason() const {
308308
/// on the target platform.
309309
static const AvailableAttr *getActiveAvailableAttribute(const Decl *D,
310310
ASTContext &AC) {
311+
D = abstractSyntaxDeclForAvailableAttribute(D);
312+
311313
for (auto Attr : D->getAttrs())
312314
if (auto AvAttr = dyn_cast<AvailableAttr>(Attr)) {
313315
if (!AvAttr->isInvalid() && AvAttr->isActivePlatform(AC)) {
@@ -494,7 +496,10 @@ class TypeRefinementContextBuilder : private ASTWalker {
494496
/// Returns a new context to be introduced for the declaration, or nullptr
495497
/// if no new context should be introduced.
496498
TypeRefinementContext *getNewContextForSignatureOfDecl(Decl *D) {
497-
if (!isa<ValueDecl>(D) && !isa<ExtensionDecl>(D) && !isa<MacroExpansionDecl>(D))
499+
if (!isa<ValueDecl>(D) &&
500+
!isa<ExtensionDecl>(D) &&
501+
!isa<MacroExpansionDecl>(D) &&
502+
!isa<PatternBindingDecl>(D))
498503
return nullptr;
499504

500505
// Only introduce for an AbstractStorageDecl if it is not local. We
@@ -503,20 +508,17 @@ class TypeRefinementContextBuilder : private ASTWalker {
503508
if (isa<AbstractStorageDecl>(D) && D->getDeclContext()->isLocalContext())
504509
return nullptr;
505510

511+
// Don't introduce for variable declarations that have a parent pattern
512+
// binding; all of the relevant information is on the pattern binding.
513+
if (auto var = dyn_cast<VarDecl>(D)) {
514+
if (var->getParentPatternBinding())
515+
return nullptr;
516+
}
517+
506518
// Ignore implicit declarations (mainly skips over `DeferStmt` functions).
507519
if (D->isImplicit())
508520
return nullptr;
509521

510-
// Skip introducing additional contexts for var decls past the first in a
511-
// pattern. The context necessary for the pattern as a whole was already
512-
// introduced if necessary by the first var decl.
513-
if (auto *VD = dyn_cast<VarDecl>(D)) {
514-
if (auto *PBD = VD->getParentPatternBinding()) {
515-
if (VD != PBD->getAnchoringVarDecl(0))
516-
return nullptr;
517-
}
518-
}
519-
520522
// Declarations with an explicit availability attribute always get a TRC.
521523
if (hasActiveAvailableAttribute(D, Context)) {
522524
AvailabilityContext DeclaredAvailability =
@@ -541,7 +543,8 @@ class TypeRefinementContextBuilder : private ASTWalker {
541543
getCurrentTRC()->getAvailabilityInfo();
542544
AvailabilityContext EffectiveAvailability =
543545
getEffectiveAvailabilityForDeclSignature(D, CurrentAvailability);
544-
if ((isa<VarDecl>(D) && refinementSourceRangeForDecl(D).isValid()) ||
546+
if ((isa<PatternBindingDecl>(D) &&
547+
refinementSourceRangeForDecl(D).isValid()) ||
545548
CurrentAvailability.isSupersetOf(EffectiveAvailability))
546549
return TypeRefinementContext::createForDeclImplicit(
547550
Context, D, getCurrentTRC(), EffectiveAvailability,
@@ -617,22 +620,6 @@ class TypeRefinementContextBuilder : private ASTWalker {
617620
// the bodies of its accessors.
618621
SourceRange Range = storageDecl->getSourceRange();
619622

620-
// For a variable declaration (without accessors) we use the range of the
621-
// containing pattern binding declaration to make sure that we include
622-
// any type annotation in the type refinement context range. We also
623-
// need to include any custom attributes that were written on the
624-
// declaration.
625-
if (auto *varDecl = dyn_cast<VarDecl>(storageDecl)) {
626-
if (auto *PBD = varDecl->getParentPatternBinding())
627-
Range = PBD->getSourceRange();
628-
629-
for (auto attr : varDecl->getOriginalAttrs()) {
630-
if (auto customAttr = dyn_cast<CustomAttr>(attr)) {
631-
Range.widen(customAttr->getRange());
632-
}
633-
}
634-
}
635-
636623
// HACK: For synthesized trivial accessors we may have not a valid
637624
// location for the end of the braces, so in that case we will fall back
638625
// to using the range for the storage declaration. The right fix here is
@@ -646,7 +633,13 @@ class TypeRefinementContextBuilder : private ASTWalker {
646633

647634
return Range;
648635
}
649-
636+
637+
// For pattern binding declarations, include the attributes in the source
638+
// range so that we're sure to cover any property wrappers.
639+
if (auto patternBinding = dyn_cast<PatternBindingDecl>(D)) {
640+
return D->getSourceRangeIncludingAttrs();
641+
}
642+
650643
return D->getSourceRange();
651644
}
652645

@@ -662,7 +655,7 @@ class TypeRefinementContextBuilder : private ASTWalker {
662655
Context, D, getCurrentTRC(), Availability, range);
663656
}
664657

665-
/// Build contexts for a VarDecl with the given initializer.
658+
/// Build contexts for a pattern binding declaration.
666659
void buildContextsForPatternBindingDecl(PatternBindingDecl *pattern) {
667660
// Build contexts for each of the pattern entries.
668661
for (unsigned index : range(pattern->getNumPatternEntries())) {
@@ -1241,22 +1234,11 @@ bool ExpandChildTypeRefinementContextsRequest::evaluate(
12411234
if (computeContainedByDeploymentTarget(parentTRC, ctx))
12421235
return false;
12431236

1244-
// Variables can have children corresponding to property wrappers and
1245-
// the initial values provided in each pattern binding entry.
1246-
if (auto var = dyn_cast<VarDecl>(decl)) {
1247-
if (auto *pattern = var->getParentPatternBinding()) {
1248-
// Only do this for the first variable in the pattern binding declaration.
1249-
auto anchorVar = pattern->getAnchoringVarDecl(0);
1250-
if (anchorVar != var) {
1251-
return evaluateOrDefault(
1252-
evaluator,
1253-
ExpandChildTypeRefinementContextsRequest{anchorVar, parentTRC},
1254-
false);
1255-
}
1256-
1257-
TypeRefinementContextBuilder builder(parentTRC, ctx);
1258-
builder.buildContextsForPatternBindingDecl(pattern);
1259-
}
1237+
// Pattern binding declarations can have children corresponding to property
1238+
// wrappers and the initial values provided in each pattern binding entry.
1239+
if (auto *binding = dyn_cast<PatternBindingDecl>(decl)) {
1240+
TypeRefinementContextBuilder builder(parentTRC, ctx);
1241+
builder.buildContextsForPatternBindingDecl(binding);
12601242
}
12611243

12621244
return false;
@@ -1596,38 +1578,6 @@ concreteSyntaxDeclForAvailableAttribute(const Decl *AbstractSyntaxDecl) {
15961578
return AbstractSyntaxDecl;
15971579
}
15981580

1599-
/// Given a declaration upon which an availability attribute would appear in
1600-
/// concrete syntax, return a declaration to which the parser
1601-
/// actually attaches the attribute in the abstract syntax tree. We use this
1602-
/// function to determine whether the concrete syntax already has an
1603-
/// availability attribute.
1604-
static const Decl *
1605-
abstractSyntaxDeclForAvailableAttribute(const Decl *ConcreteSyntaxDecl) {
1606-
// This function needs to be kept in sync with its counterpart,
1607-
// concreteSyntaxDeclForAvailableAttribute().
1608-
1609-
if (auto *PBD = dyn_cast<PatternBindingDecl>(ConcreteSyntaxDecl)) {
1610-
// Existing @available attributes in the AST are attached to VarDecls
1611-
// rather than PatternBindingDecls, so we return the first VarDecl for
1612-
// the pattern binding declaration.
1613-
// This is safe, even though there may be multiple VarDecls, because
1614-
// all parsed attribute that appear in the concrete syntax upon on the
1615-
// PatternBindingDecl are added to all of the VarDecls for the pattern
1616-
// binding.
1617-
if (PBD->getNumPatternEntries() != 0) {
1618-
return PBD->getAnchoringVarDecl(0);
1619-
}
1620-
} else if (auto *ECD = dyn_cast<EnumCaseDecl>(ConcreteSyntaxDecl)) {
1621-
// Similar to the PatternBindingDecl case above, we return the
1622-
// first EnumElementDecl.
1623-
if (auto *Elem = ECD->getFirstElement()) {
1624-
return Elem;
1625-
}
1626-
}
1627-
1628-
return ConcreteSyntaxDecl;
1629-
}
1630-
16311581
/// Given a declaration, return a better related declaration for which
16321582
/// to suggest an @available fixit, or the original declaration
16331583
/// if no such related declaration exists.

test/Sema/availability_versions.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,3 +1741,25 @@ func useHasUnavailableExtension(_ s: HasUnavailableExtension) {
17411741
s.inheritsUnavailable() // expected-error {{'inheritsUnavailable()' is unavailable in macOS}}
17421742
s.moreAvailableButStillUnavailable() // expected-error {{'moreAvailableButStillUnavailable()' is unavailable in macOS}}
17431743
}
1744+
1745+
@available(macOS 10.15, *)
1746+
func f() -> Int { 17 }
1747+
1748+
class StoredPropertiesWithAvailabilityInClosures {
1749+
private static let value: Int = {
1750+
if #available(macOS 10.15, *) {
1751+
return f()
1752+
}
1753+
1754+
return 0
1755+
}()
1756+
1757+
@available(macOS 10.14, *)
1758+
private static let otherValue: Int = {
1759+
if #available(macOS 10.15, *) {
1760+
return f()
1761+
}
1762+
1763+
return 0
1764+
}()
1765+
}

0 commit comments

Comments
 (0)