Skip to content

Commit bf2816b

Browse files
authored
Merge pull request #62599 from kavon/ban-moveonly-generic-subst
Emit error when move-only type is used as a generic type
2 parents 2f3b51a + d2da71a commit bf2816b

31 files changed

+2489
-1989
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6653,6 +6653,7 @@ ERROR(noimplicitcopy_attr_valid_only_on_local_let_params,
66536653
none, "'@_noImplicitCopy' attribute can only be applied to local lets and params", ())
66546654
ERROR(noimplicitcopy_attr_invalid_in_generic_context,
66556655
none, "'@_noImplicitCopy' attribute cannot be applied to entities in generic contexts", ())
6656+
ERROR(moveonly_generics, none, "move-only type %0 cannot be used with generics yet", (Type))
66566657
ERROR(noimplicitcopy_attr_not_allowed_on_moveonlytype,none,
66576658
"'@_noImplicitCopy' has no effect when applied to a move only type", ())
66586659

@@ -6972,4 +6973,3 @@ NOTE(opt_out_from_missing_reflection_metadata_attr,none,
69726973

69736974
#define UNDEFINE_DIAGNOSTIC_MACROS
69746975
#include "DefineDiagnosticMacros.h"
6975-

include/swift/AST/KnownProtocols.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ PROTOCOL(AsyncIteratorProtocol)
117117

118118
PROTOCOL(FloatingPoint)
119119

120+
PROTOCOL_(Copyable)
121+
120122
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByArrayLiteral, "Array", false)
121123
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByBooleanLiteral, "BooleanLiteralType", true)
122124
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByDictionaryLiteral, "Dictionary", false)

include/swift/AST/Types.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -602,9 +602,8 @@ class alignas(1 << TypeAlignInBits) TypeBase
602602

603603
bool isPlaceholder();
604604

605-
/// Returns true if this is a move only type. Returns false if this is a
606-
/// non-move only type or a move only wrapped type.
607-
bool isPureMoveOnly() const;
605+
/// Returns true if this is a move-only type.
606+
bool isPureMoveOnly();
608607

609608
/// Does the type have outer parenthesis?
610609
bool hasParenSugar() const { return getKind() == TypeKind::Paren; }

include/swift/Sema/CSFix.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,9 @@ enum class FixKind : uint8_t {
422422
/// Allow function type actor mismatch e.g. `@MainActor () -> Void`
423423
/// vs.`@OtherActor () -> Void`
424424
AllowGlobalActorMismatch,
425+
426+
/// Produce an error about a type that must be Copyable
427+
MustBeCopyable,
425428
};
426429

427430
class ConstraintFix {
@@ -2027,6 +2030,27 @@ class NotCompileTimeConst final : public ContextualMismatch {
20272030
}
20282031
};
20292032

2033+
class MustBeCopyable final : public ConstraintFix {
2034+
Type noncopyableTy;
2035+
2036+
MustBeCopyable(ConstraintSystem &cs, Type noncopyableTy, ConstraintLocator *locator);
2037+
2038+
public:
2039+
std::string getName() const override { return "remove move-only from type"; }
2040+
2041+
bool diagnose(const Solution &solution, bool asNote = false) const override;
2042+
2043+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override;
2044+
2045+
static MustBeCopyable *create(ConstraintSystem &cs,
2046+
Type noncopyableTy,
2047+
ConstraintLocator *locator);
2048+
2049+
static bool classof(ConstraintFix const* fix) {
2050+
return fix->getKind() == FixKind::MustBeCopyable;
2051+
}
2052+
};
2053+
20302054
class CollectionElementContextualMismatch final
20312055
: public ContextualMismatch,
20322056
private llvm::TrailingObjects<CollectionElementContextualMismatch,

lib/AST/Module.cpp

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,6 +1206,13 @@ ModuleDecl::lookupExistentialConformance(Type type, ProtocolDecl *protocol) {
12061206
if (!protocol->existentialConformsToSelf())
12071207
return ProtocolConformanceRef::forInvalid();
12081208

1209+
// All existentials are Copyable.
1210+
if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
1211+
return ProtocolConformanceRef(
1212+
ctx.getBuiltinConformance(type, protocol, GenericSignature(), {},
1213+
BuiltinConformanceKind::Synthesized));
1214+
}
1215+
12091216
auto layout = type->getExistentialLayout();
12101217

12111218
// Due to an IRGen limitation, witness tables cannot be passed from an
@@ -1362,8 +1369,10 @@ static ProtocolConformanceRef getBuiltinTupleTypeConformance(
13621369
return ProtocolConformanceRef(specialized);
13631370
}
13641371

1365-
// Tuple type are Sendable when all of their element types are Sendable.
1366-
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) {
1372+
/// For some known protocols (KPs) like Sendable and Copyable, a tuple type
1373+
/// conforms to the protocol KP when all of their element types conform to KP.
1374+
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) ||
1375+
protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
13671376

13681377
// Create the pieces for a generic tuple type (T1, T2, ... TN) and a
13691378
// generic signature <T1, T2, ..., TN>.
@@ -1389,9 +1398,9 @@ static ProtocolConformanceRef getBuiltinTupleTypeConformance(
13891398
BuiltinConformanceKind::Synthesized));
13901399
}
13911400

1392-
// Form a generic conformance of (T1, T2, ..., TN): Sendable with signature
1393-
// <T1, T2, ..., TN> and conditional requirements T1: Sendable,
1394-
// T2: Sendable, ..., TN: Sendable.
1401+
// Form a generic conformance of (T1, T2, ..., TN): KP with signature
1402+
// <T1, T2, ..., TN> and conditional requirements T1: KP,
1403+
// T2: P, ..., TN: KP.
13951404
auto genericTupleType = TupleType::get(genericElements, ctx);
13961405
auto genericSig = GenericSignature::get(
13971406
genericParams, conditionalRequirements);
@@ -1440,24 +1449,33 @@ static bool isSendableFunctionType(const FunctionType *functionType) {
14401449
/// appropriate.
14411450
static ProtocolConformanceRef getBuiltinFunctionTypeConformance(
14421451
Type type, const FunctionType *functionType, ProtocolDecl *protocol) {
1452+
ASTContext &ctx = protocol->getASTContext();
14431453
// @Sendable function types are Sendable.
14441454
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) &&
14451455
isSendableFunctionType(functionType)) {
1446-
ASTContext &ctx = protocol->getASTContext();
14471456
return ProtocolConformanceRef(
14481457
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { },
14491458
BuiltinConformanceKind::Synthesized));
14501459
}
14511460

1461+
// Functions cannot permanently destroy a move-only var/let
1462+
// that they capture, so it's safe to copy functions, like classes.
1463+
if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
1464+
return ProtocolConformanceRef(
1465+
ctx.getBuiltinConformance(type, protocol, GenericSignature(), {},
1466+
BuiltinConformanceKind::Synthesized));
1467+
}
1468+
14521469
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
14531470
}
14541471

14551472
/// Synthesize a builtin metatype type conformance to the given protocol, if
14561473
/// appropriate.
14571474
static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance(
14581475
Type type, const AnyMetatypeType *metatypeType, ProtocolDecl *protocol) {
1459-
// All metatypes are Sendable.
1460-
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) {
1476+
// All metatypes are Sendable and Copyable
1477+
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) ||
1478+
protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
14611479
ASTContext &ctx = protocol->getASTContext();
14621480
return ProtocolConformanceRef(
14631481
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { },
@@ -1471,8 +1489,9 @@ static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance(
14711489
/// appropriate.
14721490
static ProtocolConformanceRef getBuiltinBuiltinTypeConformance(
14731491
Type type, const BuiltinType *builtinType, ProtocolDecl *protocol) {
1474-
// All builtin are Sendable.
1475-
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) {
1492+
// All builtin are Sendable and Copyable
1493+
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) ||
1494+
protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
14761495
ASTContext &ctx = protocol->getASTContext();
14771496
return ProtocolConformanceRef(
14781497
ctx.getBuiltinConformance(type, protocol, GenericSignature(), { },
@@ -1527,6 +1546,10 @@ LookupConformanceInModuleRequest::evaluate(
15271546
// constraint and the superclass conforms to the protocol.
15281547
if (auto archetype = type->getAs<ArchetypeType>()) {
15291548

1549+
// All archetypes conform to Copyable since they represent a generic.
1550+
if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable))
1551+
return ProtocolConformanceRef(protocol);
1552+
15301553
// The generic signature builder drops conformance requirements that are made
15311554
// redundant by a superclass requirement, so check for a concrete
15321555
// conformance first, since an abstract conformance might not be
@@ -1636,6 +1659,15 @@ LookupConformanceInModuleRequest::evaluate(
16361659
} else {
16371660
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
16381661
}
1662+
} else if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
1663+
// Only move-only nominals are not Copyable
1664+
if (nominal->isMoveOnly()) {
1665+
return ProtocolConformanceRef::forInvalid();
1666+
} else {
1667+
// FIXME: this should probably follow the Sendable case in that
1668+
// we should synthesize and append a ProtocolConformance to the `conformances` list.
1669+
return ProtocolConformanceRef(protocol);
1670+
}
16391671
} else {
16401672
// Was unable to infer the missing conformance.
16411673
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);

lib/AST/ProtocolConformance.cpp

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,24 +1224,27 @@ static SmallVector<ProtocolConformance *, 2> findSynthesizedConformances(
12241224
// Try to find specific conformances
12251225
SmallVector<ProtocolConformance *, 2> result;
12261226

1227-
// Sendable may be synthesized for concrete types
1228-
if (!isa<ProtocolDecl>(nominal)) {
1229-
if (auto sendable =
1230-
findSynthesizedConformance(dc, KnownProtocolKind::Sendable)) {
1231-
result.push_back(sendable);
1227+
auto trySynthesize = [&](KnownProtocolKind knownProto) {
1228+
if (auto conformance =
1229+
findSynthesizedConformance(dc, knownProto)) {
1230+
result.push_back(conformance);
12321231
}
1232+
};
1233+
1234+
// Concrete types may synthesize some conformances
1235+
if (!isa<ProtocolDecl>(nominal)) {
1236+
trySynthesize(KnownProtocolKind::Sendable);
1237+
1238+
// FIXME(kavon): make sure this conformance doesn't show up in swiftinterfaces
1239+
// before do this synthesis unconditionally.
1240+
if (dc->getASTContext().LangOpts.hasFeature(Feature::MoveOnly))
1241+
trySynthesize(KnownProtocolKind::Copyable);
12331242
}
12341243

12351244
/// Distributed actors can synthesize Encodable/Decodable, so look for those
12361245
if (nominal->isDistributedActor()) {
1237-
if (auto conformance =
1238-
findSynthesizedConformance(dc, KnownProtocolKind::Encodable)) {
1239-
result.push_back(conformance);
1240-
}
1241-
if (auto conformance =
1242-
findSynthesizedConformance(dc, KnownProtocolKind::Decodable)) {
1243-
result.push_back(conformance);
1244-
}
1246+
trySynthesize(KnownProtocolKind::Encodable);
1247+
trySynthesize(KnownProtocolKind::Decodable);
12451248
}
12461249

12471250
return result;

lib/AST/Type.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,17 @@ bool TypeBase::isAny() {
165165
return constraint->isEqual(getASTContext().TheAnyType);
166166
}
167167

168-
bool TypeBase::isPureMoveOnly() const {
169-
if (auto *nom = getCanonicalType()->getNominalOrBoundGenericNominal())
168+
bool TypeBase::isPureMoveOnly() {
169+
if (auto *nom = getNominalOrBoundGenericNominal())
170170
return nom->isMoveOnly();
171+
172+
// if any components of the tuple are move-only, then the tuple is move-only.
173+
if (auto *tupl = getCanonicalType()->getAs<TupleType>()) {
174+
for (auto eltTy : tupl->getElementTypes())
175+
if (eltTy->isPureMoveOnly())
176+
return true;
177+
}
178+
171179
return false;
172180
}
173181

lib/IRGen/GenMeta.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5885,6 +5885,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
58855885
case KnownProtocolKind::UnsafeSendable:
58865886
case KnownProtocolKind::RangeReplaceableCollection:
58875887
case KnownProtocolKind::GlobalActor:
5888+
case KnownProtocolKind::Copyable:
58885889
return SpecialProtocol::None;
58895890
}
58905891

lib/Sema/CSDiagnostics.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5822,6 +5822,11 @@ bool NotCompileTimeConstFailure::diagnoseAsError() {
58225822
return true;
58235823
}
58245824

5825+
bool NotCopyableFailure::diagnoseAsError() {
5826+
emitDiagnostic(diag::moveonly_generics, noncopyableTy);
5827+
return true;
5828+
}
5829+
58255830
bool CollectionElementContextualFailure::diagnoseAsError() {
58265831
auto anchor = getRawAnchor();
58275832
auto *locator = getLocator();

lib/Sema/CSDiagnostics.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,6 +1823,15 @@ class NotCompileTimeConstFailure final : public FailureDiagnostic {
18231823
bool diagnoseAsError() override;
18241824
};
18251825

1826+
class NotCopyableFailure final : public FailureDiagnostic {
1827+
Type noncopyableTy;
1828+
public:
1829+
NotCopyableFailure(const Solution &solution, Type noncopyableTy, ConstraintLocator *locator)
1830+
: FailureDiagnostic(solution, locator), noncopyableTy(noncopyableTy) {}
1831+
1832+
bool diagnoseAsError() override;
1833+
};
1834+
18261835
/// Diagnose a contextual mismatch between expected collection element type
18271836
/// and the one provided (e.g. source of the assignment or argument to a call)
18281837
/// e.g.:

lib/Sema/CSFix.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,40 @@ bool NotCompileTimeConst::diagnose(const Solution &solution, bool asNote) const
13321332
return failure.diagnose(asNote);
13331333
}
13341334

1335+
MustBeCopyable::MustBeCopyable(ConstraintSystem &cs, Type noncopyableTy, ConstraintLocator *locator)
1336+
: ConstraintFix(cs, FixKind::MustBeCopyable, locator, FixBehavior::Error),
1337+
noncopyableTy(noncopyableTy) {}
1338+
1339+
bool MustBeCopyable::diagnose(const Solution &solution, bool asNote) const {
1340+
NotCopyableFailure failure(solution, noncopyableTy, getLocator());
1341+
return failure.diagnose(asNote);
1342+
}
1343+
1344+
MustBeCopyable* MustBeCopyable::create(ConstraintSystem &cs,
1345+
Type noncopyableTy,
1346+
ConstraintLocator *locator) {
1347+
return new (cs.getAllocator()) MustBeCopyable(cs, noncopyableTy, locator);
1348+
}
1349+
1350+
bool MustBeCopyable::diagnoseForAmbiguity(CommonFixesArray commonFixes) const {
1351+
// Only diagnose if all solutions agreed on the same errant non-copyable type.
1352+
Type firstNonCopyable;
1353+
for (const auto &solutionAndFix : commonFixes) {
1354+
const auto *solution = solutionAndFix.first;
1355+
const auto *fix = solutionAndFix.second->getAs<MustBeCopyable>();
1356+
1357+
auto otherNonCopyable = solution->simplifyType(fix->noncopyableTy);
1358+
if (!firstNonCopyable)
1359+
firstNonCopyable = otherNonCopyable;
1360+
1361+
if (firstNonCopyable->getCanonicalType() != otherNonCopyable->getCanonicalType()) {
1362+
return false; // fixes differed, so decline to emit a tailored diagnostic.
1363+
}
1364+
}
1365+
1366+
return diagnose(*commonFixes.front().first);
1367+
}
1368+
13351369
bool CollectionElementContextualMismatch::diagnose(const Solution &solution,
13361370
bool asNote) const {
13371371
CollectionElementContextualFailure failure(

lib/Sema/CSSimplify.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3823,6 +3823,18 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2,
38233823
return getTypeMatchAmbiguous();
38243824
}
38253825

3826+
// move-only types cannot match with any existential types.
3827+
if (type1->isPureMoveOnly()) {
3828+
// tailor error message
3829+
if (shouldAttemptFixes()) {
3830+
auto *fix = MustBeCopyable::create(*this, type1,
3831+
getConstraintLocator(locator));
3832+
if (!recordFix(fix))
3833+
return getTypeMatchSuccess();
3834+
}
3835+
return getTypeMatchFailure(locator);
3836+
}
3837+
38263838
// FIXME: Feels like a hack.
38273839
if (type1->is<InOutType>())
38283840
return getTypeMatchFailure(locator);
@@ -8129,6 +8141,14 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
81298141
return SolutionKind::Solved;
81308142
}
81318143
}
8144+
8145+
// If this is a failure to conform to Copyable, tailor the error message.
8146+
if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
8147+
auto *fix =
8148+
MustBeCopyable::create(*this, type, getConstraintLocator(locator));
8149+
if (!recordFix(fix))
8150+
return SolutionKind::Solved;
8151+
}
81328152
}
81338153

81348154
// There's nothing more we can do; fail.
@@ -11104,6 +11124,12 @@ ConstraintSystem::simplifyBridgingConstraint(Type type1,
1110411124
if (type1->isTypeVariableOrMember() || type2->isTypeVariableOrMember())
1110511125
return formUnsolved();
1110611126

11127+
// Move-only types can't be involved in a bridging conversion since a bridged
11128+
// type assumes the ability to copy.
11129+
if (type1->isPureMoveOnly()) {
11130+
return SolutionKind::Error;
11131+
}
11132+
1110711133
Type unwrappedFromType;
1110811134
unsigned numFromOptionals;
1110911135
std::tie(unwrappedFromType, numFromOptionals) = unwrapType(type1);
@@ -13943,6 +13969,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1394313969
case FixKind::IgnoreKeyPathContextualMismatch:
1394413970
case FixKind::NotCompileTimeConst:
1394513971
case FixKind::RenameConflictingPatternVariables:
13972+
case FixKind::MustBeCopyable:
1394613973
case FixKind::MacroMissingPound:
1394713974
case FixKind::AllowGlobalActorMismatch: {
1394813975
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;

0 commit comments

Comments
 (0)