Skip to content

Commit 9249186

Browse files
authored
Merge pull request swiftlang#33855 from xymus/availability-inlinable
[Sema] Fix availability checking in inlinable code
2 parents fd3e3cf + fb76ff1 commit 9249186

16 files changed

+350
-97
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2455,10 +2455,16 @@ NOTE(suggest_removing_override, none,
24552455
ERROR(override_less_available,none,
24562456
"overriding %0 must be as available as declaration it overrides",
24572457
(DeclBaseName))
2458+
WARNING(override_less_available_warn,none,
2459+
"overriding %0 must be as available as declaration it overrides",
2460+
(DeclBaseName))
24582461

24592462
ERROR(override_accessor_less_available,none,
24602463
"overriding %0 for %1 must be as available as declaration it overrides",
24612464
(DescriptiveDeclKind, DeclBaseName))
2465+
WARNING(override_accessor_less_available_warn,none,
2466+
"overriding %0 for %1 must be as available as declaration it overrides",
2467+
(DescriptiveDeclKind, DeclBaseName))
24622468

24632469
ERROR(override_let_property,none,
24642470
"cannot override immutable 'let' property %0 with the getter of a 'var'",

include/swift/AST/TypeRefinementContext.h

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,16 +154,23 @@ class TypeRefinementContext {
154154

155155
SourceRange SrcRange;
156156

157+
/// Runtime availability information for the code in this context.
157158
AvailabilityContext AvailabilityInfo;
158159

160+
/// Runtime availability information as explicitly declared by attributes
161+
/// for the inlinable code in this context. Compared to AvailabilityInfo,
162+
/// this is not bounded to the minimum deployment OS version.
163+
AvailabilityContext AvailabilityInfoExplicit;
164+
159165
std::vector<TypeRefinementContext *> Children;
160166

161167
TypeRefinementContext(ASTContext &Ctx, IntroNode Node,
162168
TypeRefinementContext *Parent, SourceRange SrcRange,
163-
const AvailabilityContext &Info);
169+
const AvailabilityContext &Info,
170+
const AvailabilityContext &InfoExplicit);
164171

165172
public:
166-
173+
167174
/// Create the root refinement context for the given SourceFile.
168175
static TypeRefinementContext *createRoot(SourceFile *SF,
169176
const AvailabilityContext &Info);
@@ -172,8 +179,9 @@ class TypeRefinementContext {
172179
static TypeRefinementContext *createForDecl(ASTContext &Ctx, Decl *D,
173180
TypeRefinementContext *Parent,
174181
const AvailabilityContext &Info,
182+
const AvailabilityContext &InfoExplicit,
175183
SourceRange SrcRange);
176-
184+
177185
/// Create a refinement context for the Then branch of the given IfStmt.
178186
static TypeRefinementContext *
179187
createForIfStmtThen(ASTContext &Ctx, IfStmt *S, TypeRefinementContext *Parent,
@@ -240,11 +248,19 @@ class TypeRefinementContext {
240248
SourceRange getSourceRange() const { return SrcRange; }
241249

242250
/// Returns the information on what can be assumed present at run time when
243-
/// running code contained in this context.
251+
/// running code contained in this context, taking into account the minimum
252+
/// deployment target.
244253
const AvailabilityContext &getAvailabilityInfo() const {
245254
return AvailabilityInfo;
246255
}
247256

257+
/// Returns the information on what can be assumed present at run time when
258+
/// running code contained in this context if it were to be inlined,
259+
/// without considering the minimum deployment target.
260+
const AvailabilityContext &getAvailabilityInfoExplicit() const {
261+
return AvailabilityInfoExplicit;
262+
}
263+
248264
/// Adds a child refinement context.
249265
void addChild(TypeRefinementContext *Child) {
250266
assert(Child->getSourceRange().isValid());

lib/AST/Availability.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,13 @@ static bool isBetterThan(const AvailableAttr *newAttr,
144144
return true;
145145

146146
// If they belong to the same platform, the one that introduces later wins.
147-
if (prevAttr->Platform == newAttr->Platform)
147+
if (prevAttr->Platform == newAttr->Platform) {
148+
if (newAttr->isUnconditionallyUnavailable())
149+
return true;
150+
if (prevAttr->isUnconditionallyUnavailable())
151+
return false;
148152
return prevAttr->Introduced.getValue() < newAttr->Introduced.getValue();
153+
}
149154

150155
// If the new attribute's platform inherits from the old one, it wins.
151156
return inheritsAvailabilityFromPlatform(newAttr->Platform,
@@ -158,10 +163,12 @@ AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) {
158163

159164
for (auto Attr : D->getAttrs()) {
160165
auto *AvailAttr = dyn_cast<AvailableAttr>(Attr);
161-
if (AvailAttr == nullptr || !AvailAttr->Introduced.hasValue() ||
166+
if (AvailAttr == nullptr ||
162167
!AvailAttr->isActivePlatform(Ctx) ||
163168
AvailAttr->isLanguageVersionSpecific() ||
164-
AvailAttr->isPackageDescriptionVersionSpecific()) {
169+
AvailAttr->isPackageDescriptionVersionSpecific() ||
170+
(!AvailAttr->Introduced.hasValue() &&
171+
!AvailAttr->isUnconditionallyUnavailable())) {
165172
continue;
166173
}
167174

@@ -172,6 +179,9 @@ AvailabilityInference::annotatedAvailableRange(const Decl *D, ASTContext &Ctx) {
172179
if (!bestAvailAttr)
173180
return None;
174181

182+
if (bestAvailAttr->isUnconditionallyUnavailable())
183+
return AvailabilityContext(VersionRange::empty());
184+
175185
return AvailabilityContext{
176186
VersionRange::allGTE(bestAvailAttr->Introduced.getValue())};
177187
}

lib/AST/TypeRefinementContext.cpp

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ using namespace swift;
2828
TypeRefinementContext::TypeRefinementContext(ASTContext &Ctx, IntroNode Node,
2929
TypeRefinementContext *Parent,
3030
SourceRange SrcRange,
31-
const AvailabilityContext &Info)
32-
: Node(Node), SrcRange(SrcRange), AvailabilityInfo(Info) {
31+
const AvailabilityContext &Info,
32+
const AvailabilityContext &InfoExplicit)
33+
: Node(Node), SrcRange(SrcRange), AvailabilityInfo(Info),
34+
AvailabilityInfoExplicit(InfoExplicit) {
3335
if (Parent) {
3436
assert(SrcRange.isValid());
3537
Parent->addChild(this);
36-
assert(Info.isContainedIn(Parent->getAvailabilityInfo()));
38+
assert(InfoExplicit.isContainedIn(Parent->getAvailabilityInfoExplicit()));
3739
}
3840
Ctx.addDestructorCleanup(Children);
3941
}
@@ -46,18 +48,20 @@ TypeRefinementContext::createRoot(SourceFile *SF,
4648
ASTContext &Ctx = SF->getASTContext();
4749
return new (Ctx)
4850
TypeRefinementContext(Ctx, SF,
49-
/*Parent=*/nullptr, SourceRange(), Info);
51+
/*Parent=*/nullptr, SourceRange(), Info,
52+
AvailabilityContext::alwaysAvailable());
5053
}
5154

5255
TypeRefinementContext *
5356
TypeRefinementContext::createForDecl(ASTContext &Ctx, Decl *D,
5457
TypeRefinementContext *Parent,
5558
const AvailabilityContext &Info,
59+
const AvailabilityContext &InfoExplicit,
5660
SourceRange SrcRange) {
5761
assert(D);
5862
assert(Parent);
5963
return new (Ctx)
60-
TypeRefinementContext(Ctx, D, Parent, SrcRange, Info);
64+
TypeRefinementContext(Ctx, D, Parent, SrcRange, Info, InfoExplicit);
6165
}
6266

6367
TypeRefinementContext *
@@ -68,7 +72,7 @@ TypeRefinementContext::createForIfStmtThen(ASTContext &Ctx, IfStmt *S,
6872
assert(Parent);
6973
return new (Ctx)
7074
TypeRefinementContext(Ctx, IntroNode(S, /*IsThen=*/true), Parent,
71-
S->getThenStmt()->getSourceRange(), Info);
75+
S->getThenStmt()->getSourceRange(), Info, Info);
7276
}
7377

7478
TypeRefinementContext *
@@ -79,7 +83,7 @@ TypeRefinementContext::createForIfStmtElse(ASTContext &Ctx, IfStmt *S,
7983
assert(Parent);
8084
return new (Ctx)
8185
TypeRefinementContext(Ctx, IntroNode(S, /*IsThen=*/false), Parent,
82-
S->getElseStmt()->getSourceRange(), Info);
86+
S->getElseStmt()->getSourceRange(), Info, Info);
8387
}
8488

8589
TypeRefinementContext *
@@ -92,7 +96,7 @@ TypeRefinementContext::createForConditionFollowingQuery(ASTContext &Ctx,
9296
assert(Parent);
9397
SourceRange Range(PAI->getEndLoc(), LastElement.getEndLoc());
9498
return new (Ctx) TypeRefinementContext(Ctx, PAI, Parent, Range,
95-
Info);
99+
Info, Info);
96100
}
97101

98102
TypeRefinementContext *
@@ -107,7 +111,7 @@ TypeRefinementContext::createForGuardStmtFallthrough(ASTContext &Ctx,
107111
SourceRange Range(RS->getEndLoc(), ContainingBraceStmt->getEndLoc());
108112
return new (Ctx) TypeRefinementContext(Ctx,
109113
IntroNode(RS, /*IsFallthrough=*/true),
110-
Parent, Range, Info);
114+
Parent, Range, Info, Info);
111115
}
112116

113117
TypeRefinementContext *
@@ -118,7 +122,7 @@ TypeRefinementContext::createForGuardStmtElse(ASTContext &Ctx, GuardStmt *RS,
118122
assert(Parent);
119123
return new (Ctx)
120124
TypeRefinementContext(Ctx, IntroNode(RS, /*IsFallthrough=*/false), Parent,
121-
RS->getBody()->getSourceRange(), Info);
125+
RS->getBody()->getSourceRange(), Info, Info);
122126
}
123127

124128
TypeRefinementContext *
@@ -128,7 +132,7 @@ TypeRefinementContext::createForWhileStmtBody(ASTContext &Ctx, WhileStmt *S,
128132
assert(S);
129133
assert(Parent);
130134
return new (Ctx) TypeRefinementContext(
131-
Ctx, S, Parent, S->getBody()->getSourceRange(), Info);
135+
Ctx, S, Parent, S->getBody()->getSourceRange(), Info, Info);
132136
}
133137

134138
// Only allow allocation of TypeRefinementContext using the allocator in
@@ -296,6 +300,7 @@ void TypeRefinementContext::print(raw_ostream &OS, SourceManager &SrcMgr,
296300
OS << "(" << getReasonName(getReason());
297301

298302
OS << " versions=" << AvailabilityInfo.getOSVersion().getAsString();
303+
OS << " explicit=" << AvailabilityInfoExplicit.getOSVersion().getAsString();
299304

300305
if (getReason() == Reason::Decl) {
301306
Decl *D = Node.getAsDecl();

lib/ClangImporter/ImportDecl.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1817,18 +1817,27 @@ static void applyAvailableAttribute(Decl *decl, AvailabilityContext &info,
18171817
if (info.isAlwaysAvailable())
18181818
return;
18191819

1820+
PlatformAgnosticAvailabilityKind platformAgnosticAvailability;
1821+
llvm::VersionTuple introducedVersion;
1822+
if (info.isKnownUnreachable()) {
1823+
platformAgnosticAvailability = PlatformAgnosticAvailabilityKind::Unavailable;
1824+
} else {
1825+
platformAgnosticAvailability = PlatformAgnosticAvailabilityKind::None;
1826+
introducedVersion = info.getOSVersion().getLowerEndpoint();
1827+
}
1828+
18201829
llvm::VersionTuple noVersion;
18211830
auto AvAttr = new (C) AvailableAttr(SourceLoc(), SourceRange(),
18221831
targetPlatform(C.LangOpts),
18231832
/*Message=*/StringRef(),
18241833
/*Rename=*/StringRef(),
1825-
info.getOSVersion().getLowerEndpoint(),
1834+
introducedVersion,
18261835
/*IntroducedRange*/SourceRange(),
18271836
/*Deprecated=*/noVersion,
18281837
/*DeprecatedRange*/SourceRange(),
18291838
/*Obsoleted=*/noVersion,
18301839
/*ObsoletedRange*/SourceRange(),
1831-
PlatformAgnosticAvailabilityKind::None,
1840+
platformAgnosticAvailability,
18321841
/*Implicit=*/false);
18331842

18341843
decl->getAttrs().add(AvAttr);

lib/SIL/IR/SILPrinter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2638,7 +2638,8 @@ void SILFunction::print(SILPrintContext &PrintCtx) const {
26382638
if (isAlwaysWeakImported())
26392639
OS << "[weak_imported] ";
26402640
auto availability = getAvailabilityForLinkage();
2641-
if (!availability.isAlwaysAvailable()) {
2641+
if (!availability.isAlwaysAvailable() &&
2642+
!availability.isKnownUnreachable()) {
26422643
auto version = availability.getOSVersion().getLowerEndpoint();
26432644
OS << "[available " << version.getAsString() << "] ";
26442645
}

lib/Sema/TypeCheckAttr.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,9 +1543,16 @@ void AttributeChecker::visitAvailableAttr(AvailableAttr *attr) {
15431543
VersionRange::allGTE(attr->Introduced.getValue())};
15441544

15451545
if (!AttrRange.isContainedIn(EnclosingAnnotatedRange.getValue())) {
1546-
diagnose(attr->getLocation(), diag::availability_decl_more_than_enclosing);
1547-
diagnose(EnclosingDecl->getLoc(),
1548-
diag::availability_decl_more_than_enclosing_enclosing_here);
1546+
// Don't show this error in swiftinterfaces if it is about a context that
1547+
// is unavailable, this was in the stdlib at Swift 5.3.
1548+
SourceFile *Parent = D->getDeclContext()->getParentSourceFile();
1549+
if (!Parent || Parent->Kind != SourceFileKind::Interface ||
1550+
!EnclosingAnnotatedRange.getValue().isKnownUnreachable()) {
1551+
diagnose(attr->getLocation(),
1552+
diag::availability_decl_more_than_enclosing);
1553+
diagnose(EnclosingDecl->getLoc(),
1554+
diag::availability_decl_more_than_enclosing_enclosing_here);
1555+
}
15491556
}
15501557
}
15511558

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,30 @@ class TypeRefinementContextBuilder : private ASTWalker {
170170
// The potential versions in the declaration are constrained by both
171171
// the declared availability of the declaration and the potential versions
172172
// of its lexical context.
173-
AvailabilityContext DeclInfo =
173+
AvailabilityContext ExplicitDeclInfo =
174174
swift::AvailabilityInference::availableRange(D, Context);
175-
DeclInfo.intersectWith(getCurrentTRC()->getAvailabilityInfo());
175+
ExplicitDeclInfo.intersectWith(
176+
getCurrentTRC()->getAvailabilityInfoExplicit());
177+
AvailabilityContext DeclInfo = ExplicitDeclInfo;
178+
179+
// When the body is inlinable consider only the explicitly declared range
180+
// for checking availability. Otherwise, use the parent range which may
181+
// begin at the minimum deployment target.
182+
//
183+
// Also use the parent range when reading swiftinterfaces for
184+
// retrocompatibility.
185+
bool isInlinable = D->getAttrs().hasAttribute<InlinableAttr>() ||
186+
D->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>();
187+
SourceFile *SF = D->getDeclContext()->getParentSourceFile();
188+
if (!isInlinable || (SF && SF->Kind == SourceFileKind::Interface)) {
189+
DeclInfo.intersectWith(
190+
getCurrentTRC()->getAvailabilityInfo());
191+
}
176192

177193
TypeRefinementContext *NewTRC =
178194
TypeRefinementContext::createForDecl(Context, D, getCurrentTRC(),
179195
DeclInfo,
196+
ExplicitDeclInfo,
180197
refinementSourceRangeForDecl(D));
181198

182199
// Record the TRC for this storage declaration so that
@@ -190,19 +207,27 @@ class TypeRefinementContextBuilder : private ASTWalker {
190207

191208
return NewTRC;
192209
}
193-
210+
194211
/// Returns true if the declaration should introduce a new refinement context.
195212
bool declarationIntroducesNewContext(Decl *D) {
196213
if (!isa<ValueDecl>(D) && !isa<ExtensionDecl>(D)) {
197214
return false;
198215
}
199-
216+
217+
// Explicit inlinability may to the decl being used on an earlier OS
218+
// version when inlined on the client side. This check assumes that
219+
// implicit decls are handled elsewhere.
220+
bool isExplicitlyInlinable = !D->isImplicit() &&
221+
(D->getAttrs().hasAttribute<InlinableAttr>() ||
222+
D->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>());
223+
200224
// No need to introduce a context if the declaration does not have an
201-
// availability attribute.
202-
if (!hasActiveAvailableAttribute(D, Context)) {
225+
// availability or non-implicit inlinable attribute.
226+
if (!hasActiveAvailableAttribute(D, Context) &&
227+
!isExplicitlyInlinable) {
203228
return false;
204229
}
205-
230+
206231
// Only introduce for an AbstractStorageDecl if it is not local.
207232
// We introduce for the non-local case because these may
208233
// have getters and setters (and these may be synthesized, so they might
@@ -213,7 +238,7 @@ class TypeRefinementContextBuilder : private ASTWalker {
213238
return false;
214239
}
215240
}
216-
241+
217242
return true;
218243
}
219244

@@ -674,9 +699,7 @@ TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc,
674699
// refined. For now, this is fine -- but if we ever synthesize #available(),
675700
// this will be a real problem.
676701

677-
// We can assume we are running on at least the minimum deployment target.
678-
auto OverApproximateContext =
679-
AvailabilityContext::forDeploymentTarget(Context);
702+
auto OverApproximateContext = AvailabilityContext::alwaysAvailable();
680703
auto isInvalidLoc = [SF](SourceLoc loc) {
681704
return SF ? loc.isInvalid() : true;
682705
};
@@ -707,6 +730,14 @@ TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc,
707730
}
708731
}
709732

733+
// If we still don't have an introduction version, use the current deployment
734+
// target. This covers cases where an inlinable function and its parent
735+
// contexts don't have explicit availability attributes.
736+
if (!OverApproximateContext.getOSVersion().hasLowerEndpoint()) {
737+
auto currentOS = AvailabilityContext::forDeploymentTarget(Context);
738+
OverApproximateContext.constrainWith(currentOS);
739+
}
740+
710741
return OverApproximateContext;
711742
}
712743

0 commit comments

Comments
 (0)