Skip to content

Commit 6fb2ad1

Browse files
authored
Merge pull request #13378 from DougGregor/associated-type-infer-refactor-more
2 parents 017323c + 82dde17 commit 6fb2ad1

File tree

6 files changed

+1222
-1126
lines changed

6 files changed

+1222
-1126
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
//===--- RequirementEnvironment.h - Swift Language Type ASTs ----*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file defines the RequirementEnvironment class, which is used to
14+
// capture how a witness to a protocol requirement maps type parameters.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef SWIFT_AST_REQUIREMENT_ENVIRONMENT_H
19+
#define SWIFT_AST_REQUIREMENT_ENVIRONMENT_H
20+
21+
#include "swift/AST/SubstitutionMap.h"
22+
23+
namespace swift {
24+
25+
/// Describes the environment of a requirement that will be used when
26+
/// matching witnesses against the requirement and to form the resulting
27+
/// \c Witness value.
28+
///
29+
/// The produced generic environment will have a fresh set of archetypes that
30+
/// describe the combined constraints of the requirement (because those
31+
/// are available to all potential witnesses) as well as the constraints from
32+
/// the context to which the protocol conformance is ascribed, which may
33+
/// include additional constraints beyond those of the extended type if the
34+
/// conformance is conditional. The type parameters for the generic
35+
/// environment are the type parameters of the conformance context
36+
/// (\c conformanceDC) with another (deeper) level of type parameters for
37+
/// generic requirements. See the \c Witness class for more information about
38+
/// this synthetic environment.
39+
class RequirementEnvironment {
40+
/// A generic signature that combines the generic parameters of the
41+
/// concrete conforming type with the generic parameters of the
42+
/// requirement.
43+
///
44+
///
45+
/// For example, if you have:
46+
///
47+
/// protocol P { func f<T>(_: T) }
48+
/// struct S<A, B> : P { func f<T>(_: T) }
49+
///
50+
/// The requirement and witness signatures are, respectively:
51+
///
52+
/// <Self : P, T>
53+
/// <A, B, T>
54+
///
55+
/// The synthetic signature in this case is just the witness signature.
56+
///
57+
/// It may be that the witness is more generic than the requirement,
58+
/// for example:
59+
///
60+
/// protocol P { func f(_: Int) }
61+
/// struct S<A, B> : P { func f<T>(_: T) { } }
62+
///
63+
/// Here, the requirement signature and witness signatures are:
64+
///
65+
/// <Self : P>
66+
/// <A, B, T>
67+
///
68+
/// The synthetic signature is just:
69+
///
70+
/// <A, B>
71+
///
72+
/// The witness thunk emitted by SILGen uses the synthetic signature.
73+
/// Therefore one invariant we preserve is that the witness thunk is
74+
/// ABI compatible with the requirement's function type.
75+
GenericSignature *syntheticSignature = nullptr;
76+
GenericEnvironment *syntheticEnvironment = nullptr;
77+
78+
/// The generic signature of the protocol requirement member.
79+
GenericSignature *reqSig = nullptr;
80+
81+
/// A substitution map mapping the requirement signature to the
82+
/// generic parameters of the synthetic signature.
83+
SubstitutionMap reqToSyntheticEnvMap;
84+
85+
public:
86+
/// Create a new environment for matching the given requirement within a
87+
/// particular conformance.
88+
///
89+
/// \param conformanceDC The \c DeclContext to which the protocol
90+
/// conformance is ascribed, which provides additional constraints.
91+
///
92+
/// \param reqSig The generic signature of the requirement for which we
93+
/// are creating a generic environment.
94+
///
95+
/// \param proto The protocol containing the requirement.
96+
///
97+
/// \param conformance The protocol conformance, or null if there is no
98+
/// conformance (because we're finding default implementations).
99+
RequirementEnvironment(DeclContext *conformanceDC,
100+
GenericSignature *reqSig,
101+
ProtocolDecl *proto,
102+
ClassDecl *covariantSelf,
103+
ProtocolConformance *conformance);
104+
105+
/// Retrieve the synthetic generic environment.
106+
GenericEnvironment *getSyntheticEnvironment() const {
107+
return syntheticEnvironment;
108+
}
109+
110+
/// Retrieve the generic signature of the requirement.
111+
const GenericSignature *getRequirementSignature() const {
112+
return reqSig;
113+
}
114+
115+
/// Retrieve the substitution map that maps the interface types of the
116+
/// requirement to the interface types of the synthetic environment.
117+
const SubstitutionMap &getRequirementToSyntheticMap() const {
118+
return reqToSyntheticEnvMap;
119+
}
120+
};
121+
122+
}
123+
124+
#endif // SWIFT_AST_REQUIREMENT_ENVIRONMENT_H

lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ add_swift_library(swiftAST STATIC
4444
PrettyStackTrace.cpp
4545
ProtocolConformance.cpp
4646
RawComment.cpp
47+
RequirementEnvironment.cpp
4748
SyntaxASTMap.cpp
4849
SILLayout.cpp
4950
Stmt.cpp

lib/AST/RequirementEnvironment.cpp

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
//===--- RequirementEnvironment.cpp - ASTContext Implementation -----------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file implements the RequirementEnvironment class, which is used to
14+
// capture how a witness to a protocol requirement maps type parameters.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#include "swift/AST/RequirementEnvironment.h"
19+
#include "swift/AST/ASTContext.h"
20+
#include "swift/AST/Decl.h"
21+
#include "swift/AST/DeclContext.h"
22+
#include "swift/AST/GenericSignature.h"
23+
#include "swift/AST/GenericSignatureBuilder.h"
24+
#include "swift/AST/ProtocolConformance.h"
25+
#include "swift/AST/Types.h"
26+
#include "llvm/ADT/Statistic.h"
27+
28+
#define DEBUG_TYPE "Protocol conformance checking"
29+
30+
using namespace swift;
31+
32+
STATISTIC(NumRequirementEnvironments, "# of requirement environments");
33+
34+
RequirementEnvironment::RequirementEnvironment(
35+
DeclContext *conformanceDC,
36+
GenericSignature *reqSig,
37+
ProtocolDecl *proto,
38+
ClassDecl *covariantSelf,
39+
ProtocolConformance *conformance)
40+
: reqSig(reqSig) {
41+
ASTContext &ctx = conformanceDC->getASTContext();
42+
43+
auto concreteType = conformanceDC->getSelfInterfaceType();
44+
auto *conformanceSig = conformanceDC->getGenericSignatureOfContext();
45+
46+
// Build a substitution map from the generic parameters of the conforming
47+
// type to the synthetic environment.
48+
//
49+
// For structs, enums and protocols, this is a 1:1 mapping; for classes,
50+
// we increase the depth of each generic parameter by 1 so that we can
51+
// introduce a class-bound 'Self' parameter.
52+
auto substConcreteType = concreteType;
53+
SubstitutionMap conformanceToSyntheticEnvMap;
54+
if (conformanceSig) {
55+
conformanceToSyntheticEnvMap = conformanceSig->getSubstitutionMap(
56+
[&](SubstitutableType *type) {
57+
auto *genericParam = cast<GenericTypeParamType>(type);
58+
if (covariantSelf) {
59+
return GenericTypeParamType::get(
60+
genericParam->getDepth() + 1,
61+
genericParam->getIndex(),
62+
ctx);
63+
}
64+
65+
return GenericTypeParamType::get(
66+
genericParam->getDepth(),
67+
genericParam->getIndex(),
68+
ctx);
69+
},
70+
MakeAbstractConformanceForGenericType());
71+
72+
substConcreteType = concreteType.subst(conformanceToSyntheticEnvMap);
73+
}
74+
75+
// Calculate the depth at which the requirement's generic parameters
76+
// appear in the synthetic signature.
77+
unsigned depth = 0;
78+
if (covariantSelf) {
79+
depth++;
80+
}
81+
if (conformanceSig) {
82+
depth += conformanceSig->getGenericParams().back()->getDepth() + 1;
83+
}
84+
85+
// Build a substitution map to replace the protocol's \c Self and the type
86+
// parameters of the requirement into a combined context that provides the
87+
// type parameters of the conformance context and the parameters of the
88+
// requirement.
89+
auto selfType = cast<GenericTypeParamType>(
90+
proto->getSelfInterfaceType()->getCanonicalType());
91+
92+
reqToSyntheticEnvMap = reqSig->getSubstitutionMap(
93+
[selfType, substConcreteType, depth, covariantSelf, &ctx]
94+
(SubstitutableType *type) -> Type {
95+
// If the conforming type is a class, the protocol 'Self' maps to
96+
// the class-constrained 'Self'. Otherwise, it maps to the concrete
97+
// type.
98+
if (type->isEqual(selfType)) {
99+
if (covariantSelf)
100+
return GenericTypeParamType::get(/*depth=*/0, /*index=*/0, ctx);
101+
return substConcreteType;
102+
}
103+
// Other requirement generic parameters map 1:1 with their depth
104+
// increased appropriately.
105+
auto *genericParam = cast<GenericTypeParamType>(type);
106+
// In a protocol requirement, the only generic parameter at depth 0
107+
// should be 'Self', and all others at depth 1. Anything else is
108+
// invalid code.
109+
if (genericParam->getDepth() != 1)
110+
return Type();
111+
auto substGenericParam =
112+
GenericTypeParamType::get(depth, genericParam->getIndex(), ctx);
113+
return substGenericParam;
114+
},
115+
[selfType, substConcreteType, conformance, conformanceDC, &ctx](
116+
CanType type, Type replacement, ProtocolType *protoType)
117+
-> Optional<ProtocolConformanceRef> {
118+
auto proto = protoType->getDecl();
119+
120+
// The protocol 'Self' conforms concretely to the conforming type.
121+
if (type->isEqual(selfType)) {
122+
ProtocolConformance *specialized = conformance;
123+
if (conformance && conformance->getGenericSignature()) {
124+
auto concreteSubs =
125+
substConcreteType->getContextSubstitutionMap(
126+
conformanceDC->getParentModule(), conformanceDC);
127+
specialized =
128+
ctx.getSpecializedConformance(substConcreteType, conformance,
129+
concreteSubs);
130+
}
131+
132+
if (specialized)
133+
return ProtocolConformanceRef(specialized);
134+
}
135+
136+
// All other generic parameters come from the requirement itself
137+
// and conform abstractly.
138+
return ProtocolConformanceRef(proto);
139+
});
140+
141+
// If the requirement itself is non-generic, the synthetic signature
142+
// is that of the conformance context.
143+
if (!covariantSelf &&
144+
reqSig->getGenericParams().size() == 1 &&
145+
reqSig->getRequirements().size() == 1) {
146+
syntheticSignature = conformanceDC->getGenericSignatureOfContext();
147+
if (syntheticSignature) {
148+
syntheticSignature = syntheticSignature->getCanonicalSignature();
149+
syntheticEnvironment =
150+
syntheticSignature->createGenericEnvironment();
151+
}
152+
153+
return;
154+
}
155+
156+
// Construct a generic signature builder by collecting the constraints
157+
// from the requirement and the context of the conformance together,
158+
// because both define the capabilities of the requirement.
159+
GenericSignatureBuilder builder(ctx);
160+
161+
auto source =
162+
GenericSignatureBuilder::FloatingRequirementSource::forAbstract();
163+
164+
// If the conforming type is a class, add a class-constrained 'Self'
165+
// parameter.
166+
if (covariantSelf) {
167+
auto paramTy = GenericTypeParamType::get(/*depth=*/0, /*index=*/0, ctx);
168+
builder.addGenericParameter(paramTy);
169+
}
170+
171+
// Now, add all generic parameters from the conforming type.
172+
if (conformanceSig) {
173+
for (auto param : conformanceSig->getGenericParams()) {
174+
builder.addGenericParameter(
175+
Type(param).subst(conformanceToSyntheticEnvMap)
176+
->castTo<GenericTypeParamType>());
177+
}
178+
}
179+
180+
// Next, add requirements.
181+
if (covariantSelf) {
182+
auto paramTy = GenericTypeParamType::get(/*depth=*/0, /*index=*/0, ctx);
183+
Requirement reqt(RequirementKind::Superclass, paramTy, substConcreteType);
184+
builder.addRequirement(reqt, source, nullptr);
185+
}
186+
187+
if (conformanceSig) {
188+
for (auto &rawReq : conformanceSig->getRequirements()) {
189+
if (auto req = rawReq.subst(conformanceToSyntheticEnvMap))
190+
builder.addRequirement(*req, source, nullptr);
191+
}
192+
}
193+
194+
// Finally, add the generic parameters from the requirement.
195+
for (auto genericParam : reqSig->getGenericParams().slice(1)) {
196+
// The only depth that makes sense is depth == 1, the generic parameters
197+
// of the requirement itself. Anything else is from invalid code.
198+
if (genericParam->getDepth() != 1) {
199+
return;
200+
}
201+
202+
// Create an equivalent generic parameter at the next depth.
203+
auto substGenericParam =
204+
GenericTypeParamType::get(depth, genericParam->getIndex(), ctx);
205+
206+
builder.addGenericParameter(substGenericParam);
207+
}
208+
209+
++NumRequirementEnvironments;
210+
211+
// Next, add each of the requirements (mapped from the requirement's
212+
// interface types into the abstract type parameters).
213+
for (auto &rawReq : reqSig->getRequirements()) {
214+
// FIXME: This should not be necessary, since the constraint is redundant,
215+
// but we need it to work around some crashes for now.
216+
if (rawReq.getKind() == RequirementKind::Conformance &&
217+
rawReq.getFirstType()->isEqual(selfType) &&
218+
rawReq.getSecondType()->isEqual(proto->getDeclaredType()))
219+
continue;
220+
221+
if (auto req = rawReq.subst(reqToSyntheticEnvMap))
222+
builder.addRequirement(*req, source, conformanceDC->getParentModule());
223+
}
224+
225+
// Produce the generic signature and environment.
226+
// FIXME: Pass in a source location for the conformance, perhaps? It seems
227+
// like this could fail.
228+
syntheticSignature =
229+
std::move(builder).computeGenericSignature(SourceLoc());
230+
syntheticEnvironment = syntheticSignature->createGenericEnvironment();
231+
}

0 commit comments

Comments
 (0)