Skip to content

Commit 8873f19

Browse files
authored
Merge pull request swiftlang#63674 from kavon/require-ownership-specifier-noncopyable
Require parameters of noncopyable type to specify ownership
2 parents 57ceb50 + c948f3c commit 8873f19

18 files changed

+588
-417
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.

test/Constraints/moveonly_constraints.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ enum Maybe<T> {
4242
case just(T)
4343
}
4444

45-
func takeConcrete(_ m: MO) {}
45+
func takeConcrete(_ m: __shared MO) {}
4646
func takeGeneric<T>(_ t: T) {}
4747
func takeGenericSendable<T>(_ t: T) where T: Sendable {}
4848
func takeMaybe<T>(_ m: Maybe<T>) {}
@@ -71,7 +71,7 @@ func testAny() {
7171
takeAny(MO()) // expected-error {{move-only type 'MO' cannot be used with generics yet}}
7272
}
7373

74-
func testBasic(_ mo: MO) {
74+
func testBasic(_ mo: __shared MO) {
7575
takeConcrete(globalMO)
7676
takeConcrete(MO())
7777

@@ -139,7 +139,7 @@ func checkMethodCalls() {
139139
takeMaybe(true ? .none : .just(MO())) // expected-error 3{{move-only type 'MO' cannot be used with generics yet}}
140140
}
141141

142-
func checkCasting(_ b: any Box, _ mo: MO, _ a: Any) {
142+
func checkCasting(_ b: any Box, _ mo: __shared MO, _ a: Any) {
143143
// casting dynamically is allowed, but should always fail since you can't
144144
// construct such a type.
145145
let box = b as! ValBox<MO> // expected-error {{move-only type 'MO' cannot be used with generics yet}}
@@ -221,7 +221,7 @@ func checkCasting(_ b: any Box, _ mo: MO, _ a: Any) {
221221

222222
}
223223

224-
func checkStdlibTypes(_ mo: MO) {
224+
func checkStdlibTypes(_ mo: __shared MO) {
225225
let _: [MO] = // expected-error {{move-only type 'MO' cannot be used with generics yet}}
226226
[MO(), MO()]
227227
let _: [MO] = // expected-error {{move-only type 'MO' cannot be used with generics yet}}

test/Interpreter/moveonly_bufferview.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public struct BufferView<T> {
2323
}
2424

2525
extension Array {
26-
public mutating func withBufferView<U>(_ f: (BufferView<Element>) -> U) -> U {
26+
public mutating func withBufferView<U>(_ f: (__shared BufferView<Element>) -> U) -> U {
2727
return withUnsafeBufferPointer {
2828
return f(BufferView(ptr: $0))
2929
}

0 commit comments

Comments
 (0)