Skip to content

Commit ed96151

Browse files
authored
Merge pull request #61182 from hamishknight/warranty-void
2 parents fa994e7 + 20830cc commit ed96151

File tree

10 files changed

+176
-10
lines changed

10 files changed

+176
-10
lines changed

include/swift/AST/Decl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,12 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
10491049

10501050
bool isAvailableAsSPI() const;
10511051

1052+
/// Whether the declaration is considered unavailable through either being
1053+
/// explicitly marked as such, or has a parent decl that is semantically
1054+
/// unavailable. This is a broader notion of unavailability than is checked by
1055+
/// \c AvailableAttr::isUnavailable.
1056+
bool isSemanticallyUnavailable() const;
1057+
10521058
// List the SPI groups declared with @_spi or inherited by this decl.
10531059
//
10541060
// SPI groups are inherited from the parent contexts only if the local decl

include/swift/AST/TypeCheckRequests.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3489,6 +3489,22 @@ class RenamedDeclRequest
34893489
bool isCached() const { return true; }
34903490
};
34913491

3492+
class IsSemanticallyUnavailableRequest
3493+
: public SimpleRequest<IsSemanticallyUnavailableRequest,
3494+
bool(const Decl *),
3495+
RequestFlags::Cached> {
3496+
public:
3497+
using SimpleRequest::SimpleRequest;
3498+
3499+
private:
3500+
friend SimpleRequest;
3501+
3502+
bool evaluate(Evaluator &evaluator, const Decl *decl) const;
3503+
3504+
public:
3505+
bool isCached() const { return true; }
3506+
};
3507+
34923508
class ClosureEffectsRequest
34933509
: public SimpleRequest<ClosureEffectsRequest,
34943510
FunctionType::ExtInfo(ClosureExpr *),

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,9 @@ SWIFT_REQUEST(TypeChecker, GetImplicitSendableRequest,
395395
SWIFT_REQUEST(TypeChecker, RenamedDeclRequest,
396396
ValueDecl *(const ValueDecl *, const AvailableAttr *),
397397
Cached, NoLocationInfo)
398+
SWIFT_REQUEST(TypeChecker, IsSemanticallyUnavailableRequest,
399+
bool(const Decl *),
400+
Cached, NoLocationInfo)
398401
SWIFT_REQUEST(TypeChecker, ClosureEffectsRequest,
399402
FunctionType::ExtInfo(ClosureExpr *),
400403
Cached, NoLocationInfo)

include/swift/SIL/SILDeclRef.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,11 @@ struct SILDeclRef {
300300
/// Retrieves the ASTContext from the underlying AST node being stored.
301301
ASTContext &getASTContext() const;
302302

303+
/// Retrieve the innermost declaration context corresponding to the underlying
304+
/// node, which will either be the node itself (if it's also a declaration
305+
/// context) or its parent context.
306+
DeclContext *getInnermostDeclContext() const;
307+
303308
llvm::Optional<AnyFunctionRef> getAnyFunctionRef() const;
304309

305310
SILLocation getAsRegularLocation() const;

include/swift/SIL/SILProfiler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class SILFunction;
3232
class SILModule;
3333

3434
/// Returns whether the given AST node requires profiling instrumentation.
35-
bool doesASTRequireProfiling(SILModule &M, ASTNode N);
35+
bool doesASTRequireProfiling(SILModule &M, ASTNode N, SILDeclRef Constant);
3636

3737
/// SILProfiler - Maps AST nodes to profile counters.
3838
class SILProfiler : public SILAllocated<SILProfiler> {

lib/AST/Availability.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/AST/Types.h"
2121
#include "swift/AST/Availability.h"
2222
#include "swift/AST/PlatformKind.h"
23+
#include "swift/AST/TypeCheckRequests.h"
2324
#include "swift/AST/TypeWalker.h"
2425
#include <map>
2526

@@ -194,6 +195,41 @@ bool Decl::isAvailableAsSPI() const {
194195
.isAvailableAsSPI();
195196
}
196197

198+
bool IsSemanticallyUnavailableRequest::evaluate(Evaluator &evaluator,
199+
const Decl *decl) const {
200+
// Directly marked unavailable.
201+
if (AvailableAttr::isUnavailable(decl))
202+
return true;
203+
204+
// If this is an extension, it's semantically unavailable if its nominal is,
205+
// as there is no way to reference or construct the type.
206+
if (auto *ext = dyn_cast<ExtensionDecl>(decl)) {
207+
if (auto *nom = ext->getExtendedNominal()) {
208+
if (nom->isSemanticallyUnavailable())
209+
return true;
210+
}
211+
}
212+
213+
// If the parent decl is semantically unavailable, then this decl is too.
214+
// For local contexts, this means it's a local decl in e.g an unavailable
215+
// function, which cannot be accessed. For non-local contexts, this is a
216+
// nested type or a member with an unavailable parent, which cannot be
217+
// referenced.
218+
// Similar to `AvailableAttr::isUnavailable`, don't apply this logic to
219+
// Clang decls, as they may be inaccurately parented.
220+
if (!decl->hasClangNode()) {
221+
auto *DC = decl->getDeclContext();
222+
if (auto *parentDecl = DC->getInnermostDeclarationDeclContext())
223+
return parentDecl->isSemanticallyUnavailable();
224+
}
225+
return false;
226+
}
227+
228+
bool Decl::isSemanticallyUnavailable() const {
229+
auto &eval = getASTContext().evaluator;
230+
return evaluateOrDefault(eval, IsSemanticallyUnavailableRequest{this}, false);
231+
}
232+
197233
AvailabilityContext
198234
AvailabilityInference::annotatedAvailableRangeForAttr(const SpecializeAttr* attr,
199235
ASTContext &ctx) {

lib/SIL/IR/SILDeclRef.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,18 +195,26 @@ Optional<AnyFunctionRef> SILDeclRef::getAnyFunctionRef() const {
195195
llvm_unreachable("Unhandled case in switch");
196196
}
197197

198-
ASTContext &SILDeclRef::getASTContext() const {
198+
DeclContext *SILDeclRef::getInnermostDeclContext() const {
199+
if (!loc)
200+
return nullptr;
199201
switch (getLocKind()) {
200202
case LocKind::Decl:
201-
return getDecl()->getASTContext();
203+
return getDecl()->getInnermostDeclContext();
202204
case LocKind::Closure:
203-
return getAbstractClosureExpr()->getASTContext();
205+
return getAbstractClosureExpr();
204206
case LocKind::File:
205-
return getFileUnit()->getASTContext();
207+
return getFileUnit();
206208
}
207209
llvm_unreachable("Unhandled case in switch");
208210
}
209211

212+
ASTContext &SILDeclRef::getASTContext() const {
213+
auto *DC = getInnermostDeclContext();
214+
assert(DC && "Must have a decl context");
215+
return DC->getASTContext();
216+
}
217+
210218
Optional<AvailabilityContext> SILDeclRef::getAvailabilityForLinkage() const {
211219
// Back deployment thunks and fallbacks don't have availability since they
212220
// are non-ABI.

lib/SIL/IR/SILProfiler.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,30 @@ static bool doesClosureHaveBody(AbstractClosureExpr *ACE) {
4242
}
4343

4444
/// Check whether a root AST node should be profiled.
45-
static bool shouldProfile(ASTNode N) {
45+
static bool shouldProfile(ASTNode N, SILDeclRef Constant) {
4646
// Do not profile AST nodes with invalid source locations.
4747
if (N.getStartLoc().isInvalid() || N.getEndLoc().isInvalid()) {
4848
LLVM_DEBUG(llvm::dbgs()
4949
<< "Skipping ASTNode: invalid start/end locations\n");
5050
return false;
5151
}
52+
if (!Constant) {
53+
// We should only ever have a null SILDeclRef for top-level code, which is
54+
// always user-written, and should always be profiled.
55+
// FIXME: Once top-level code is unified under a single SILProfiler, this
56+
// case can be eliminated.
57+
assert(isa<TopLevelCodeDecl>(N.get<Decl *>()));
58+
return true;
59+
}
60+
61+
// Do not profile AST nodes in unavailable contexts.
62+
auto *DC = Constant.getInnermostDeclContext();
63+
if (auto *D = DC->getInnermostDeclarationDeclContext()) {
64+
if (D->isSemanticallyUnavailable()) {
65+
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: unavailable context\n");
66+
return false;
67+
}
68+
}
5269

5370
if (auto *E = N.dyn_cast<Expr *>()) {
5471
if (auto *CE = dyn_cast<AbstractClosureExpr>(E)) {
@@ -96,13 +113,13 @@ static bool shouldProfile(ASTNode N) {
96113
}
97114

98115
namespace swift {
99-
bool doesASTRequireProfiling(SILModule &M, ASTNode N) {
116+
bool doesASTRequireProfiling(SILModule &M, ASTNode N, SILDeclRef Constant) {
100117
// If profiling isn't enabled, don't profile anything.
101118
auto &Opts = M.getOptions();
102119
if (Opts.UseProfile.empty() && !Opts.GenerateProfile)
103120
return false;
104121

105-
return shouldProfile(N);
122+
return shouldProfile(N, Constant);
106123
}
107124
} // namespace swift
108125

@@ -157,7 +174,7 @@ static bool canCreateProfilerForAST(ASTNode N, SILDeclRef forDecl) {
157174

158175
SILProfiler *SILProfiler::create(SILModule &M, ASTNode N, SILDeclRef Ref) {
159176
const auto &Opts = M.getOptions();
160-
if (!doesASTRequireProfiling(M, N))
177+
if (!doesASTRequireProfiling(M, N, Ref))
161178
return nullptr;
162179

163180
if (!canCreateProfilerForAST(N, Ref)) {

lib/SILGen/SILGen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1436,7 +1436,7 @@ void SILGenModule::emitFunction(FuncDecl *fd) {
14361436

14371437
if (fd->hasBody()) {
14381438
SILDeclRef constant(decl);
1439-
bool ForCoverageMapping = doesASTRequireProfiling(M, fd);
1439+
bool ForCoverageMapping = doesASTRequireProfiling(M, fd, constant);
14401440
emitOrDelayFunction(*this, constant,
14411441
/*forceEmission=*/ForCoverageMapping);
14421442
}

test/Profiler/unmapped.swift

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,78 @@ struct R : Codable {
2222
enum E : Int {
2323
case a
2424
}
25+
26+
// We don't profile unavailable functions, as they don't provide useful coverage
27+
// info.
28+
29+
@available(*, unavailable)
30+
public func unavailableFunction() -> Int {
31+
.random() ? 1 : 2
32+
}
33+
34+
struct TypeWithUnavailableMethods {
35+
@available(*, unavailable)
36+
func foo() -> Int {
37+
.random() ? 1 : 2
38+
}
39+
}
40+
41+
@available(*, unavailable)
42+
extension TypeWithUnavailableMethods {
43+
func bar() -> Int {
44+
.random() ? 1 : 2
45+
}
46+
public func baz() -> Int {
47+
.random() ? 1 : 2
48+
}
49+
}
50+
51+
@propertyWrapper
52+
struct Wrapper<T> {
53+
var wrappedValue: T
54+
}
55+
56+
@available(*, unavailable)
57+
struct UnavailableType {
58+
func foo() -> Int { .random() ? 1 : 2 }
59+
public func bar() -> Int { .random() ? 1 : 2 }
60+
61+
var qux: Int {
62+
// The && is here to test autoclosures.
63+
.random() && .random() ? 1 : 2
64+
}
65+
66+
var quux: Int {
67+
get { .random() ? 1 : 2 }
68+
set { _ = newValue }
69+
}
70+
71+
subscript(_ x: Int) -> Int {
72+
get { .random() ? 1 : 2 }
73+
set { quux = newValue }
74+
}
75+
76+
func baz(_ x: Int = .random() ? 0 : 1) {
77+
_ = {
78+
struct Nested {
79+
func evenMoreNested() -> () -> Int { { .random() ? 1 : 2 } }
80+
}
81+
func nested() -> () -> Int { { .random() ? 1 : 2 } }
82+
}
83+
}
84+
85+
var stored: Int = .random() ? 0 : 1
86+
87+
var storedClosure: Int = { .random() ? 0 : 1 }()
88+
89+
@Wrapper
90+
var wrappered = .random() ? 0 : 1
91+
92+
@Wrapper(wrappedValue: .random() ? 0 : 1)
93+
var alsoWrappered: Int
94+
}
95+
96+
@available(*, unavailable)
97+
class UnavailableClass {
98+
deinit {}
99+
}

0 commit comments

Comments
 (0)