Skip to content

Commit e6d1ef9

Browse files
authored
Merge pull request #41868 from slavapestov/rqm-opaque-archetypes
RequirementMachine: Opaque archetype support (sort of)
2 parents 61da992 + 2f727d6 commit e6d1ef9

13 files changed

+213
-55
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,11 @@ namespace swift {
549549
/// if you have a testcase which requires this, please submit a bug report.
550550
bool EnableRequirementMachineLoopNormalization = false;
551551

552+
/// Enable experimental, more correct support for opaque result types as
553+
/// concrete types. This will sometimes fail to produce a convergent
554+
/// rewrite system.
555+
bool EnableRequirementMachineOpaqueArchetypes = false;
556+
552557
/// Enables dumping type witness systems from associated type inference.
553558
bool DumpTypeWitnessSystems = false;
554559

include/swift/Option/FrontendOptions.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,9 @@ def disable_requirement_machine_concrete_contraction : Flag<["-"], "disable-requ
362362
def enable_requirement_machine_loop_normalization : Flag<["-"], "enable-requirement-machine-loop-normalization">,
363363
HelpText<"Enable stronger minimization algorithm, for debugging only">;
364364

365+
def enable_requirement_machine_opaque_archetypes : Flag<["-"], "enable-requirement-machine-opaque-archetypes">,
366+
HelpText<"Enable more correct opaque archetype support, which is off by default because it might fail to produce a convergent rewrite system">;
367+
365368
def dump_type_witness_systems : Flag<["-"], "dump-type-witness-systems">,
366369
HelpText<"Enables dumping type witness systems from associated type inference">;
367370

lib/AST/RequirementMachine/ConcreteContraction.cpp

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -235,19 +235,10 @@ Optional<Type> ConcreteContraction::substTypeParameter(
235235
->substBaseType(module, *substBaseType);
236236
}
237237

238-
auto *decl = (*substBaseType)->getAnyNominal();
239-
if (decl == nullptr) {
240-
if (Debug) {
241-
llvm::dbgs() << "@@@ Not a nominal type: " << *substBaseType << "\n";
242-
}
243-
244-
return None;
245-
}
246-
247238
// An unresolved DependentMemberType stores an identifier. Handle this
248239
// by performing a name lookup into the base type.
249240
SmallVector<TypeDecl *> concreteDecls;
250-
lookupConcreteNestedType(decl, memberType->getName(), concreteDecls);
241+
lookupConcreteNestedType(*substBaseType, memberType->getName(), concreteDecls);
251242

252243
auto *typeDecl = findBestConcreteNestedType(concreteDecls);
253244
if (typeDecl == nullptr) {
@@ -261,8 +252,9 @@ Optional<Type> ConcreteContraction::substTypeParameter(
261252
}
262253

263254
// Substitute the base type into the member type.
255+
auto *dc = typeDecl->getDeclContext();
264256
auto subMap = (*substBaseType)->getContextSubstitutionMap(
265-
decl->getParentModule(), typeDecl->getDeclContext());
257+
dc->getParentModule(), dc);
266258
return typeDecl->getDeclaredInterfaceType().subst(subMap);
267259
}
268260

lib/AST/RequirementMachine/ConcreteTypeWitness.cpp

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,7 @@ void PropertyMap::concretizeNestedTypesFromConcreteParent(
132132
//
133133
// This occurs when a pair of rules are inherited from the property map
134134
// entry for this key's suffix.
135-
auto pair = std::make_pair(concreteRuleID, conformanceRuleID);
136-
auto found = ConcreteConformances.find(pair);
137-
if (found != ConcreteConformances.end())
135+
if (!checkRulePairOnce(concreteRuleID, conformanceRuleID))
138136
continue;
139137

140138
// FIXME: Either remove the ModuleDecl entirely from conformance lookup,
@@ -164,40 +162,44 @@ void PropertyMap::concretizeNestedTypesFromConcreteParent(
164162
continue;
165163
}
166164

167-
// FIXME: Maybe this can happen if the concrete type is an
168-
// opaque result type?
169-
assert(!conformance.isAbstract());
170-
171-
// Save this conformance for later.
172-
auto *concrete = conformance.getConcrete();
173-
auto inserted = ConcreteConformances.insert(
174-
std::make_pair(pair, concrete));
175-
assert(inserted.second);
176-
(void) inserted;
177-
178165
auto concreteConformanceSymbol = Symbol::forConcreteConformance(
179166
concreteType, substitutions, proto, Context);
180167

181168
recordConcreteConformanceRule(concreteRuleID, conformanceRuleID,
182169
requirementKind, concreteConformanceSymbol);
183170

171+
// This is disabled by default because we fail to produce a convergent
172+
// rewrite system if the opaque archetype has infinitely-recursive
173+
// nested types. Fixing this requires a better representation for
174+
// concrete conformances in the rewrite system.
175+
if (conformance.isAbstract() &&
176+
!Context.getASTContext().LangOpts.EnableRequirementMachineOpaqueArchetypes) {
177+
if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) {
178+
llvm::dbgs() << "^^ " << "Skipping abstract conformance of "
179+
<< concreteType << " to " << proto->getName() << "\n";
180+
}
181+
182+
continue;
183+
}
184+
184185
for (auto *assocType : proto->getAssociatedTypeMembers()) {
185186
concretizeTypeWitnessInConformance(key, requirementKind,
186187
concreteConformanceSymbol,
187-
concrete, assocType);
188+
conformance, assocType);
188189
}
189190

190191
// We only infer conditional requirements in top-level generic signatures,
191192
// not in protocol requirement signatures.
192-
if (key.getRootProtocol() == nullptr)
193-
inferConditionalRequirements(concrete, substitutions);
193+
if (conformance.isConcrete() &&
194+
key.getRootProtocol() == nullptr)
195+
inferConditionalRequirements(conformance.getConcrete(), substitutions);
194196
}
195197
}
196198

197199
void PropertyMap::concretizeTypeWitnessInConformance(
198200
Term key, RequirementKind requirementKind,
199201
Symbol concreteConformanceSymbol,
200-
ProtocolConformance *concrete,
202+
ProtocolConformanceRef conformance,
201203
AssociatedTypeDecl *assocType) const {
202204
auto concreteType = concreteConformanceSymbol.getConcreteType();
203205
auto substitutions = concreteConformanceSymbol.getSubstitutions();
@@ -211,17 +213,35 @@ void PropertyMap::concretizeTypeWitnessInConformance(
211213
<< " on " << concreteType << "\n";
212214
}
213215

214-
auto t = concrete->getTypeWitness(assocType);
215-
if (!t) {
216-
if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) {
217-
llvm::dbgs() << "^^ " << "Type witness for " << assocType->getName()
218-
<< " of " << concreteType << " could not be inferred\n";
216+
CanType typeWitness;
217+
if (conformance.isConcrete()) {
218+
auto t = conformance.getConcrete()->getTypeWitness(assocType);
219+
220+
if (!t) {
221+
if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) {
222+
llvm::dbgs() << "^^ " << "Type witness for " << assocType->getName()
223+
<< " of " << concreteType << " could not be inferred\n";
224+
}
225+
226+
t = ErrorType::get(concreteType);
219227
}
220228

221-
t = ErrorType::get(concreteType);
222-
}
229+
typeWitness = t->getCanonicalType();
230+
} else if (conformance.isAbstract()) {
231+
auto archetype = concreteType->getAs<OpaqueTypeArchetypeType>();
232+
if (archetype == nullptr) {
233+
llvm::errs() << "Should only have an abstract conformance with an "
234+
<< "opaque archetype type\n";
235+
llvm::errs() << "Symbol: " << concreteConformanceSymbol << "\n";
236+
llvm::errs() << "Term: " << key << "\n";
237+
dump(llvm::errs());
238+
abort();
239+
}
223240

224-
auto typeWitness = t->getCanonicalType();
241+
typeWitness = archetype->getNestedType(assocType)->getCanonicalType();
242+
} else if (conformance.isInvalid()) {
243+
typeWitness = CanType(ErrorType::get(Context.getASTContext()));
244+
}
225245

226246
if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) {
227247
llvm::dbgs() << "^^ " << "Type witness for " << assocType->getName()

lib/AST/RequirementMachine/GenericSignatureQueries.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "swift/AST/ASTContext.h"
1919
#include "swift/AST/Decl.h"
20+
#include "swift/AST/GenericEnvironment.h"
2021
#include "swift/AST/GenericSignature.h"
2122
#include "swift/AST/Module.h"
2223
#include <vector>
@@ -659,8 +660,7 @@ RequirementMachine::lookupNestedType(Type depType, Identifier name) const {
659660
typeToSearch = props->getSuperclassBound();
660661

661662
if (typeToSearch)
662-
if (auto *decl = typeToSearch->getAnyNominal())
663-
lookupConcreteNestedType(decl, name, concreteDecls);
663+
lookupConcreteNestedType(typeToSearch, name, concreteDecls);
664664
}
665665

666666
if (bestAssocType) {

lib/AST/RequirementMachine/NameLookup.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,33 @@
1212

1313
#include "NameLookup.h"
1414
#include "swift/AST/Decl.h"
15+
#include "swift/AST/GenericEnvironment.h"
1516
#include "swift/AST/Module.h"
17+
#include "swift/AST/Types.h"
1618
#include "llvm/ADT/SmallVector.h"
1719
#include <algorithm>
1820

1921
using namespace swift;
2022
using namespace rewriting;
2123

24+
void
25+
swift::rewriting::lookupConcreteNestedType(
26+
Type baseType,
27+
Identifier name,
28+
SmallVectorImpl<TypeDecl *> &concreteDecls) {
29+
if (auto *decl = baseType->getAnyNominal())
30+
lookupConcreteNestedType(decl, name, concreteDecls);
31+
else if (auto *archetype = baseType->getAs<OpaqueTypeArchetypeType>()) {
32+
// If our concrete type is an opaque result archetype, look into its
33+
// generic environment recursively.
34+
auto *genericEnv = archetype->getGenericEnvironment();
35+
auto genericSig = genericEnv->getGenericSignature();
36+
37+
concreteDecls.push_back(
38+
genericSig->lookupNestedType(archetype->getInterfaceType(), name));
39+
}
40+
}
41+
2242
void
2343
swift::rewriting::lookupConcreteNestedType(
2444
NominalTypeDecl *decl,

lib/AST/RequirementMachine/NameLookup.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,16 @@ namespace swift {
1919

2020
class Identifier;
2121
class NominalTypeDecl;
22+
class Type;
2223
class TypeDecl;
2324

2425
namespace rewriting {
2526

27+
void lookupConcreteNestedType(
28+
Type baseType,
29+
Identifier name,
30+
llvm::SmallVectorImpl<TypeDecl *> &concreteDecls);
31+
2632
void lookupConcreteNestedType(
2733
NominalTypeDecl *decl,
2834
Identifier name,

lib/AST/RequirementMachine/PropertyMap.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,6 @@ class PropertyMap {
174174
// To avoid wasted work from re-introducing the same induced rules,
175175
// we track the rules we've seen already on previous builds.
176176

177-
/// Maps a pair of rules where the first is a conformance rule and the
178-
/// second is a superclass or concrete type rule, to a concrete
179-
/// conformance.
180-
llvm::DenseMap<std::pair<unsigned, unsigned>, ProtocolConformance *>
181-
ConcreteConformances;
182-
183177
/// Superclass requirements always imply a layout requirement, and
184178
/// concrete type requirements where the type is a class imply a
185179
/// superclass requirement.
@@ -291,7 +285,7 @@ class PropertyMap {
291285
void concretizeTypeWitnessInConformance(
292286
Term key, RequirementKind requirementKind,
293287
Symbol concreteConformanceSymbol,
294-
ProtocolConformance *concrete,
288+
ProtocolConformanceRef conformance,
295289
AssociatedTypeDecl *assocType) const;
296290

297291
void inferConditionalRequirements(

lib/AST/RequirementMachine/RequirementLowering.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,13 @@ static void desugarConformanceRequirement(Type subjectType, Type constraintType,
168168
errors.push_back(RequirementError::forRedundantRequirement(
169169
{RequirementKind::Conformance, subjectType, constraintType}, loc));
170170

171-
assert(conformance.isConcrete());
172-
auto *concrete = conformance.getConcrete();
173-
174-
// Introduce conditional requirements if the subject type is concrete.
175-
for (auto req : concrete->getConditionalRequirements()) {
176-
desugarRequirement(req, result, errors);
171+
if (conformance.isConcrete()) {
172+
// Introduce conditional requirements if the conformance is concrete.
173+
for (auto req : conformance.getConcrete()->getConditionalRequirements()) {
174+
desugarRequirement(req, result, errors);
175+
}
177176
}
177+
178178
return;
179179
}
180180

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
10071007
if (Args.hasArg(OPT_enable_requirement_machine_loop_normalization))
10081008
Opts.EnableRequirementMachineLoopNormalization = true;
10091009

1010+
if (Args.hasArg(OPT_enable_requirement_machine_opaque_archetypes))
1011+
Opts.EnableRequirementMachineOpaqueArchetypes = true;
1012+
10101013
Opts.DumpTypeWitnessSystems = Args.hasArg(OPT_dump_type_witness_systems);
10111014

10121015
return HadError || UnsupportedOS || UnsupportedArch;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// RUN: %target-swift-frontend -typecheck -verify %s -disable-availability-checking -debug-generic-signatures -requirement-machine-inferred-signatures=on -enable-requirement-machine-opaque-archetypes 2>&1 | %FileCheck %s
2+
3+
protocol P1 {
4+
associatedtype T : P2
5+
associatedtype U
6+
}
7+
8+
struct S_P1 : P1 {
9+
typealias T = S_P2
10+
typealias U = Int
11+
}
12+
13+
protocol P2 {}
14+
15+
struct S_P2 : P2 {}
16+
17+
protocol P {
18+
associatedtype T
19+
20+
var t: T { get }
21+
}
22+
23+
struct DefinesOpaqueP1 : P {
24+
var t: some P1 {
25+
return S_P1()
26+
}
27+
}
28+
29+
struct ConcreteHasP<T : P1, TT : P2, TU> {}
30+
31+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=ConcreteHasP
32+
// CHECK-NEXT: Generic signature: <T, TT, TU where T == some P1, TT == (some P1).T, TU == (some P1).U>
33+
extension ConcreteHasP where T == DefinesOpaqueP1.T, TT == T.T, TU == T.U {
34+
func checkSameType1(_ t: TT) -> DefinesOpaqueP1.T.T { return t }
35+
func checkSameType2(_ u: TU) -> DefinesOpaqueP1.T.U { return u }
36+
37+
func checkSameType3(_ t: T.T) -> DefinesOpaqueP1.T.T { return t }
38+
func checkSameType4(_ u: T.U) -> DefinesOpaqueP1.T.U { return u }
39+
}
40+
41+
struct G<T> {}
42+
43+
protocol HasP {
44+
associatedtype T : P1
45+
associatedtype U
46+
}
47+
48+
// CHECK-LABEL: ExtensionDecl line={{.*}} base=HasP
49+
// CHECK-NEXT: Generic signature: <Self where Self : HasP, Self.[HasP]T == some P1, Self.[HasP]U == G<(some P1).T>>
50+
extension HasP where T == DefinesOpaqueP1.T, U == G<T.T> {
51+
func checkSameType1(_ t: T.T) -> DefinesOpaqueP1.T.T { return t }
52+
func checkSameType2(_ u: T.U) -> DefinesOpaqueP1.T.U { return u }
53+
}
54+
55+
// FIXME: This does not work with -enable-requirement-machine-opaque-archetypes.
56+
// See opaque_archetype_concrete_requirement_recursive.swift for a demonstration
57+
// that it works without the flag (but more involved examples like the above
58+
// won't work).
59+
60+
protocol RecursiveP {
61+
associatedtype T : RecursiveP
62+
}
63+
64+
struct S_RecursiveP : RecursiveP {
65+
typealias T = S_RecursiveP
66+
}
67+
68+
struct DefinesRecursiveP : P {
69+
var t: some RecursiveP {
70+
return S_RecursiveP()
71+
}
72+
}
73+
74+
protocol HasRecursiveP {
75+
associatedtype T : RecursiveP
76+
}
77+
78+
extension HasRecursiveP where T == DefinesRecursiveP.T {}
79+
// expected-error@-1 {{cannot build rewrite system for generic signature; rule length limit exceeded}}
80+
// expected-note@-2 {{failed rewrite rule is τ_0_0.[HasRecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[concrete: ((((((((((some RecursiveP).T).T).T).T).T).T).T).T).T).T] => τ_0_0.[HasRecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T]}}
81+

0 commit comments

Comments
 (0)