Skip to content

Commit

Permalink
[Concurrency] Apply @preconcurrency to typealiases at the point of …
Browse files Browse the repository at this point in the history
…use.

When referencing a `@preconcurrency` typealias from code that does not
require strict concurrency, strip off `@Sendable` and global actor
types. This is consistent with references to functions/properties/etc.
that are `@preconcurrency`.

Fixes apple#60487, rdar://98343624.

(cherry picked from commit 5b88b96)
  • Loading branch information
DougGregor committed Aug 17, 2022
1 parent c254623 commit 37f9f0c
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 6 deletions.
47 changes: 41 additions & 6 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,27 @@ static Type getIdentityOpaqueTypeArchetypeType(
return OpaqueTypeArchetypeType::get(opaqueDecl, interfaceType, subs);
}

/// Adjust the underlying type of a typealias within the given context to
/// account for @preconcurrency.
static Type adjustTypeAliasTypeInContext(
Type type, TypeAliasDecl *aliasDecl, DeclContext *fromDC) {
if (!aliasDecl->preconcurrency())
return type;

if (contextRequiresStrictConcurrencyChecking(
fromDC,
[](const AbstractClosureExpr *closure) {
return closure->getType();
},
[](const ClosureExpr *closure) {
return closure->isIsolatedByPreconcurrency();
}))
return type;

return type->stripConcurrency(
/*recurse=*/true, /*dropGlobalActor=*/true);
}

Type TypeResolution::resolveTypeInContext(TypeDecl *typeDecl,
DeclContext *foundDC,
bool isSpecialized) const {
Expand All @@ -307,6 +328,13 @@ Type TypeResolution::resolveTypeInContext(TypeDecl *typeDecl,
return genericParam->getDeclaredInterfaceType();
}

/// Call this function before returning the underlying type of a typealias,
/// to adjust its type for concurrency.
auto adjustAliasType = [&](Type type) -> Type {
return adjustTypeAliasTypeInContext(
type, cast<TypeAliasDecl>(typeDecl), fromDC);
};

if (!isSpecialized) {
// If we are referring to a type within its own context, and we have either
// a generic type with no generic arguments or a non-generic type, use the
Expand Down Expand Up @@ -342,9 +370,9 @@ Type TypeResolution::resolveTypeInContext(TypeDecl *typeDecl,
if (ugAliasDecl == aliasDecl) {
if (getStage() == TypeResolutionStage::Structural &&
aliasDecl->getUnderlyingTypeRepr() != nullptr) {
return aliasDecl->getStructuralType();
return adjustAliasType(aliasDecl->getStructuralType());
}
return aliasDecl->getDeclaredInterfaceType();
return adjustAliasType(aliasDecl->getDeclaredInterfaceType());
}

extendedType = unboundGeneric->getParent();
Expand All @@ -356,9 +384,9 @@ Type TypeResolution::resolveTypeInContext(TypeDecl *typeDecl,
if (aliasType->getDecl() == aliasDecl) {
if (getStage() == TypeResolutionStage::Structural &&
aliasDecl->getUnderlyingTypeRepr() != nullptr) {
return aliasDecl->getStructuralType();
return adjustAliasType(aliasDecl->getStructuralType());
}
return aliasDecl->getDeclaredInterfaceType();
return adjustAliasType(aliasDecl->getDeclaredInterfaceType());
}
extendedType = aliasType->getParent();
continue;
Expand All @@ -381,9 +409,9 @@ Type TypeResolution::resolveTypeInContext(TypeDecl *typeDecl,
// Otherwise, return the appropriate type.
if (getStage() == TypeResolutionStage::Structural &&
aliasDecl->getUnderlyingTypeRepr() != nullptr) {
return aliasDecl->getStructuralType();
return adjustAliasType(aliasDecl->getStructuralType());
}
return aliasDecl->getDeclaredInterfaceType();
return adjustAliasType(aliasDecl->getDeclaredInterfaceType());
}

// When a nominal type used outside its context, return the unbound
Expand Down Expand Up @@ -1571,6 +1599,12 @@ static Type resolveNestedIdentTypeComponent(TypeResolution resolution,
AssociatedTypeDecl *inferredAssocType) {
bool hasUnboundOpener = !!resolution.getUnboundTypeOpener();

// Type aliases might require adjustment due to @preconcurrency.
if (auto aliasDecl = dyn_cast<TypeAliasDecl>(member)) {
memberType = adjustTypeAliasTypeInContext(
memberType, aliasDecl, resolution.getDeclContext());
}

if (options.contains(TypeResolutionFlags::SilenceErrors)) {
if (TypeChecker::isUnsupportedMemberTypeAccess(parentTy, member,
hasUnboundOpener)
Expand Down Expand Up @@ -4401,6 +4435,7 @@ class ExistentialTypeVisitor
}
} else if (auto *alias = dyn_cast_or_null<TypeAliasDecl>(comp->getBoundDecl())) {
auto type = Type(alias->getDeclaredInterfaceType()->getDesugaredType());

// If this is a type alias to a constraint type, the type
// alias name must be prefixed with 'any' to be used as an
// existential type.
Expand Down
34 changes: 34 additions & 0 deletions test/Concurrency/preconcurrency_typealias.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: %target-swift-frontend -typecheck -verify %s
// REQUIRES: concurrency

@preconcurrency @MainActor func f() { }
// expected-note@-1 2{{calls to global function 'f()' from outside of its actor context are implicitly asynchronous}}

@preconcurrency typealias FN = @Sendable () -> Void

struct Outer {
@preconcurrency typealias FN = @Sendable () -> Void
}

func test() {
var _: Outer.FN = {
f()
}

var _: FN = {
f()
print("Hello")
}
}

@available(SwiftStdlib 5.1, *)
func testAsync() async {
var _: Outer.FN = {
f() // expected-error{{call to main actor-isolated global function 'f()' in a synchronous nonisolated context}}
}

var _: FN = {
f() // expected-error{{call to main actor-isolated global function 'f()' in a synchronous nonisolated context}}
print("Hello")
}
}

0 comments on commit 37f9f0c

Please sign in to comment.