Skip to content

Commit f33077a

Browse files
committed
Ensure that @safe(unchecked) only applies to bodies (not declarations)
1 parent e260d65 commit f33077a

File tree

4 files changed

+119
-67
lines changed

4 files changed

+119
-67
lines changed

include/swift/AST/AvailabilityContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ class AvailabilityContext {
106106
constrainWithDeclAndPlatformRange(const Decl *decl,
107107
const AvailabilityRange &platformRange);
108108

109+
/// Constrain to allow unsafe code.
110+
void constrainWithAllowsUnsafe(ASTContext &ctx);
111+
109112
/// Returns true if `other` is as available or is more available.
110113
bool isContainedIn(const AvailabilityContext other) const;
111114

lib/AST/AvailabilityContext.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,15 @@ void AvailabilityContext::constrainWithDecl(const Decl *decl) {
194194
constrainWithDeclAndPlatformRange(decl, AvailabilityRange::alwaysAvailable());
195195
}
196196

197+
void AvailabilityContext::constrainWithAllowsUnsafe(ASTContext &ctx) {
198+
if (allowsUnsafe())
199+
return;
200+
201+
PlatformInfo platformInfo{Info->Platform};
202+
platformInfo.AllowsUnsafe = true;
203+
Info = Storage::get(platformInfo, ctx);
204+
}
205+
197206
void AvailabilityContext::constrainWithPlatformRange(
198207
const AvailabilityRange &platformRange, ASTContext &ctx) {
199208
PlatformInfo platformAvailability{Info->Platform};

lib/AST/Decl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1080,7 +1080,7 @@ bool Decl::isUnsafe() const {
10801080
}
10811081

10821082
bool Decl::allowsUnsafe() const {
1083-
return getAttrs().hasAttribute<SafeAttr>() || isUnsafe();
1083+
return isUnsafe();
10841084
}
10851085

10861086
Type AbstractFunctionDecl::getThrownInterfaceType() const {

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 106 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -869,100 +869,140 @@ class AvailabilityScopeBuilder : private ASTWalker {
869869
return Range;
870870
}
871871

872-
// Creates an implicit decl scope specifying the deployment target for
873-
// `range` in decl `D`.
874-
AvailabilityScope *
875-
createImplicitDeclContextForDeploymentTarget(Decl *D, SourceRange range) {
876-
const AvailabilityContext Availability =
877-
constrainCurrentAvailabilityWithPlatformRange(
878-
AvailabilityRange::forDeploymentTarget(Context));
879-
return AvailabilityScope::createForDeclImplicit(
880-
Context, D, getCurrentScope(), Availability, range);
881-
}
882-
883-
void buildContextsForBodyOfDecl(Decl *D) {
884-
// Are we already constrained by the deployment target? If not, adding
885-
// new contexts won't change availability.
886-
if (isCurrentScopeContainedByDeploymentTarget())
887-
return;
888-
872+
/// Enumerate the AST nodes and their corresponding source ranges for
873+
/// the body (or bodies) of the given declaration.
874+
void enumerateBodyRanges(
875+
Decl *decl,
876+
llvm::function_ref<void(Decl *decl, ASTNode body, SourceRange)> acceptBody
877+
) {
889878
// Top level code always uses the deployment target.
890-
if (auto tlcd = dyn_cast<TopLevelCodeDecl>(D)) {
879+
if (auto tlcd = dyn_cast<TopLevelCodeDecl>(decl)) {
891880
if (auto bodyStmt = tlcd->getBody()) {
892-
pushDeclBodyContext(
893-
tlcd, {{bodyStmt, createImplicitDeclContextForDeploymentTarget(
894-
tlcd, refinementSourceRangeForDecl(tlcd))}});
881+
acceptBody(tlcd, bodyStmt, refinementSourceRangeForDecl(tlcd));
895882
}
896883
return;
897884
}
898885

899-
// Function bodies use the deployment target if they are within the module's
900-
// resilience domain.
901-
if (auto afd = dyn_cast<AbstractFunctionDecl>(D)) {
902-
if (!afd->isImplicit() &&
903-
afd->getResilienceExpansion() != ResilienceExpansion::Minimal) {
886+
// For functions, provide the body source range.
887+
if (auto afd = dyn_cast<AbstractFunctionDecl>(decl)) {
888+
if (!afd->isImplicit()) {
904889
if (auto body = afd->getBody(/*canSynthesize*/ false)) {
905-
pushDeclBodyContext(
906-
afd, {{body, createImplicitDeclContextForDeploymentTarget(
907-
afd, afd->getBodySourceRange())}});
890+
acceptBody(afd, body, afd->getBodySourceRange());
908891
}
909892
}
910893
return;
911894
}
912895

913-
// Pattern binding declarations can have children corresponding to property
914-
// wrappers and the initial values provided in each pattern binding entry
915-
if (auto *pbd = dyn_cast<PatternBindingDecl>(D)) {
916-
llvm::SmallVector<std::pair<ASTNode, AvailabilityScope *>, 4>
917-
nodesAndScopes;
918-
896+
// Pattern binding declarations have initial values that are their
897+
// bodies.
898+
if (auto *pbd = dyn_cast<PatternBindingDecl>(decl)) {
919899
for (unsigned index : range(pbd->getNumPatternEntries())) {
920900
auto var = pbd->getAnchoringVarDecl(index);
921901
if (!var)
922902
continue;
923903

924-
// Var decls may have associated pattern binding decls or property
925-
// wrappers with init expressions. Those expressions need to be
926-
// constrained to the deployment target unless they are exposed to
927-
// clients.
928-
if (!var->hasInitialValue() || var->isInitExposedToClients())
929-
continue;
930-
931904
auto *initExpr = pbd->getInit(index);
932905
if (initExpr && !initExpr->isImplicit()) {
933906
assert(initExpr->getSourceRange().isValid());
934907

935908
// Create a scope for the init written in the source.
936-
nodesAndScopes.push_back(
937-
{initExpr, createImplicitDeclContextForDeploymentTarget(
938-
var, initExpr->getSourceRange())});
909+
acceptBody(var, initExpr, initExpr->getSourceRange());
939910
}
940911
}
912+
return;
913+
}
914+
}
915+
916+
// Creates an implicit decl scope specifying the deployment target for
917+
// `range` in decl `D`.
918+
AvailabilityScope *
919+
createImplicitDeclContextForDeploymentTarget(Decl *D, SourceRange range) {
920+
const AvailabilityContext Availability =
921+
constrainCurrentAvailabilityWithPlatformRange(
922+
AvailabilityRange::forDeploymentTarget(Context));
923+
return AvailabilityScope::createForDeclImplicit(
924+
Context, D, getCurrentScope(), Availability, range);
925+
}
926+
927+
/// Determine whether the body of the given declaration has
928+
/// deployment-target availability.
929+
static bool bodyIsDeploymentTarget(Decl *decl) {
930+
if (auto afd = dyn_cast<AbstractFunctionDecl>(decl)) {
931+
return afd->getResilienceExpansion() != ResilienceExpansion::Minimal;
932+
}
933+
934+
if (auto var = dyn_cast<VarDecl>(decl)) {
935+
// Var decls may have associated pattern binding decls or property
936+
// wrappers with init expressions. Those expressions need to be
937+
// constrained to the deployment target unless they are exposed to
938+
// clients.
939+
return var->hasInitialValue() && !var->isInitExposedToClients();
940+
}
941+
942+
return true;
943+
}
944+
945+
void buildContextsForBodyOfDecl(Decl *D) {
946+
// Are we already constrained by the deployment target and the declaration
947+
// doesn't explicitly allow unsafe constructs in its definition, adding
948+
// new contexts won't change availability.
949+
bool allowsUnsafe = D->getAttrs().hasAttribute<SafeAttr>();
950+
if (isCurrentScopeContainedByDeploymentTarget() && !allowsUnsafe)
951+
return;
952+
953+
// Enumerate all of the body scopes to apply availability.
954+
llvm::SmallVector<std::pair<ASTNode, AvailabilityScope *>, 4>
955+
nodesAndScopes;
956+
enumerateBodyRanges(D, [&](Decl *decl, ASTNode body, SourceRange range) {
957+
auto availability = getCurrentScope()->getAvailabilityContext();
958+
959+
// Apply deployment-target availability if appropriate for this body.
960+
if (!isCurrentScopeContainedByDeploymentTarget() &&
961+
bodyIsDeploymentTarget(decl)) {
962+
availability.constrainWithPlatformRange(
963+
AvailabilityRange::forDeploymentTarget(Context), Context);
964+
}
965+
966+
// Allow unsafe if appropriate for this body.
967+
if (allowsUnsafe) {
968+
availability.constrainWithAllowsUnsafe(Context);
969+
}
941970

942-
if (nodesAndScopes.size() > 0)
943-
pushDeclBodyContext(pbd, nodesAndScopes);
944-
945-
// Ideally any init expression would be returned by `getInit()` above.
946-
// However, for property wrappers it doesn't get populated until
947-
// typechecking completes (which is too late). Instead, we find the
948-
// the property wrapper attribute and use its source range to create a
949-
// scope for the initializer expression.
950-
//
951-
// FIXME: Since we don't have an expression here, we can't build out its
952-
// scope. If the Expr that will eventually be created contains a closure
953-
// expression, then it might have AST nodes that need to be refined. For
954-
// example, property wrapper initializers that takes block arguments
955-
// are not handled correctly because of this (rdar://77841331).
956-
if (auto firstVar = pbd->getAnchoringVarDecl(0)) {
957-
if (firstVar->hasInitialValue() &&
958-
!firstVar->isInitExposedToClients()) {
959-
for (auto *wrapper : firstVar->getAttachedPropertyWrappers()) {
960-
createImplicitDeclContextForDeploymentTarget(firstVar,
961-
wrapper->getRange());
971+
nodesAndScopes.push_back({
972+
body,
973+
AvailabilityScope::createForDeclImplicit(
974+
Context, decl, getCurrentScope(), availability, range)
975+
});
976+
});
977+
978+
if (nodesAndScopes.size() > 0)
979+
pushDeclBodyContext(D, nodesAndScopes);
980+
981+
if (!isCurrentScopeContainedByDeploymentTarget()) {
982+
// Pattern binding declarations can have children corresponding to property
983+
// wrappers, which we handle separately.
984+
if (auto *pbd = dyn_cast<PatternBindingDecl>(D)) {
985+
// Ideally any init expression would be returned by `getInit()` above.
986+
// However, for property wrappers it doesn't get populated until
987+
// typechecking completes (which is too late). Instead, we find the
988+
// the property wrapper attribute and use its source range to create a
989+
// scope for the initializer expression.
990+
//
991+
// FIXME: Since we don't have an expression here, we can't build out its
992+
// scope. If the Expr that will eventually be created contains a closure
993+
// expression, then it might have AST nodes that need to be refined. For
994+
// example, property wrapper initializers that takes block arguments
995+
// are not handled correctly because of this (rdar://77841331).
996+
if (auto firstVar = pbd->getAnchoringVarDecl(0)) {
997+
if (firstVar->hasInitialValue() &&
998+
!firstVar->isInitExposedToClients()) {
999+
for (auto *wrapper : firstVar->getAttachedPropertyWrappers()) {
1000+
createImplicitDeclContextForDeploymentTarget(firstVar,
1001+
wrapper->getRange());
1002+
}
9621003
}
9631004
}
9641005
}
965-
return;
9661006
}
9671007
}
9681008

0 commit comments

Comments
 (0)