Skip to content

Commit 3908c81

Browse files
committed
NCGenerics: sometimes synth. Copyable/Escapable
When the Swift module is not available, we'll synthesize the Copyable/Escapable decls into the Builtin module. In the future, it might be nice to just do this always, and define typealiases for those types in the stdlib to refer to the ones in the builtin module.
1 parent 7612682 commit 3908c81

File tree

11 files changed

+145
-44
lines changed

11 files changed

+145
-44
lines changed

include/swift/AST/ASTContext.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,13 @@ class ASTContext final {
792792

793793
/// Retrieve a specific, known protocol.
794794
ProtocolDecl *getProtocol(KnownProtocolKind kind) const;
795+
796+
/// Synthesizes the given invertible protocol decl into the stdlib (preferred)
797+
/// or, if the stdlib is not available, the Builtin module.
798+
///
799+
/// Does *not* perform any name lookup to check whether, the module already
800+
/// contains a decl with the same name, only does synthesis.
801+
ProtocolDecl *synthesizeInvertibleProtocolDecl(InvertibleProtocolKind ip) const;
795802

796803
/// Determine whether the given nominal type is one of the standard
797804
/// library or Cocoa framework types that is known to be bridged by another

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ IDENTIFIER(enqueue)
9090
IDENTIFIER(erasing)
9191
IDENTIFIER(error)
9292
IDENTIFIER(errorDomain)
93+
IDENTIFIER(Escapable)
9394
IDENTIFIER(Failure)
9495
IDENTIFIER(first)
9596
IDENTIFIER(forKeyedSubscript)

lib/AST/ASTContext.cpp

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,8 @@ struct ASTContext::Implementation {
630630

631631
/// The declared interface type of Builtin.TheTupleType.
632632
BuiltinTupleType *TheTupleType = nullptr;
633+
634+
std::array<ProtocolDecl *, NumInvertibleProtocols> InvertibleProtocolDecls = {};
633635
};
634636

635637
ASTContext::Implementation::Implementation()
@@ -1189,6 +1191,47 @@ Type ASTContext::get##NAME##Type() const { \
11891191

11901192
#include "swift/AST/KnownSDKTypes.def"
11911193

1194+
ProtocolDecl *
1195+
ASTContext::synthesizeInvertibleProtocolDecl(InvertibleProtocolKind ip) const {
1196+
const uint8_t index = (uint8_t)ip;
1197+
if (auto *proto = getImpl().InvertibleProtocolDecls[index])
1198+
return proto;
1199+
1200+
ModuleDecl *stdlib = getStdlibModule();
1201+
FileUnit *file = nullptr;
1202+
if (stdlib) {
1203+
file = &stdlib->getFiles()[0]->getOrCreateSynthesizedFile();
1204+
} else {
1205+
file = &TheBuiltinModule->getMainFile(FileUnitKind::Builtin);
1206+
}
1207+
1208+
// No need to form an inheritance clause; invertible protocols do not
1209+
// implicitly inherit from other invertible protocols.
1210+
auto identifier = getIdentifier(getProtocolName(getKnownProtocolKind(ip)));
1211+
ProtocolDecl *protocol = new (*this) ProtocolDecl(file,
1212+
SourceLoc(), SourceLoc(),
1213+
identifier,
1214+
/*primaryAssocTypes=*/{},
1215+
/*inherited=*/{},
1216+
/*whereClause=*/nullptr);
1217+
protocol->setImplicit(true);
1218+
1219+
// @_marker
1220+
protocol->getAttrs().add(new (*this) MarkerAttr(/*implicit=*/true));
1221+
1222+
// public
1223+
protocol->setAccess(AccessLevel::Public);
1224+
1225+
// Hack to get name lookup to work after synthesizing it into the stdlib.
1226+
if (stdlib) {
1227+
cast<SynthesizedFileUnit>(file)->addTopLevelDecl(protocol);
1228+
stdlib->clearLookupCache();
1229+
}
1230+
1231+
getImpl().InvertibleProtocolDecls[index] = protocol;
1232+
return protocol;
1233+
}
1234+
11921235
ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
11931236
// Check whether we've already looked for and cached this protocol.
11941237
unsigned index = (unsigned)kind;
@@ -1200,6 +1243,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
12001243
SmallVector<ValueDecl *, 1> results;
12011244

12021245
const ModuleDecl *M;
1246+
NLKind NameLookupKind = NLKind::UnqualifiedLookup;
12031247
switch (kind) {
12041248
case KnownProtocolKind::BridgedNSError:
12051249
case KnownProtocolKind::BridgedStoredNSError:
@@ -1245,6 +1289,16 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
12451289
case KnownProtocolKind::UnsafeCxxMutableRandomAccessIterator:
12461290
M = getLoadedModule(Id_Cxx);
12471291
break;
1292+
case KnownProtocolKind::Copyable:
1293+
case KnownProtocolKind::Escapable:
1294+
// If there's no stdlib, do qualified lookup in the Builtin module,
1295+
// which will trigger the correct synthesis of the protocols in that module.
1296+
M = getStdlibModule();
1297+
if (!M) {
1298+
NameLookupKind = NLKind::QualifiedLookup;
1299+
M = TheBuiltinModule;
1300+
}
1301+
break;
12481302
default:
12491303
M = getStdlibModule();
12501304
break;
@@ -1253,7 +1307,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
12531307
if (!M)
12541308
return nullptr;
12551309
M->lookupValue(getIdentifier(getProtocolName(kind)),
1256-
NLKind::UnqualifiedLookup,
1310+
NameLookupKind,
12571311
ModuleLookupFlags::ExcludeMacroExpansions,
12581312
results);
12591313

@@ -1264,6 +1318,14 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
12641318
}
12651319
}
12661320

1321+
// If the invertible protocol wasn't found in the stdlib, synthesize it there.
1322+
if (auto ip = getInvertibleProtocolKind(kind)) {
1323+
assert(M == getStdlibModule());
1324+
auto *protocol = synthesizeInvertibleProtocolDecl(*ip);
1325+
getImpl().KnownProtocols[index] = protocol;
1326+
return protocol;
1327+
}
1328+
12671329
return nullptr;
12681330
}
12691331

@@ -6394,7 +6456,7 @@ BuiltinTupleDecl *ASTContext::getBuiltinTupleDecl() {
63946456
if (result)
63956457
return result;
63966458

6397-
auto *dc = TheBuiltinModule->getFiles()[0];
6459+
auto *dc = &TheBuiltinModule->getMainFile(FileUnitKind::Builtin);
63986460

63996461
result = new (*this) BuiltinTupleDecl(Id_TheTupleType, dc);
64006462
result->setAccess(AccessLevel::Public);

lib/AST/Builtins.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2404,6 +2404,11 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
24042404
if (Id == Context.Id_TheTupleType)
24052405
return Context.getBuiltinTupleDecl();
24062406

2407+
if (Id == Context.Id_Copyable)
2408+
return Context.synthesizeInvertibleProtocolDecl(InvertibleProtocolKind::Copyable);
2409+
if (Id == Context.Id_Escapable)
2410+
return Context.synthesizeInvertibleProtocolDecl(InvertibleProtocolKind::Escapable);
2411+
24072412
SmallVector<Type, 4> Types;
24082413
StringRef OperationName = getBuiltinBaseName(Context, Id.str(), Types);
24092414

lib/AST/Decl.cpp

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6590,34 +6590,35 @@ bool ProtocolDecl::inheritsFrom(const ProtocolDecl *super) const {
65906590
}
65916591

65926592
bool ProtocolDecl::requiresInvertible(InvertibleProtocolKind ip) const {
6593-
// Specially handle when asking if an invertible protocol requires another.
6593+
// Protocols don't inherit from themselves.
65946594
if (auto thisIP = getInvertibleProtocolKind()) {
6595-
if (SWIFT_ENABLE_EXPERIMENTAL_NONCOPYABLE_GENERICS) {
6596-
// Hardcode that the invertible protocols do not require themselves.
6597-
// Otherwise, defer to what the stdlib says by walking the protocol.
6598-
if (thisIP == ip)
6599-
return false;
6600-
} else {
6601-
// The stdlib was NOT built with noncopyable generics, so claim that
6602-
// this invertible protocol require no others.
6603-
// FIXME: this configuration will eventually go away.
6595+
if (thisIP == ip)
66046596
return false;
6605-
}
66066597
}
66076598

66086599
auto kp = ::getKnownProtocolKind(ip);
6600+
6601+
// Otherwise, check for inverses on all of the inherited protocols. If there
6602+
// is one protocol missing an inverse for this `super` protocol, then it is
6603+
// implicitly inherited.
66096604
return walkInheritedProtocols([kp, ip](ProtocolDecl *proto) {
66106605
if (proto->isSpecificProtocol(kp))
6611-
return TypeWalker::Action::Stop; // it is required.
6606+
return TypeWalker::Action::Stop; // It is explicitly inherited.
6607+
6608+
// There is no implicit inheritance of an invertible protocol requirement
6609+
// on an invertible protocol itself.
6610+
if (proto->getInvertibleProtocolKind())
6611+
return TypeWalker::Action::Continue;
66126612

6613+
// Otherwise, check to see if there's an inverse on this protocol.
66136614
switch (proto->getMarking(ip).getInverse().getKind()) {
66146615
case InverseMarking::Kind::None:
6615-
return TypeWalker::Action::Stop; // it is required.
6616+
return TypeWalker::Action::Stop; // No inverse, so implicitly inherited.
66166617

66176618
case InverseMarking::Kind::LegacyExplicit:
66186619
case InverseMarking::Kind::Explicit:
66196620
case InverseMarking::Kind::Inferred:
6620-
// the implicit requirement was suppressed on this protocol, keep looking.
6621+
// The implicit requirement was suppressed on this protocol, keep looking.
66216622
return TypeWalker::Action::Continue;
66226623
}
66236624
});
@@ -6750,6 +6751,7 @@ ProtocolDecl::setLazyPrimaryAssociatedTypeMembers(
67506751
void ProtocolDecl::computeKnownProtocolKind() const {
67516752
auto module = getModuleContext();
67526753
if (module != module->getASTContext().getStdlibModule() &&
6754+
module != module->getASTContext().TheBuiltinModule &&
67536755
!module->getName().is("Foundation") &&
67546756
!module->getName().is("_Differentiation") &&
67556757
!module->getName().is("_Concurrency") &&

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1037,7 +1037,10 @@ StructuralRequirementsRequest::evaluate(Evaluator &evaluator,
10371037

10381038
desugarRequirements(result, inverses, errors);
10391039

1040-
InverseRequirement::expandDefaults(ctx, needsDefaultRequirements, result);
1040+
// We do not expand defaults for invertible protocols themselves.
1041+
if (!proto->getInvertibleProtocolKind())
1042+
InverseRequirement::expandDefaults(ctx, needsDefaultRequirements, result);
1043+
10411044
applyInverses(ctx, needsDefaultRequirements, inverses, result, errors);
10421045

10431046
diagnoseRequirementErrors(ctx, errors,

lib/SILGen/SILGenTopLevel.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -377,9 +377,8 @@ void SILGenTopLevel::visitSourceFile(SourceFile *SF) {
377377

378378
if (auto *SynthesizedFile = SF->getSynthesizedFile()) {
379379
for (auto *D : SynthesizedFile->getTopLevelDecls()) {
380-
if (isa<ExtensionDecl>(D)) {
381-
visit(D);
382-
}
380+
assert(isa<ExtensionDecl>(D) || isa<ProtocolDecl>(D));
381+
visit(D);
383382
}
384383
}
385384

lib/Sema/TypeChecker.cpp

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -290,21 +290,27 @@ TypeCheckSourceFileRequest::evaluate(Evaluator &eval, SourceFile *SF) const {
290290
}
291291
}
292292

293-
// Type-check macro-generated or implicitly-synthesized extensions.
293+
// Type-check macro-generated or implicitly-synthesized decls.
294294
if (auto *synthesizedSF = SF->getSynthesizedFile()) {
295295
for (auto *decl : synthesizedSF->getTopLevelDecls()) {
296-
auto extension = cast<ExtensionDecl>(decl);
297-
298-
// Limit typechecking of synthesized _implicit_ extensions to conformance
299-
// checking. This is done because a conditional conformance to Copyable
300-
// is synthesized as an extension, based on the markings of `~Copyable`
301-
// in a value type. This call to `checkConformancesInContext` will
302-
// the actual check to verify that the conditional extension is correct,
303-
// as it may be an invalid conformance.
304-
if (extension->isImplicit())
305-
TypeChecker::checkConformancesInContext(extension);
306-
else
307-
TypeChecker::typeCheckDecl(extension);
296+
assert(isa<ExtensionDecl>(decl) || isa<ProtocolDecl>(decl));
297+
298+
// Limit typechecking of synthesized _implicit_ extensions to
299+
// conformance checking. This is done because a conditional
300+
// conformance to Copyable is synthesized as an extension, based on
301+
// the markings of ~Copyable in a value type. This call to
302+
// checkConformancesInContext will the actual check to verify that
303+
// the conditional extension is correct, as it may be an invalid
304+
// conformance.
305+
if (auto *extension = dyn_cast<ExtensionDecl>(decl)) {
306+
if (extension->isImplicit()) {
307+
TypeChecker::checkConformancesInContext(extension);
308+
continue;
309+
}
310+
}
311+
312+
// For other kinds of decls, do normal typechecking.
313+
TypeChecker::typeCheckDecl(decl);
308314
}
309315
}
310316
SF->typeCheckDelayedFunctions();

stdlib/public/core/Misc.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -169,17 +169,8 @@ func _rethrowsViaClosure(_ fn: () throws -> ()) rethrows {
169169
try fn()
170170
}
171171

172-
#if $NoncopyableGenerics && $NonescapableTypes
173-
@_marker public protocol Copyable: ~Escapable {}
174-
@_marker public protocol Escapable: ~Copyable {}
175-
176-
#elseif $NoncopyableGenerics
177172
@_marker public protocol Copyable {}
178-
@_marker public protocol Escapable: ~Copyable {}
179173

180-
#else
181-
@_marker public protocol Copyable {}
182174
@_marker public protocol Escapable {}
183-
#endif
184175

185176
@_marker public protocol _BitwiseCopyable {}

test/Sema/copyable_constraint.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
// >> First verify that when building the stdlib, we do have the copyable constraint. Note the module-name!!
33
// RUN: %target-swift-frontend -typecheck -verify -parse-stdlib -module-name Swift %s
44

5-
// FIXME: Now demonstrate that plain -parse-stdlib, such as in some arbitrary test, doesn't get the Copyable constraint :(
6-
// RUN: not %target-swift-frontend -typecheck -verify -parse-stdlib %s
5+
// Demonstrate that plain -parse-stdlib, such as in some arbitrary test, does still get the Copyable constraint
6+
// RUN: %target-swift-frontend -typecheck -verify -parse-stdlib %s
77

88
@_marker public protocol Copyable {}
99

0 commit comments

Comments
 (0)