Skip to content

Commit 68ae729

Browse files
committed
_Copyable as a Requirement Against the Machine
An initial implementation of a rework in how we prevent noncopyable types from being substituted in places they are not permitted. Instead of generating a constraint for every generic parameter in the solver, we produce real Copyable conformance requirements. This is much better for our longer-term goal of supporting `~Copyable` in more places.
1 parent 97668d5 commit 68ae729

File tree

8 files changed

+382
-7
lines changed

8 files changed

+382
-7
lines changed

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@ EXPERIMENTAL_FEATURE(RawLayout, true)
235235
/// Enables the "embedded" swift mode (no runtime).
236236
EXPERIMENTAL_FEATURE(Embedded, true)
237237

238+
/// Enables noncopyable generics
239+
EXPERIMENTAL_FEATURE(NoncopyableGenerics, false)
240+
238241
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
239242
#undef EXPERIMENTAL_FEATURE
240243
#undef UPCOMING_FEATURE

lib/AST/ASTPrinter.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
//===--- ASTPrinter.cpp - Swift Language AST Printer ----------------------===//
21
//
32
// This source file is part of the Swift.org open source project
43
//
@@ -3385,6 +3384,12 @@ static bool usesFeatureMoveOnlyPartialConsumption(Decl *decl) {
33853384
return false;
33863385
}
33873386

3387+
static bool usesFeatureNoncopyableGenerics(Decl *decl) {
3388+
// FIXME: need to look for suppressed entries on generic parameters or
3389+
// inheritance clauses!
3390+
return false;
3391+
}
3392+
33883393
static bool usesFeatureOneWayClosureParameters(Decl *decl) {
33893394
return false;
33903395
}

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,21 @@ void swift::rewriting::realizeInheritedRequirements(
756756

757757
realizeTypeRequirement(dc, type, inheritedType, loc, result, errors);
758758
}
759+
760+
auto &ctx = dc->getASTContext();
761+
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
762+
// All associated types or generic type params are Copyable by default.
763+
// TODO: if we saw ~Copyable in the inherited types list earlier, skip this!
764+
765+
if (auto *copyable = ctx.getProtocol(KnownProtocolKind::Copyable)) {
766+
SourceLoc loc = decl->getLoc();
767+
Type copyableType = copyable->getDeclaredInterfaceType();
768+
769+
// Add Copyable as an "inferred" requirement.
770+
Requirement req(RequirementKind::Conformance, type, copyableType);
771+
result.push_back({req, loc, /*wasInferred=*/true});
772+
}
773+
}
759774
}
760775

761776
/// StructuralRequirementsRequest realizes all the user-written requirements

lib/Sema/CSDiagnostics.cpp

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,20 @@ bool MissingConformanceFailure::diagnoseAsError() {
616616
if (diagnoseAsAmbiguousOperatorRef())
617617
return true;
618618

619+
// Use tailored diagnostics for failure to conform to Copyable.
620+
if (auto asProtoType = protocolType->getAs<ProtocolType>()) {
621+
if (auto *protoDecl = asProtoType->getDecl()) {
622+
if (protoDecl->isSpecificProtocol(KnownProtocolKind::Copyable)) {
623+
NotCopyableFailure failure(getSolution(),
624+
nonConformingType,
625+
NoncopyableMatchFailure::forCopyableConstraint(),
626+
getLocator());
627+
if (failure.diagnoseAsError())
628+
return true;
629+
}
630+
}
631+
}
632+
619633
if (nonConformingType->isObjCExistentialType()) {
620634
emitDiagnostic(diag::protocol_does_not_conform_static, nonConformingType,
621635
protocolType);
@@ -6167,6 +6181,7 @@ bool NotCopyableFailure::diagnoseAsError() {
61676181

61686182
case NoncopyableMatchFailure::CopyableConstraint: {
61696183
auto *loc = getLocator();
6184+
auto path = loc->getPath();
61706185

61716186
if (loc->isLastElement<LocatorPathElt::AnyTupleElement>()) {
61726187
assert(!noncopyableTy->is<TupleType>() && "will use poor wording");
@@ -6180,9 +6195,11 @@ bool NotCopyableFailure::diagnoseAsError() {
61806195
return true;
61816196
}
61826197

6183-
// a bit paranoid of nulls and such...
6184-
if (auto *genericParam = loc->getGenericParameter()) {
6185-
if (auto *paramDecl = genericParam->getDecl()) {
6198+
auto diagnoseGenericTypeParamType = [&](GenericTypeParamType *typeParam) {
6199+
if (!typeParam)
6200+
return false;
6201+
6202+
if (auto *paramDecl = typeParam->getDecl()) {
61866203
if (auto *owningDecl =
61876204
dyn_cast_or_null<ValueDecl>(paramDecl->getDeclContext()->getAsDecl())) {
61886205

@@ -6191,27 +6208,45 @@ bool NotCopyableFailure::diagnoseAsError() {
61916208
emitDiagnostic(diag::noncopyable_generics_generic_param_metatype,
61926209
noncopyableTy->getMetatypeInstanceType(),
61936210
paramDecl->getDescriptiveKind(),
6194-
genericParam,
6211+
typeParam,
61956212
owningDecl->getName(),
61966213
noncopyableTy);
61976214
else
61986215
emitDiagnostic(diag::noncopyable_generics_generic_param,
61996216
noncopyableTy,
62006217
paramDecl->getDescriptiveKind(),
6201-
genericParam,
6218+
typeParam,
62026219
owningDecl->getName());
62036220

62046221
// If we have a location for the parameter, point it out in a note.
62056222
if (auto loc = paramDecl->getNameLoc()) {
62066223
emitDiagnosticAt(loc,
62076224
diag::noncopyable_generics_implicit_copyable,
62086225
paramDecl->getDescriptiveKind(),
6209-
genericParam);
6226+
typeParam);
62106227
}
62116228

62126229
return true;
62136230
}
62146231
}
6232+
return false;
6233+
};
6234+
6235+
// NOTE: a non-requirement constraint locator might now be impossible after
6236+
// having made Copyable a Requirement in Feature::NoncopyableGenerics
6237+
if (diagnoseGenericTypeParamType(loc->getGenericParameter()))
6238+
return true;
6239+
6240+
if (auto tpr =
6241+
loc->getLastElementAs<LocatorPathElt::TypeParameterRequirement>()) {
6242+
auto signature = path[path.size() - 2]
6243+
.castTo<LocatorPathElt::OpenedGeneric>()
6244+
.getSignature();
6245+
auto requirement = signature.getRequirements()[tpr->getIndex()];
6246+
auto subject = requirement.getFirstType();
6247+
6248+
if (diagnoseGenericTypeParamType(subject->getAs<GenericTypeParamType>()))
6249+
return true;
62156250
}
62166251
break;
62176252
}

lib/Sema/ConstraintSystem.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,6 +1928,10 @@ TypeVariableType *ConstraintSystem::openGenericParameter(
19281928
assert(result.second);
19291929
(void)result;
19301930

1931+
if (Context.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
1932+
return typeVar;
1933+
}
1934+
19311935
// Add a constraint that generic parameters conform to Copyable.
19321936
// This lookup only can fail if the stdlib (i.e. the Swift module) has not
19331937
// been loaded because you've passed `-parse-stdlib` and are not building the

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1811,12 +1811,23 @@ static void diagnoseWrittenPlaceholderTypes(ASTContext &Ctx,
18111811
static void checkProtocolRefinementRequirements(ProtocolDecl *proto) {
18121812
auto requiredProtos = proto->getGenericSignature()->getRequiredProtocols(
18131813
proto->getSelfInterfaceType());
1814+
const bool enabledNoncopyableGenerics =
1815+
proto->getASTContext().LangOpts.hasFeature(Feature::NoncopyableGenerics);
18141816

18151817
for (auto *otherProto : requiredProtos) {
18161818
// Every protocol 'P' has an implied requirement 'Self : P'; skip it.
18171819
if (otherProto == proto)
18181820
continue;
18191821

1822+
if (enabledNoncopyableGenerics) {
1823+
// For any protocol 'P', there is an implied requirement 'Self: Copyable',
1824+
// unless it was suppressed via `Self: ~Copyable`; so skip if present.
1825+
if (otherProto->isSpecificProtocol(KnownProtocolKind::Copyable))
1826+
continue;
1827+
1828+
// TODO: report that something implied Copyable despite writing ~Copyable
1829+
}
1830+
18201831
// GenericSignature::getRequiredProtocols() canonicalizes the protocol
18211832
// list by dropping protocols that are inherited by other protocols in
18221833
// the list. Any protocols that remain in the list other than 'proto'

lib/Sema/TypeCheckType.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,10 @@ static bool didDiagnoseMoveOnlyGenericArgs(ASTContext &ctx,
10151015
SourceLoc loc,
10161016
Type unboundTy,
10171017
ArrayRef<Type> genericArgs) {
1018+
1019+
if (ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics))
1020+
return false;
1021+
10181022
bool didEmitDiag = false;
10191023
for (auto t: genericArgs) {
10201024
if (!t->isPureMoveOnly())
@@ -2294,6 +2298,9 @@ bool TypeResolver::diagnoseInvalidPlaceHolder(OpaqueReturnTypeRepr *repr) {
22942298
bool TypeResolver::diagnoseMoveOnlyGeneric(TypeRepr *repr,
22952299
Type unboundTy,
22962300
Type genericArgTy) {
2301+
if (getASTContext().LangOpts.hasFeature(Feature::NoncopyableGenerics))
2302+
return false;
2303+
22972304
if (genericArgTy->isPureMoveOnly()) {
22982305
if (unboundTy) {
22992306
diagnoseInvalid(repr, repr->getLoc(), diag::noncopyable_generics_specific,

0 commit comments

Comments
 (0)