Skip to content

Commit 197a976

Browse files
committed
require an ownership specifier for noncopyable parameters
Per the current proposal, these are to be specified explicitly, as they form an important part of the API. Bonus: This commit includes a fix to make `CompileTimeConstTypeRepr` a proper `isa<>` subtype of `SpecifierTypeRepr`, since we forgot to add it to that type's `classof` function. resolves rdar://105480354
1 parent 87459d6 commit 197a976

File tree

7 files changed

+196
-25
lines changed

7 files changed

+196
-25
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6864,6 +6864,10 @@ ERROR(moveonly_cannot_conform_to_type, none,
68646864
(DescriptiveDeclKind, DeclName, Type))
68656865
ERROR(moveonly_non_final_class_cannot_contain_moveonly_field, none,
68666866
"non-final classes containing move only fields is not yet supported", ())
6867+
ERROR(moveonly_parameter_missing_ownership, none,
6868+
"noncopyable parameter must specify its ownership", ())
6869+
NOTE(moveonly_parameter_ownership_suggestion, none,
6870+
"add '%0' %1", (StringRef, StringRef))
68676871

68686872
//------------------------------------------------------------------------------
68696873
// MARK: Runtime discoverable attributes (@runtimeMetadata)

include/swift/AST/TypeRepr.h

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,8 @@ class SpecifierTypeRepr : public TypeRepr {
10541054
public:
10551055
SpecifierTypeRepr(TypeReprKind Kind, TypeRepr *Base, SourceLoc Loc)
10561056
: TypeRepr(Kind), Base(Base), SpecifierLoc(Loc) {
1057+
// ensure the kind passed-in matches up with our TypeRepr classof.
1058+
assert(SpecifierTypeRepr::classof(cast<TypeRepr>(this)));
10571059
}
10581060

10591061
TypeRepr *getBase() const { return Base; }
@@ -1063,7 +1065,8 @@ class SpecifierTypeRepr : public TypeRepr {
10631065
return T->getKind() == TypeReprKind::InOut ||
10641066
T->getKind() == TypeReprKind::Shared ||
10651067
T->getKind() == TypeReprKind::Owned ||
1066-
T->getKind() == TypeReprKind::Isolated;
1068+
T->getKind() == TypeReprKind::Isolated ||
1069+
T->getKind() == TypeReprKind::CompileTimeConst;
10671070
}
10681071
static bool classof(const SpecifierTypeRepr *T) { return true; }
10691072

@@ -1073,45 +1076,65 @@ class SpecifierTypeRepr : public TypeRepr {
10731076
void printImpl(ASTPrinter &Printer, const PrintOptions &Opts) const;
10741077
friend class TypeRepr;
10751078
};
1079+
1080+
// This repr holds types that are associated with some ownership specifier
1081+
// like 'inout', '__shared', etc. It's a simple grouping of those specifiers.
1082+
class OwnershipTypeRepr : public SpecifierTypeRepr {
1083+
public:
1084+
OwnershipTypeRepr(TypeReprKind Kind, TypeRepr *Base, SourceLoc OwnershipLoc)
1085+
: SpecifierTypeRepr(Kind, Base, OwnershipLoc) {
1086+
assert(OwnershipTypeRepr::classof(cast<TypeRepr>(this)));
1087+
}
1088+
1089+
static bool classof(const TypeRepr *T) {
1090+
return T->getKind() == TypeReprKind::InOut ||
1091+
T->getKind() == TypeReprKind::Shared ||
1092+
T->getKind() == TypeReprKind::Owned;
1093+
}
1094+
static bool classof(const OwnershipTypeRepr *T) { return true; }
1095+
friend class SpecifierTypeRepr;
1096+
};
10761097

10771098
/// An 'inout' type.
10781099
/// \code
10791100
/// x : inout Int
10801101
/// \endcode
1081-
class InOutTypeRepr : public SpecifierTypeRepr {
1102+
class InOutTypeRepr : public OwnershipTypeRepr {
10821103
public:
10831104
InOutTypeRepr(TypeRepr *Base, SourceLoc InOutLoc)
1084-
: SpecifierTypeRepr(TypeReprKind::InOut, Base, InOutLoc) {}
1105+
: OwnershipTypeRepr(TypeReprKind::InOut, Base, InOutLoc) {}
10851106

10861107
static bool classof(const TypeRepr *T) {
10871108
return T->getKind() == TypeReprKind::InOut;
10881109
}
10891110
static bool classof(const InOutTypeRepr *T) { return true; }
10901111
};
10911112

1092-
/// A 'shared' type.
1113+
/// A 'borrowing' parameter type.
10931114
/// \code
1094-
/// x : shared Int
1115+
/// x : borrowing Int
10951116
/// \endcode
1096-
class SharedTypeRepr : public SpecifierTypeRepr {
1117+
/// Historically, this attribute was spelled '__shared'.
1118+
class SharedTypeRepr : public OwnershipTypeRepr {
10971119
public:
10981120
SharedTypeRepr(TypeRepr *Base, SourceLoc SharedLoc)
1099-
: SpecifierTypeRepr(TypeReprKind::Shared, Base, SharedLoc) {}
1121+
: OwnershipTypeRepr(TypeReprKind::Shared, Base, SharedLoc) {}
11001122

11011123
static bool classof(const TypeRepr *T) {
11021124
return T->getKind() == TypeReprKind::Shared;
11031125
}
11041126
static bool classof(const SharedTypeRepr *T) { return true; }
11051127
};
11061128

1107-
/// A 'owned' type.
1129+
/// A 'consuming' parameter type.
11081130
/// \code
1109-
/// x : owned Int
1131+
/// x : consuming Int
11101132
/// \endcode
1111-
class OwnedTypeRepr : public SpecifierTypeRepr {
1133+
/// Historically, this attribute was spelled '__owned'.
1134+
class OwnedTypeRepr : public OwnershipTypeRepr {
11121135
public:
11131136
OwnedTypeRepr(TypeRepr *Base, SourceLoc OwnedLoc)
1114-
: SpecifierTypeRepr(TypeReprKind::Owned, Base, OwnedLoc) {}
1137+
: OwnershipTypeRepr(TypeReprKind::Owned, Base, OwnedLoc) {}
11151138

11161139
static bool classof(const TypeRepr *T) {
11171140
return T->getKind() == TypeReprKind::Owned;

lib/Parse/ParsePattern.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -607,13 +607,10 @@ mapParsedParameters(Parser &parser,
607607
if (auto *STR = dyn_cast<SpecifierTypeRepr>(unwrappedType)) {
608608
if (isa<IsolatedTypeRepr>(STR))
609609
param->setIsolated(true);
610-
unwrappedType = STR->getBase();
611-
continue;
612-
}
610+
else if (isa<CompileTimeConstTypeRepr>(STR))
611+
param->setCompileTimeConst(true);
613612

614-
if (auto *CTR = dyn_cast<CompileTimeConstTypeRepr>(unwrappedType)) {
615-
param->setCompileTimeConst(true);
616-
unwrappedType = CTR->getBase();
613+
unwrappedType = STR->getBase();
617614
continue;
618615
}
619616

lib/Sema/TypeCheckGeneric.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -653,12 +653,16 @@ GenericSignatureRequest::evaluate(Evaluator &evaluator,
653653
for (auto param : *params) {
654654
auto *typeRepr = param->getTypeRepr();
655655
if (typeRepr == nullptr)
656-
continue;
656+
continue;
657657

658658
auto paramOptions = baseOptions;
659659

660-
if (auto *specifier = dyn_cast<SpecifierTypeRepr>(typeRepr))
660+
if (auto *specifier = dyn_cast<SpecifierTypeRepr>(typeRepr)) {
661+
if (isa<OwnershipTypeRepr>(specifier))
662+
paramOptions |= TypeResolutionFlags::HasOwnership;
663+
661664
typeRepr = specifier->getBase();
665+
}
662666

663667
if (auto *packExpansion = dyn_cast<VarargTypeRepr>(typeRepr)) {
664668
paramOptions.setContext(TypeResolverContext::VariadicFunctionInput);

lib/Sema/TypeCheckType.cpp

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1990,6 +1990,8 @@ namespace {
19901990
}
19911991

19921992
bool diagnoseMoveOnly(TypeRepr *repr, Type genericArgTy);
1993+
bool diagnoseMoveOnlyMissingOwnership(TypeRepr *repr,
1994+
TypeResolutionOptions options);
19931995

19941996
bool diagnoseDisallowedExistential(TypeRepr *repr);
19951997

@@ -2044,7 +2046,7 @@ namespace {
20442046
Optional<SILResultInfo> &errorResult);
20452047
NeverNullType resolveDeclRefTypeRepr(DeclRefTypeRepr *repr,
20462048
TypeResolutionOptions options);
2047-
NeverNullType resolveSpecifierTypeRepr(SpecifierTypeRepr *repr,
2049+
NeverNullType resolveOwnershipTypeRepr(OwnershipTypeRepr *repr,
20482050
TypeResolutionOptions options);
20492051
NeverNullType resolveIsolatedTypeRepr(IsolatedTypeRepr *repr,
20502052
TypeResolutionOptions options);
@@ -2250,6 +2252,51 @@ bool TypeResolver::diagnoseMoveOnly(TypeRepr *repr, Type genericArgTy) {
22502252
return false;
22512253
}
22522254

2255+
/// Assuming this repr has resolved to a move-only / noncopyable type, checks
2256+
/// to see if that resolution happened in a context requiring an ownership
2257+
/// annotation. If it did and there was no ownership specified, emits a
2258+
/// diagnostic.
2259+
///
2260+
/// \returns true if an error diagnostic was emitted
2261+
bool TypeResolver::diagnoseMoveOnlyMissingOwnership(
2262+
TypeRepr *repr,
2263+
TypeResolutionOptions options) {
2264+
// only required on function inputs.
2265+
// we can ignore InoutFunctionInput since it's already got ownership.
2266+
if (!options.is(TypeResolverContext::FunctionInput))
2267+
return false;
2268+
2269+
// enum cases don't need to specify ownership for associated values
2270+
if (options.hasBase(TypeResolverContext::EnumElementDecl))
2271+
return false;
2272+
2273+
// otherwise, we require ownership.
2274+
if (options.contains(TypeResolutionFlags::HasOwnership))
2275+
return false;
2276+
2277+
diagnose(repr->getLoc(),
2278+
diag::moveonly_parameter_missing_ownership);
2279+
2280+
// FIXME: this should be 'borrowing'
2281+
diagnose(repr->getLoc(), diag::moveonly_parameter_ownership_suggestion,
2282+
"__shared", "for an immutable reference")
2283+
.fixItInsert(repr->getStartLoc(), "__shared ");
2284+
2285+
diagnose(repr->getLoc(), diag::moveonly_parameter_ownership_suggestion,
2286+
"inout", "for a mutable reference")
2287+
.fixItInsert(repr->getStartLoc(), "inout ");
2288+
2289+
// FIXME: this should be 'consuming'
2290+
diagnose(repr->getLoc(), diag::moveonly_parameter_ownership_suggestion,
2291+
"__owned", "to take the value from callers")
2292+
.fixItInsert(repr->getStartLoc(), "__owned ");
2293+
2294+
// to avoid duplicate diagnostics
2295+
repr->setInvalid();
2296+
2297+
return true;
2298+
}
2299+
22532300
NeverNullType TypeResolver::resolveType(TypeRepr *repr,
22542301
TypeResolutionOptions options) {
22552302
assert(repr && "Cannot validate null TypeReprs!");
@@ -2284,7 +2331,7 @@ NeverNullType TypeResolver::resolveType(TypeRepr *repr,
22842331
case TypeReprKind::InOut:
22852332
case TypeReprKind::Shared:
22862333
case TypeReprKind::Owned:
2287-
return resolveSpecifierTypeRepr(cast<SpecifierTypeRepr>(repr), options);
2334+
return resolveOwnershipTypeRepr(cast<OwnershipTypeRepr>(repr), options);
22882335

22892336
case TypeReprKind::Isolated:
22902337
return resolveIsolatedTypeRepr(cast<IsolatedTypeRepr>(repr), options);
@@ -4146,6 +4193,10 @@ TypeResolver::resolveDeclRefTypeRepr(DeclRefTypeRepr *repr,
41464193
}
41474194
}
41484195

4196+
// move-only types must have an ownership specifier when used as a parameter of a function.
4197+
if (result->isPureMoveOnly())
4198+
diagnoseMoveOnlyMissingOwnership(repr, options);
4199+
41494200
// Hack to apply context-specific @escaping to a typealias with an underlying
41504201
// function type.
41514202
if (result->is<FunctionType>())
@@ -4155,9 +4206,9 @@ TypeResolver::resolveDeclRefTypeRepr(DeclRefTypeRepr *repr,
41554206
}
41564207

41574208
NeverNullType
4158-
TypeResolver::resolveSpecifierTypeRepr(SpecifierTypeRepr *repr,
4209+
TypeResolver::resolveOwnershipTypeRepr(OwnershipTypeRepr *repr,
41594210
TypeResolutionOptions options) {
4160-
// inout is only valid for (non-Subscript and non-EnumCaseDecl)
4211+
// ownership is only valid for (non-Subscript and non-EnumCaseDecl)
41614212
// function parameters.
41624213
if (!options.is(TypeResolverContext::FunctionInput) ||
41634214
options.hasBase(TypeResolverContext::SubscriptDecl) ||
@@ -4177,10 +4228,10 @@ TypeResolver::resolveSpecifierTypeRepr(SpecifierTypeRepr *repr,
41774228
name = "inout";
41784229
break;
41794230
case TypeReprKind::Shared:
4180-
name = "__shared";
4231+
name = "__shared"; // FIXME: use 'borrowing'
41814232
break;
41824233
case TypeReprKind::Owned:
4183-
name = "__owned";
4234+
name = "__owned"; // FIXME: use 'consuming'
41844235
break;
41854236
default:
41864237
llvm_unreachable("unknown SpecifierTypeRepr kind");
@@ -4195,6 +4246,9 @@ TypeResolver::resolveSpecifierTypeRepr(SpecifierTypeRepr *repr,
41954246
options.setContext(TypeResolverContext::InoutFunctionInput);
41964247
}
41974248

4249+
// Remember that we've seen an ownership specifier for this base type.
4250+
options |= TypeResolutionFlags::HasOwnership;
4251+
41984252
return resolveType(repr->getBase(), options);
41994253
}
42004254

lib/Sema/TypeCheckType.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ enum class TypeResolutionFlags : uint16_t {
8181

8282
/// Whether this is a resolution based on a pack reference.
8383
FromPackReference = 1 << 12,
84+
85+
/// Whether this resolution happens under an explicit ownership specifier.
86+
HasOwnership = 1 << 13,
8487
};
8588

8689
/// Type resolution contexts that require special handling.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-move-only
2+
3+
// Coverage for check that requires some ownership specifier to be written
4+
// when a move-only / noncopyable type appears as a parameter of a function.
5+
6+
@_moveOnly
7+
struct MO {
8+
var x = 0
9+
}
10+
11+
struct Box<T> { var val: T }
12+
13+
@_moveOnly
14+
public struct NoncopyableWrapper<T> {
15+
var x: T
16+
}
17+
18+
class Inspector {
19+
func inspect(_ hasIt: inout MO, _ mo: MO, _ hasItAgain: __owned MO) {}
20+
// expected-error@-1 {{noncopyable parameter must specify its ownership}}
21+
// expected-note@-2 {{add '__shared' for an immutable reference}}{{41-41=__shared }}
22+
// expected-note@-3 {{add 'inout' for a mutable reference}}{{41-41=inout }}
23+
// expected-note@-4 {{add '__owned' to take the value from callers}}{{41-41=__owned }}
24+
}
25+
26+
// expected-error@+4 {{noncopyable parameter must specify its ownership}}
27+
// expected-note@+3 {{add '__shared' for an immutable reference}}{{20-20=__shared }}
28+
// expected-note@+2 {{add 'inout' for a mutable reference}}{{20-20=inout }}
29+
// expected-note@+1 {{add '__owned' to take the value from callers}}{{20-20=__owned }}
30+
func applier(_ f: (MO) -> (),
31+
_ v: MO) {}
32+
// expected-error@-1 {{noncopyable parameter must specify its ownership}}
33+
// expected-note@-2 {{add '__shared' for an immutable reference}}{{19-19=__shared }}
34+
// expected-note@-3 {{add 'inout' for a mutable reference}}{{19-19=inout }}
35+
// expected-note@-4 {{add '__owned' to take the value from callers}}{{19-19=__owned }}
36+
37+
func caller() {
38+
let f = { (_ mo1: MO, _ mo2: MO) in () }
39+
// expected-error@-1 2{{noncopyable parameter must specify its ownership}}
40+
// expected-note@-2 {{add '__shared' for an immutable reference}}{{21-21=__shared }}
41+
// expected-note@-3 {{add '__shared' for an immutable reference}}{{32-32=__shared }}
42+
// expected-note@-4 {{add 'inout' for a mutable reference}}{{21-21=inout }}
43+
// expected-note@-5 {{add 'inout' for a mutable reference}}{{32-32=inout }}
44+
// expected-note@-6 {{add '__owned' to take the value from callers}}{{21-21=__owned }}
45+
// expected-note@-7 {{add '__owned' to take the value from callers}}{{32-32=__owned }}
46+
47+
let g: (MO, MO) -> () = f
48+
// expected-error@-1 2{{noncopyable parameter must specify its ownership}}
49+
// expected-note@-2 {{add '__shared' for an immutable reference}}{{11-11=__shared }}
50+
// expected-note@-3 {{add '__shared' for an immutable reference}}{{15-15=__shared }}
51+
// expected-note@-4 {{add 'inout' for a mutable reference}}{{11-11=inout }}
52+
// expected-note@-5 {{add 'inout' for a mutable reference}}{{15-15=inout }}
53+
// expected-note@-6 {{add '__owned' to take the value from callers}}{{11-11=__owned }}
54+
// expected-note@-7 {{add '__owned' to take the value from callers}}{{15-15=__owned }}
55+
56+
let partialG = { g($0, MO()) }
57+
58+
let _: Box<(MO) -> ()> = Box(val: partialG)
59+
// expected-error@-1 {{noncopyable parameter must specify its ownership}}
60+
// expected-note@-2 {{add '__shared' for an immutable reference}}{{15-15=__shared }}
61+
// expected-note@-3 {{add 'inout' for a mutable reference}}{{15-15=inout }}
62+
// expected-note@-4 {{add '__owned' to take the value from callers}}{{15-15=__owned }}
63+
64+
let _: Box<(inout MO) -> ()>? = nil
65+
let _: Box<(__shared MO) -> ()>? = nil
66+
67+
let _: Box<(MO) -> ()>? = nil
68+
// expected-error@-1 {{noncopyable parameter must specify its ownership}}
69+
// expected-note@-2 {{add '__shared' for an immutable reference}}{{15-15=__shared }}
70+
// expected-note@-3 {{add 'inout' for a mutable reference}}{{15-15=inout }}
71+
// expected-note@-4 {{add '__owned' to take the value from callers}}{{15-15=__owned }}
72+
73+
applier(partialG, MO())
74+
}
75+
76+
func takeGeneric<T>(_ x: NoncopyableWrapper<T>) {}
77+
// expected-error@-1 {{noncopyable parameter must specify its ownership}}
78+
// expected-note@-2 {{add '__shared' for an immutable reference}}{{26-26=__shared }}
79+
// expected-note@-3 {{add 'inout' for a mutable reference}}{{26-26=inout }}
80+
// expected-note@-4 {{add '__owned' to take the value from callers}}{{26-26=__owned }}
81+
82+
func takeInstantiated(_ x: NoncopyableWrapper<Int>) {}
83+
// expected-error@-1 {{noncopyable parameter must specify its ownership}}
84+
// expected-note@-2 {{add '__shared' for an immutable reference}}{{28-28=__shared }}
85+
// expected-note@-3 {{add 'inout' for a mutable reference}}{{28-28=inout }}
86+
// expected-note@-4 {{add '__owned' to take the value from callers}}{{28-28=__owned }}

0 commit comments

Comments
 (0)