Skip to content

Commit fb5d544

Browse files
authored
Merge pull request #60596 from DougGregor/preconcurrency-typealias-5.7
Apply @preconcurrency to typealiases at the point of use
2 parents c254623 + 5fd84c3 commit fb5d544

File tree

4 files changed

+116
-7
lines changed

4 files changed

+116
-7
lines changed

lib/Sema/TypeCheckDecl.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1814,6 +1814,8 @@ UnderlyingTypeRequest::evaluate(Evaluator &evaluator,
18141814
TypeResolutionOptions options((typeAlias->getGenericParams()
18151815
? TypeResolverContext::GenericTypeAliasDecl
18161816
: TypeResolverContext::TypeAliasDecl));
1817+
if (typeAlias->preconcurrency())
1818+
options |= TypeResolutionFlags::Preconcurrency;
18171819

18181820
// This can happen when code completion is attempted inside
18191821
// of typealias underlying type e.g. `typealias F = () -> Int#^TOK^#`
@@ -2094,8 +2096,11 @@ ResultTypeRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
20942096
: ErrorType::get(ctx));
20952097
}
20962098

2097-
const auto options =
2099+
auto options =
20982100
TypeResolutionOptions(TypeResolverContext::FunctionResult);
2101+
if (decl->preconcurrency())
2102+
options |= TypeResolutionFlags::Preconcurrency;
2103+
20992104
auto *const dc = decl->getInnermostDeclContext();
21002105
return TypeResolution::forInterface(dc, options,
21012106
/*unboundTyOpener*/ nullptr,
@@ -2196,6 +2201,13 @@ static Type validateParameterType(ParamDecl *decl) {
21962201
options = TypeResolutionOptions(TypeResolverContext::EnumElementDecl);
21972202
}
21982203

2204+
// Set the "preconcurrency" flag if this is a parameter of a preconcurrency
2205+
// declaration.
2206+
if (auto decl = dc->getAsDecl()) {
2207+
if (decl->preconcurrency())
2208+
options |= TypeResolutionFlags::Preconcurrency;
2209+
}
2210+
21992211
// If the element is a variadic parameter, resolve the parameter type as if
22002212
// it were in non-parameter position, since we want functions to be
22012213
// @escaping in this case.

lib/Sema/TypeCheckType.cpp

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,37 @@ static Type getIdentityOpaqueTypeArchetypeType(
286286
return OpaqueTypeArchetypeType::get(opaqueDecl, interfaceType, subs);
287287
}
288288

289+
/// Adjust the underlying type of a typealias within the given context to
290+
/// account for @preconcurrency.
291+
static Type adjustTypeAliasTypeInContext(
292+
Type type, TypeAliasDecl *aliasDecl, DeclContext *fromDC,
293+
TypeResolutionOptions options) {
294+
// If we are in a @preconcurrency declaration, don't adjust the types of
295+
// type aliases.
296+
if (options.contains(TypeResolutionFlags::Preconcurrency))
297+
return type;
298+
299+
// If the type alias itself isn't marked with @preconcurrency, don't
300+
// adjust the type.
301+
if (!aliasDecl->preconcurrency())
302+
return type;
303+
304+
// Only adjust the type within a strict concurrency context, so we don't
305+
// change the types as seen by code that hasn't adopted concurrency.
306+
if (contextRequiresStrictConcurrencyChecking(
307+
fromDC,
308+
[](const AbstractClosureExpr *closure) {
309+
return closure->getType();
310+
},
311+
[](const ClosureExpr *closure) {
312+
return closure->isIsolatedByPreconcurrency();
313+
}))
314+
return type;
315+
316+
return type->stripConcurrency(
317+
/*recurse=*/true, /*dropGlobalActor=*/true);
318+
}
319+
289320
Type TypeResolution::resolveTypeInContext(TypeDecl *typeDecl,
290321
DeclContext *foundDC,
291322
bool isSpecialized) const {
@@ -307,6 +338,13 @@ Type TypeResolution::resolveTypeInContext(TypeDecl *typeDecl,
307338
return genericParam->getDeclaredInterfaceType();
308339
}
309340

341+
/// Call this function before returning the underlying type of a typealias,
342+
/// to adjust its type for concurrency.
343+
auto adjustAliasType = [&](Type type) -> Type {
344+
return adjustTypeAliasTypeInContext(
345+
type, cast<TypeAliasDecl>(typeDecl), fromDC, options);
346+
};
347+
310348
if (!isSpecialized) {
311349
// If we are referring to a type within its own context, and we have either
312350
// a generic type with no generic arguments or a non-generic type, use the
@@ -342,9 +380,9 @@ Type TypeResolution::resolveTypeInContext(TypeDecl *typeDecl,
342380
if (ugAliasDecl == aliasDecl) {
343381
if (getStage() == TypeResolutionStage::Structural &&
344382
aliasDecl->getUnderlyingTypeRepr() != nullptr) {
345-
return aliasDecl->getStructuralType();
383+
return adjustAliasType(aliasDecl->getStructuralType());
346384
}
347-
return aliasDecl->getDeclaredInterfaceType();
385+
return adjustAliasType(aliasDecl->getDeclaredInterfaceType());
348386
}
349387

350388
extendedType = unboundGeneric->getParent();
@@ -356,9 +394,9 @@ Type TypeResolution::resolveTypeInContext(TypeDecl *typeDecl,
356394
if (aliasType->getDecl() == aliasDecl) {
357395
if (getStage() == TypeResolutionStage::Structural &&
358396
aliasDecl->getUnderlyingTypeRepr() != nullptr) {
359-
return aliasDecl->getStructuralType();
397+
return adjustAliasType(aliasDecl->getStructuralType());
360398
}
361-
return aliasDecl->getDeclaredInterfaceType();
399+
return adjustAliasType(aliasDecl->getDeclaredInterfaceType());
362400
}
363401
extendedType = aliasType->getParent();
364402
continue;
@@ -381,9 +419,9 @@ Type TypeResolution::resolveTypeInContext(TypeDecl *typeDecl,
381419
// Otherwise, return the appropriate type.
382420
if (getStage() == TypeResolutionStage::Structural &&
383421
aliasDecl->getUnderlyingTypeRepr() != nullptr) {
384-
return aliasDecl->getStructuralType();
422+
return adjustAliasType(aliasDecl->getStructuralType());
385423
}
386-
return aliasDecl->getDeclaredInterfaceType();
424+
return adjustAliasType(aliasDecl->getDeclaredInterfaceType());
387425
}
388426

389427
// When a nominal type used outside its context, return the unbound
@@ -1571,6 +1609,13 @@ static Type resolveNestedIdentTypeComponent(TypeResolution resolution,
15711609
AssociatedTypeDecl *inferredAssocType) {
15721610
bool hasUnboundOpener = !!resolution.getUnboundTypeOpener();
15731611

1612+
// Type aliases might require adjustment due to @preconcurrency.
1613+
if (auto aliasDecl = dyn_cast<TypeAliasDecl>(member)) {
1614+
memberType = adjustTypeAliasTypeInContext(
1615+
memberType, aliasDecl, resolution.getDeclContext(),
1616+
resolution.getOptions());
1617+
}
1618+
15741619
if (options.contains(TypeResolutionFlags::SilenceErrors)) {
15751620
if (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member,
15761621
hasUnboundOpener)
@@ -4401,6 +4446,7 @@ class ExistentialTypeVisitor
44014446
}
44024447
} else if (auto *alias = dyn_cast_or_null<TypeAliasDecl>(comp->getBoundDecl())) {
44034448
auto type = Type(alias->getDeclaredInterfaceType()->getDesugaredType());
4449+
44044450
// If this is a type alias to a constraint type, the type
44054451
// alias name must be prefixed with 'any' to be used as an
44064452
// existential type.

lib/Sema/TypeCheckType.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ enum class TypeResolutionFlags : uint16_t {
6767
/// Needed to enforce that \c any P<some Q> does not resolve to a
6868
/// parameterized existential with an opaque type constraint.
6969
DisallowOpaqueTypes = 1 << 9,
70+
71+
/// We are in a `@preconcurrency` declaration.
72+
Preconcurrency = 1 << 10,
7073
};
7174

7275
/// Type resolution contexts that require special handling.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: %target-swift-frontend -typecheck -verify %s
2+
// REQUIRES: concurrency
3+
4+
@preconcurrency @MainActor func f() { }
5+
// expected-note@-1 2{{calls to global function 'f()' from outside of its actor context are implicitly asynchronous}}
6+
7+
@preconcurrency typealias FN = @Sendable () -> Void
8+
9+
struct Outer {
10+
@preconcurrency typealias FN = @Sendable () -> Void
11+
}
12+
13+
@preconcurrency func preconcurrencyFunc(callback: FN) {}
14+
15+
func test() {
16+
var _: Outer.FN = {
17+
f()
18+
}
19+
20+
var _: FN = {
21+
f()
22+
print("Hello")
23+
}
24+
25+
var mutableVariable = 0
26+
preconcurrencyFunc {
27+
mutableVariable += 1 // no sendable warning
28+
}
29+
mutableVariable += 1
30+
}
31+
32+
@available(SwiftStdlib 5.1, *)
33+
func testAsync() async {
34+
var _: Outer.FN = {
35+
f() // expected-error{{call to main actor-isolated global function 'f()' in a synchronous nonisolated context}}
36+
}
37+
38+
var _: FN = {
39+
f() // expected-error{{call to main actor-isolated global function 'f()' in a synchronous nonisolated context}}
40+
print("Hello")
41+
}
42+
43+
var mutableVariable = 0
44+
preconcurrencyFunc {
45+
mutableVariable += 1 // expected-warning{{mutation of captured var 'mutableVariable' in concurrently-executing code; this is an error in Swift 6}}
46+
}
47+
mutableVariable += 1
48+
}

0 commit comments

Comments
 (0)