Skip to content

Commit f76360c

Browse files
committed
[Sema] "Noncopyable" means no Copyable conformance
1 parent 71a5935 commit f76360c

23 files changed

+532
-184
lines changed

include/swift/AST/Decl.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -3110,13 +3110,13 @@ class TypeDecl : public ValueDecl {
31103110
ArrayRef<InheritedEntry> Inherited;
31113111

31123112
struct {
3113-
/// Whether the "isNoncopyable" bit has been computed yet.
3114-
unsigned isNoncopyableComputed : 1;
3113+
/// Whether the "hasNoncopyableAnnotation" bit has been computed yet.
3114+
unsigned isNoncopyableAnnotationComputed : 1;
31153115

3116-
/// Whether this declaration supports copying.
3117-
unsigned isNoncopyable : 1;
3116+
/// Whether this declaration had a noncopyable inverse written somewhere.
3117+
unsigned hasNoncopyableAnnotation : 1;
31183118
} LazySemanticInfo = { };
3119-
friend class IsNoncopyableRequest;
3119+
friend class HasNoncopyableAnnotationRequest;
31203120

31213121
protected:
31223122
TypeDecl(DeclKind K, llvm::PointerUnion<DeclContext *, ASTContext *> context,

include/swift/AST/DiagnosticsSema.def

+19
Original file line numberDiff line numberDiff line change
@@ -7452,6 +7452,25 @@ ERROR(accessor_macro_not_single_var, none,
74527452
ERROR(noncopyable_class, none,
74537453
"classes cannot be noncopyable",
74547454
())
7455+
ERROR(noncopyable_type_member_in_copyable,none,
7456+
"%select{stored property %2|associated value %2}1 of "
7457+
"'Copyable'-conforming %kind3 has noncopyable type %0",
7458+
(Type, bool, DeclName, const ValueDecl *))
7459+
NOTE(add_inverse_for_containment,none,
7460+
"consider removing implicit '%1' conformance from %kind0",
7461+
(const ValueDecl *, StringRef))
7462+
NOTE(remove_inverse_on_generic_parameter_for_conformance,none,
7463+
"consider removing '~%1' from generic parameter %0 so it conforms to the '%1' protocol",
7464+
(Type, StringRef))
7465+
NOTE(remove_inverse_on_nominal_for_conformance,none,
7466+
"consider removing '~%1' from %kind0 so it conforms to the '%1' protocol",
7467+
(const ValueDecl *, StringRef))
7468+
NOTE(add_explicit_protocol_for_conformance,none,
7469+
"consider making %kind0 explicitly conform to the '%1' protocol",
7470+
(const ValueDecl *, StringRef))
7471+
7472+
// -- older ones below --
7473+
74557474
ERROR(noncopyable_within_copyable, none,
74567475
"%kind0 cannot contain a noncopyable type without also being noncopyable",
74577476
(const ValueDecl *))

include/swift/AST/TypeCheckRequests.h

+62-3
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,10 @@ class IsFinalRequest :
407407
void cacheResult(bool value) const;
408408
};
409409

410-
/// Determine whether the given declaration is noncopyable
411-
class IsNoncopyableRequest
412-
: public SimpleRequest<IsNoncopyableRequest, bool(TypeDecl *),
410+
/// Determine whether the given declaration has any markings that would cause it
411+
/// to not have an implicit, unconditional conformance to Copyable.
412+
class HasNoncopyableAnnotationRequest
413+
: public SimpleRequest<HasNoncopyableAnnotationRequest, bool(TypeDecl *),
413414
RequestFlags::SeparatelyCached> {
414415
public:
415416
using SimpleRequest::SimpleRequest;
@@ -427,6 +428,24 @@ class IsNoncopyableRequest
427428
void cacheResult(bool value) const;
428429
};
429430

431+
/// Determine whether the given type is noncopyable. Assumes type parameters
432+
/// have become archetypes.
433+
class IsNoncopyableRequest
434+
: public SimpleRequest<IsNoncopyableRequest, bool(CanType),
435+
RequestFlags::Cached> {
436+
public:
437+
using SimpleRequest::SimpleRequest;
438+
439+
private:
440+
friend SimpleRequest;
441+
442+
// Evaluation.
443+
bool evaluate(Evaluator &evaluator, CanType type) const;
444+
445+
public:
446+
// Caching.
447+
bool isCached() const { return true; }
448+
};
430449

431450
/// Determine whether the given declaration is escapable.
432451
class IsEscapableRequest
@@ -448,6 +467,46 @@ class IsEscapableRequest
448467
void cacheResult(bool value) const;
449468
};
450469

470+
471+
/// Determine whether the given declaration is escapable.
472+
class IsEscapableRequest
473+
: public SimpleRequest<IsEscapableRequest, bool(ValueDecl *),
474+
RequestFlags::SeparatelyCached> {
475+
public:
476+
using SimpleRequest::SimpleRequest;
477+
478+
private:
479+
friend SimpleRequest;
480+
481+
// Evaluation.
482+
bool evaluate(Evaluator &evaluator, ValueDecl *decl) const;
483+
484+
public:
485+
// Separate caching.
486+
bool isCached() const { return true; }
487+
llvm::Optional<bool> getCachedResult() const;
488+
void cacheResult(bool value) const;
489+
};
490+
491+
/// Determine whether the given type is noncopyable. Assumes type parameters
492+
/// have become archetypes.
493+
class IsNoncopyableRequest
494+
: public SimpleRequest<IsNoncopyableRequest, bool(CanType),
495+
RequestFlags::Cached> {
496+
public:
497+
using SimpleRequest::SimpleRequest;
498+
499+
private:
500+
friend SimpleRequest;
501+
502+
// Evaluation.
503+
bool evaluate(Evaluator &evaluator, CanType type) const;
504+
505+
public:
506+
// Caching.
507+
bool isCached() const { return true; }
508+
};
509+
451510
/// Determine whether the given declaration is 'dynamic''.
452511
class IsDynamicRequest :
453512
public SimpleRequest<IsDynamicRequest,

include/swift/AST/TypeCheckerTypeIDZone.def

+4-1
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,11 @@ SWIFT_REQUEST(TypeChecker, IsDynamicRequest, bool(ValueDecl *),
210210
SeparatelyCached, NoLocationInfo)
211211
SWIFT_REQUEST(TypeChecker, IsFinalRequest, bool(ValueDecl *), SeparatelyCached,
212212
NoLocationInfo)
213-
SWIFT_REQUEST(TypeChecker, IsNoncopyableRequest, bool(TypeDecl *), SeparatelyCached,
213+
SWIFT_REQUEST(TypeChecker, HasNoncopyableAnnotationRequest, bool(TypeDecl *), SeparatelyCached,
214214
NoLocationInfo)
215+
SWIFT_REQUEST(TypeChecker, IsNoncopyableRequest,
216+
bool (CanType),
217+
Cached, NoLocationInfo)
215218
SWIFT_REQUEST(TypeChecker, IsEscapableRequest, bool(ValueDecl *),
216219
SeparatelyCached, NoLocationInfo)
217220
SWIFT_REQUEST(TypeChecker, IsGetterMutatingRequest, bool(AbstractStorageDecl *),

include/swift/AST/Types.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ class ProtocolConformance;
100100
enum PointerTypeKind : unsigned;
101101
struct ValueOwnershipKind;
102102
class ErrorExpr;
103+
enum class KnownProtocolKind : uint8_t;
103104

104105
typedef CanTypeWrapper<SILFunctionType> CanSILFunctionType;
105106

@@ -636,9 +637,12 @@ class alignas(1 << TypeAlignInBits) TypeBase
636637

637638
bool isPlaceholder();
638639

639-
/// Returns true if this is a noncopyable type.
640+
/// DEPRECIATED: Returns true if this is a noncopyable type.
640641
bool isNoncopyable();
641642

643+
/// Returns true if this type lacks conformance to Copyable in the context.
644+
bool isNoncopyable(const DeclContext *dc);
645+
642646
/// Does the type have outer parenthesis?
643647
bool hasParenSugar() const { return getKind() == TypeKind::Paren; }
644648

lib/AST/ConformanceLookupTable.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,7 @@ bool ConformanceLookupTable::resolveConformances(ProtocolDecl *protocol) {
810810
return anySuperseded;
811811
}
812812

813-
DeclContext *ConformanceLookupTable::getConformingContext(
813+
DeclContext *ConformanceLookupTable:: getConformingContext(
814814
NominalTypeDecl *nominal,
815815
ConformanceEntry *entry) {
816816
ProtocolDecl *protocol = entry->getProtocol();
@@ -848,7 +848,8 @@ DeclContext *ConformanceLookupTable::getConformingContext(
848848
const auto &superclassConformances =
849849
superclassDecl->ConformanceTable->Conformances[protocol];
850850
if (superclassConformances.empty()) {
851-
assert(protocol->isSpecificProtocol(KnownProtocolKind::Sendable));
851+
assert(protocol->isSpecificProtocol(KnownProtocolKind::Sendable) ||
852+
protocol->isSpecificProtocol(KnownProtocolKind::Copyable));
852853

853854
// Go dig for a superclass that does conform to Sendable.
854855
// FIXME: This is a hack because the inherited conformances aren't

lib/AST/Decl.cpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -4836,11 +4836,10 @@ GenericParameterReferenceInfo ValueDecl::findExistentialSelfReferences(
48364836
}
48374837

48384838
bool TypeDecl::isNoncopyable() const {
4839-
assert(!getASTContext().LangOpts.hasFeature(Feature::NoncopyableGenerics)
4840-
&& "TypeDecl::isNoncopyable() is not compatible with NoncopyableGenerics");
4841-
4839+
// NOTE: must answer true iff it is unconditionally noncopyable.
48424840
return evaluateOrDefault(getASTContext().evaluator,
4843-
IsNoncopyableRequest{const_cast<TypeDecl *>(this)},
4841+
HasNoncopyableAnnotationRequest{
4842+
const_cast<TypeDecl *>(this)},
48444843
true); // default to true for safety
48454844
}
48464845

lib/AST/Module.cpp

+29-11
Original file line numberDiff line numberDiff line change
@@ -1795,16 +1795,21 @@ static ProtocolConformanceRef getBuiltinMetaTypeTypeConformance(
17951795
Type type, const AnyMetatypeType *metatypeType, ProtocolDecl *protocol) {
17961796
ASTContext &ctx = protocol->getASTContext();
17971797

1798-
// Only metatypes of Copyable types are Copyable.
1799-
if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable) &&
1800-
!metatypeType->getInstanceType()->isNoncopyable()) {
1801-
return ProtocolConformanceRef(
1802-
ctx.getBuiltinConformance(type, protocol,
1803-
BuiltinConformanceKind::Synthesized));
1798+
if (!ctx.LangOpts.hasFeature(swift::Feature::NoncopyableGenerics) &&
1799+
protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
1800+
// Only metatypes of Copyable types are Copyable.
1801+
if (metatypeType->getInstanceType()->isNoncopyable()) {
1802+
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
1803+
} else {
1804+
return ProtocolConformanceRef(
1805+
ctx.getBuiltinConformance(type, protocol,
1806+
BuiltinConformanceKind::Synthesized));
1807+
}
18041808
}
18051809

1806-
// All metatypes are Sendable
1807-
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable)) {
1810+
// All metatypes are Sendable and Copyable
1811+
if (protocol->isSpecificProtocol(KnownProtocolKind::Sendable) ||
1812+
protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
18081813
return ProtocolConformanceRef(
18091814
ctx.getBuiltinConformance(type, protocol,
18101815
BuiltinConformanceKind::Synthesized));
@@ -1876,9 +1881,10 @@ LookupConformanceInModuleRequest::evaluate(
18761881
// constraint and the superclass conforms to the protocol.
18771882
if (auto archetype = type->getAs<ArchetypeType>()) {
18781883

1879-
// All archetypes conform to Copyable since they represent a generic.
1880-
if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable))
1881-
return ProtocolConformanceRef(protocol);
1884+
// Without noncopyable generics, all archetypes are Copyable
1885+
if (!ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics))
1886+
if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable))
1887+
return ProtocolConformanceRef(protocol);
18821888

18831889
// The generic signature builder drops conformance requirements that are made
18841890
// redundant by a superclass requirement, so check for a concrete
@@ -2002,6 +2008,18 @@ LookupConformanceInModuleRequest::evaluate(
20022008
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
20032009
}
20042010
} else if (protocol->isSpecificProtocol(KnownProtocolKind::Copyable)) {
2011+
2012+
if (!ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
2013+
// Return an abstract conformance to maintain legacy compatability.
2014+
// We only need to do this until we are properly dealing with or
2015+
// omitting Copyable conformances in modules/interfaces.
2016+
2017+
if (nominal->isNoncopyable())
2018+
return ProtocolConformanceRef::forMissingOrInvalid(type, protocol);
2019+
else
2020+
return ProtocolConformanceRef(protocol);
2021+
}
2022+
20052023
// Try to infer Copyable conformance.
20062024
ImplicitKnownProtocolConformanceRequest
20072025
cvRequest{nominal, KnownProtocolKind::Copyable};

lib/AST/RequirementMachine/RequirementLowering.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,7 @@ void swift::rewriting::realizeInheritedRequirements(
784784
// associated type or generic type param.
785785
for (auto kp : defaults) {
786786
ProtocolDecl *decl = ctx.getProtocol(kp);
787-
assert(decl && "couldn't load protocol??");
787+
if (!decl) llvm_unreachable("missing known protocol!");
788788

789789
SourceLoc loc = decl->getLoc();
790790
Type protocolType = decl->getDeclaredInterfaceType();

lib/AST/Type.cpp

+59-11
Original file line numberDiff line numberDiff line change
@@ -157,25 +157,73 @@ bool TypeBase::isMarkerExistential() {
157157
return true;
158158
}
159159

160-
bool TypeBase::isNoncopyable() {
161-
assert(!getASTContext().LangOpts.hasFeature(Feature::NoncopyableGenerics)
162-
&& "TypeBase::isNoncopyable() is not compatible with NoncopyableGenerics");
163-
164-
if (auto *nom = getAnyNominal())
165-
return nom->isNoncopyable();
160+
/// Returns true if this type is _always_ Copyable using the legacy check
161+
/// that does not rely on conformances.
162+
static bool alwaysNoncopyable(Type ty) {
163+
if (auto *nominal = ty->getNominalOrBoundGenericNominal())
164+
return nominal->isNoncopyable();
166165

167-
if (auto *expansion = getAs<PackExpansionType>()) {
168-
return expansion->getPatternType()->isNoncopyable();
166+
if (auto *expansion = ty->getAs<PackExpansionType>()) {
167+
return alwaysNoncopyable(expansion->getPatternType());
169168
}
170169

171170
// if any components of the tuple are move-only, then the tuple is move-only.
172-
if (auto *tupl = getCanonicalType()->getAs<TupleType>()) {
171+
if (auto *tupl = ty->getCanonicalType()->getAs<TupleType>()) {
173172
for (auto eltTy : tupl->getElementTypes())
174-
if (eltTy->isNoncopyable())
173+
if (alwaysNoncopyable(eltTy))
175174
return true;
176175
}
177176

178-
return false;
177+
return false; // otherwise, the conservative assumption is it's copyable.
178+
}
179+
180+
/// \returns true iff this type lacks conformance to Copyable, using the given
181+
/// context to substitute unbound types.
182+
bool TypeBase::isNoncopyable(const DeclContext *dc) {
183+
assert(dc);
184+
185+
/** FIXME: seems this is busted :(
186+
// Fast-path for type parameters; ask the generic signature directly and
187+
// cache that answer.
188+
if (isTypeParameter()) {
189+
auto canType = getCanonicalType();
190+
auto &ctx = canType->getASTContext();
191+
IsNoncopyableRequest req {canType};
192+
193+
if (ctx.evaluator.hasCachedResult(req))
194+
return evaluateOrDefault(ctx.evaluator, req, true);
195+
196+
auto *copyableProto = ctx.getProtocol(KnownProtocolKind::Copyable);
197+
if (!copyableProto)
198+
llvm_unreachable("missing Copyable protocol!");
199+
200+
auto sig = dc->getGenericSignatureOfContext();
201+
bool isNoncopyable = !sig->requiresProtocol(canType, copyableProto);
202+
ctx.evaluator.cacheOutput(req, isNoncopyable == true);
203+
204+
return isNoncopyable;
205+
}
206+
*/
207+
208+
if (!hasArchetype() || hasOpenedExistential())
209+
// TODO: the need for the following test is suspicious.
210+
if (dc->getGenericEnvironmentOfContext() || !hasTypeParameter())
211+
return dc->mapTypeIntoContext(this)->isNoncopyable();
212+
213+
return isNoncopyable();
214+
}
215+
216+
/// \returns true iff this type lacks conformance to Copyable.
217+
bool TypeBase::isNoncopyable() {
218+
auto canType = getCanonicalType();
219+
auto &ctx = canType->getASTContext();
220+
221+
// for legacy-mode queries that are not dependent on conformances to Copyable
222+
if (!ctx.LangOpts.hasFeature(Feature::NoncopyableGenerics))
223+
return alwaysNoncopyable(canType);
224+
225+
IsNoncopyableRequest request{canType};
226+
return evaluateOrDefault(ctx.evaluator, request, /*default=*/true);
179227
}
180228

181229
bool TypeBase::isPlaceholder() {

lib/AST/TypeCheckRequests.cpp

+7-7
Original file line numberDiff line numberDiff line change
@@ -314,21 +314,21 @@ void IsFinalRequest::cacheResult(bool value) const {
314314
}
315315

316316
//----------------------------------------------------------------------------//
317-
// isNoncopyable computation.
317+
// hasNoncopyableAnnotation computation.
318318
//----------------------------------------------------------------------------//
319319

320-
llvm::Optional<bool> IsNoncopyableRequest::getCachedResult() const {
320+
llvm::Optional<bool> HasNoncopyableAnnotationRequest::getCachedResult() const {
321321
auto decl = std::get<0>(getStorage());
322-
if (decl->LazySemanticInfo.isNoncopyableComputed)
323-
return decl->LazySemanticInfo.isNoncopyable;
322+
if (decl->LazySemanticInfo.isNoncopyableAnnotationComputed)
323+
return decl->LazySemanticInfo.hasNoncopyableAnnotation;
324324

325325
return llvm::None;
326326
}
327327

328-
void IsNoncopyableRequest::cacheResult(bool value) const {
328+
void HasNoncopyableAnnotationRequest::cacheResult(bool value) const {
329329
auto decl = std::get<0>(getStorage());
330-
decl->LazySemanticInfo.isNoncopyableComputed = true;
331-
decl->LazySemanticInfo.isNoncopyable = value;
330+
decl->LazySemanticInfo.isNoncopyableAnnotationComputed = true;
331+
decl->LazySemanticInfo.hasNoncopyableAnnotation = value;
332332

333333
if (!decl->getASTContext().LangOpts.hasFeature(Feature::NoncopyableGenerics)) {
334334
// Add an attribute for printing

0 commit comments

Comments
 (0)