Skip to content

Commit b42f261

Browse files
authored
Merge pull request swiftlang#36083 from DougGregor/concurrentvalue-keypaths
2 parents 68da1f5 + 65d6af6 commit b42f261

File tree

4 files changed

+36
-95
lines changed

4 files changed

+36
-95
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4316,6 +4316,9 @@ WARNING(non_concurrent_property_type,none,
43164316
"cannot use %0 %1 with a non-concurrent-value type %2 "
43174317
"%select{across actors|from concurrently-executed code}3",
43184318
(DescriptiveDeclKind, DeclName, Type, bool))
4319+
WARNING(non_concurrent_keypath_capture,none,
4320+
"cannot form key path that captures non-concurrent-value type %0",
4321+
(Type))
43194322
ERROR(non_concurrent_type_member,none,
43204323
"%select{stored property %1|associated value %1}0 of "
43214324
"'ConcurrentValue'-conforming %2 %3 has non-concurrent-value type %4",

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 16 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -793,71 +793,6 @@ static bool isConcurrentValueType(const DeclContext *dc, Type type) {
793793
return checker.visit(type);
794794
}
795795

796-
Optional<NonConcurrentType> NonConcurrentType::get(
797-
const DeclContext *dc, ConcreteDeclRef declRef) {
798-
// For functions, check the parameter and result types.
799-
SubstitutionMap subs = declRef.getSubstitutions();
800-
if (auto function = dyn_cast<AbstractFunctionDecl>(declRef.getDecl())) {
801-
for (auto param : *function->getParameters()) {
802-
Type paramType = param->getInterfaceType().subst(subs);
803-
if (!isConcurrentValueType(dc, paramType)) {
804-
return NonConcurrentType {
805-
Kind::Parameter, ConcreteDeclRef(param, subs), paramType };
806-
}
807-
}
808-
809-
// Check the result type of a function.
810-
if (auto func = dyn_cast<FuncDecl>(function)) {
811-
Type resultType = func->getResultInterfaceType().subst(subs);
812-
if (!isConcurrentValueType(dc, resultType)) {
813-
return NonConcurrentType { Kind::Result, declRef, resultType };
814-
}
815-
}
816-
817-
// Check the "self" type of an instance method.
818-
if (function->isInstanceMember()) {
819-
if (auto selfParam = function->getImplicitSelfDecl()) {
820-
Type paramType = selfParam->getInterfaceType().subst(subs);
821-
if (!isConcurrentValueType(dc, paramType)) {
822-
return NonConcurrentType {
823-
Kind::Parameter, ConcreteDeclRef(selfParam, subs),
824-
paramType };
825-
}
826-
}
827-
}
828-
} else if (auto var = dyn_cast<VarDecl>(declRef.getDecl())) {
829-
Type propertyType = var->getValueInterfaceType().subst(subs);
830-
if (!isConcurrentValueType(dc, propertyType)) {
831-
return NonConcurrentType {
832-
Kind::Property, declRef, propertyType };
833-
}
834-
}
835-
836-
return None;
837-
}
838-
839-
void NonConcurrentType::diagnose(SourceLoc loc) {
840-
ASTContext &ctx = declRef.getDecl()->getASTContext();
841-
842-
switch (kind) {
843-
case Parameter:
844-
ctx.Diags.diagnose(loc, diag::non_concurrent_param_type, type);
845-
break;
846-
847-
case Result:
848-
ctx.Diags.diagnose(loc, diag::non_concurrent_result_type, type);
849-
break;
850-
851-
case Property: {
852-
auto var = cast<VarDecl>(declRef.getDecl());
853-
ctx.Diags.diagnose(loc, diag::non_concurrent_property_type,
854-
var->getDescriptiveKind(), var->getName(),
855-
type, var->isLocalCapture());
856-
break;
857-
}
858-
}
859-
}
860-
861796
static bool diagnoseNonConcurrentParameter(
862797
SourceLoc loc, ConcurrentReferenceKind refKind, ConcreteDeclRef declRef,
863798
ParamDecl *param, Type paramType) {
@@ -1176,6 +1111,22 @@ namespace {
11761111
}
11771112
}
11781113

1114+
// Key paths require any captured values to be ConcurrentValue-conforming.
1115+
if (auto keyPath = dyn_cast<KeyPathExpr>(expr)) {
1116+
for (const auto &component : keyPath->getComponents()) {
1117+
auto indexExpr = component.getIndexExpr();
1118+
if (!indexExpr || !indexExpr->getType())
1119+
continue;
1120+
1121+
if (ctx.LangOpts.EnableExperimentalConcurrentValueChecking &&
1122+
!isConcurrentValueType(getDeclContext(), indexExpr->getType())) {
1123+
ctx.Diags.diagnose(
1124+
component.getLoc(), diag::non_concurrent_keypath_capture,
1125+
indexExpr->getType());
1126+
}
1127+
}
1128+
}
1129+
11791130
// The children of #selector expressions are not evaluated, so we do not
11801131
// need to do isolation checking there. This is convenient because such
11811132
// expressions tend to violate restrictions on the use of instance

lib/Sema/TypeCheckConcurrency.h

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -66,36 +66,6 @@ enum class ConcurrentReferenceKind {
6666
ConcurrentFunction,
6767
};
6868

69-
/// Describes why or where a particular entity has a non-concurrent-value type.
70-
struct NonConcurrentType {
71-
enum Kind {
72-
/// A function parameter is a non-concurrent-value type.
73-
Parameter,
74-
/// The result of a function is a non-concurrent-value type.
75-
Result,
76-
/// The type of a property is a non-concurrent-value type.
77-
Property,
78-
} kind;
79-
80-
/// The declaration reference.
81-
ConcreteDeclRef declRef;
82-
83-
/// The non-concurrent-value type being diagnosed.
84-
Type type;
85-
86-
/// Determine whether a reference to the given declaration involves a
87-
/// non-concurrent-value type. If it does, return the reason. Otherwise,
88-
/// return \c None.
89-
///
90-
/// \param dc The declaration context from which the reference occurs.
91-
/// \param declRef The reference to the declaration.
92-
static Optional<NonConcurrentType> get(
93-
const DeclContext *dc, ConcreteDeclRef declRef);
94-
95-
/// Diagnose the non-concurrent-value type at the given source location.
96-
void diagnose(SourceLoc loc);
97-
};
98-
9969
/// The isolation restriction in effect for a given declaration that is
10070
/// referenced from source.
10171
class ActorIsolationRestriction {

test/Concurrency/concurrent_value_checking.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,23 @@ func testConcurrency() {
122122
}
123123
}
124124

125+
// ----------------------------------------------------------------------
126+
// ConcurrentValue restriction on key paths.
127+
// ----------------------------------------------------------------------
128+
class NC: Hashable {
129+
func hash(into: inout Hasher) { }
130+
static func==(_: NC, _: NC) -> Bool { true }
131+
}
132+
133+
class HasNC {
134+
var dict: [NC: Int] = [:]
135+
}
136+
137+
func testKeyPaths(dict: [NC: Int], nc: NC) {
138+
_ = \HasNC.dict[nc] // expected-warning{{cannot form key path that captures non-concurrent-value type 'NC'}}
139+
}
140+
141+
125142
// ----------------------------------------------------------------------
126143
// ConcurrentValue restriction on conformances.
127144
// ----------------------------------------------------------------------

0 commit comments

Comments
 (0)