-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Conformance checking] More refactoring of associated type inference. #13378
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
swift-ci
merged 1 commit into
swiftlang:master
from
DougGregor:associated-type-infer-refactor-more
Dec 12, 2017
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
//===--- RequirementEnvironment.h - Swift Language Type ASTs ----*- C++ -*-===// | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This file defines the RequirementEnvironment class, which is used to | ||
// capture how a witness to a protocol requirement maps type parameters. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef SWIFT_AST_REQUIREMENT_ENVIRONMENT_H | ||
#define SWIFT_AST_REQUIREMENT_ENVIRONMENT_H | ||
|
||
#include "swift/AST/SubstitutionMap.h" | ||
|
||
namespace swift { | ||
|
||
/// Describes the environment of a requirement that will be used when | ||
/// matching witnesses against the requirement and to form the resulting | ||
/// \c Witness value. | ||
/// | ||
/// The produced generic environment will have a fresh set of archetypes that | ||
/// describe the combined constraints of the requirement (because those | ||
/// are available to all potential witnesses) as well as the constraints from | ||
/// the context to which the protocol conformance is ascribed, which may | ||
/// include additional constraints beyond those of the extended type if the | ||
/// conformance is conditional. The type parameters for the generic | ||
/// environment are the type parameters of the conformance context | ||
/// (\c conformanceDC) with another (deeper) level of type parameters for | ||
/// generic requirements. See the \c Witness class for more information about | ||
/// this synthetic environment. | ||
class RequirementEnvironment { | ||
/// A generic signature that combines the generic parameters of the | ||
/// concrete conforming type with the generic parameters of the | ||
/// requirement. | ||
/// | ||
/// | ||
/// For example, if you have: | ||
/// | ||
/// protocol P { func f<T>(_: T) } | ||
/// struct S<A, B> : P { func f<T>(_: T) } | ||
/// | ||
/// The requirement and witness signatures are, respectively: | ||
/// | ||
/// <Self : P, T> | ||
/// <A, B, T> | ||
/// | ||
/// The synthetic signature in this case is just the witness signature. | ||
/// | ||
/// It may be that the witness is more generic than the requirement, | ||
/// for example: | ||
/// | ||
/// protocol P { func f(_: Int) } | ||
/// struct S<A, B> : P { func f<T>(_: T) { } } | ||
/// | ||
/// Here, the requirement signature and witness signatures are: | ||
/// | ||
/// <Self : P> | ||
/// <A, B, T> | ||
/// | ||
/// The synthetic signature is just: | ||
/// | ||
/// <A, B> | ||
/// | ||
/// The witness thunk emitted by SILGen uses the synthetic signature. | ||
/// Therefore one invariant we preserve is that the witness thunk is | ||
/// ABI compatible with the requirement's function type. | ||
GenericSignature *syntheticSignature = nullptr; | ||
GenericEnvironment *syntheticEnvironment = nullptr; | ||
|
||
/// The generic signature of the protocol requirement member. | ||
GenericSignature *reqSig = nullptr; | ||
|
||
/// A substitution map mapping the requirement signature to the | ||
/// generic parameters of the synthetic signature. | ||
SubstitutionMap reqToSyntheticEnvMap; | ||
|
||
public: | ||
/// Create a new environment for matching the given requirement within a | ||
/// particular conformance. | ||
/// | ||
/// \param conformanceDC The \c DeclContext to which the protocol | ||
/// conformance is ascribed, which provides additional constraints. | ||
/// | ||
/// \param reqSig The generic signature of the requirement for which we | ||
/// are creating a generic environment. | ||
/// | ||
/// \param proto The protocol containing the requirement. | ||
/// | ||
/// \param conformance The protocol conformance, or null if there is no | ||
/// conformance (because we're finding default implementations). | ||
RequirementEnvironment(DeclContext *conformanceDC, | ||
GenericSignature *reqSig, | ||
ProtocolDecl *proto, | ||
ClassDecl *covariantSelf, | ||
ProtocolConformance *conformance); | ||
|
||
/// Retrieve the synthetic generic environment. | ||
GenericEnvironment *getSyntheticEnvironment() const { | ||
return syntheticEnvironment; | ||
} | ||
|
||
/// Retrieve the generic signature of the requirement. | ||
const GenericSignature *getRequirementSignature() const { | ||
return reqSig; | ||
} | ||
|
||
/// Retrieve the substitution map that maps the interface types of the | ||
/// requirement to the interface types of the synthetic environment. | ||
const SubstitutionMap &getRequirementToSyntheticMap() const { | ||
return reqToSyntheticEnvMap; | ||
} | ||
}; | ||
|
||
} | ||
|
||
#endif // SWIFT_AST_REQUIREMENT_ENVIRONMENT_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
//===--- RequirementEnvironment.cpp - ASTContext Implementation -----------===// | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also wrong description here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (FWIW, I fixed these in a subsequent, completely-unrelated PR) |
||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This file implements the RequirementEnvironment class, which is used to | ||
// capture how a witness to a protocol requirement maps type parameters. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "swift/AST/RequirementEnvironment.h" | ||
#include "swift/AST/ASTContext.h" | ||
#include "swift/AST/Decl.h" | ||
#include "swift/AST/DeclContext.h" | ||
#include "swift/AST/GenericSignature.h" | ||
#include "swift/AST/GenericSignatureBuilder.h" | ||
#include "swift/AST/ProtocolConformance.h" | ||
#include "swift/AST/Types.h" | ||
#include "llvm/ADT/Statistic.h" | ||
|
||
#define DEBUG_TYPE "Protocol conformance checking" | ||
|
||
using namespace swift; | ||
|
||
STATISTIC(NumRequirementEnvironments, "# of requirement environments"); | ||
|
||
RequirementEnvironment::RequirementEnvironment( | ||
DeclContext *conformanceDC, | ||
GenericSignature *reqSig, | ||
ProtocolDecl *proto, | ||
ClassDecl *covariantSelf, | ||
ProtocolConformance *conformance) | ||
: reqSig(reqSig) { | ||
ASTContext &ctx = conformanceDC->getASTContext(); | ||
|
||
auto concreteType = conformanceDC->getSelfInterfaceType(); | ||
auto *conformanceSig = conformanceDC->getGenericSignatureOfContext(); | ||
|
||
// Build a substitution map from the generic parameters of the conforming | ||
// type to the synthetic environment. | ||
// | ||
// For structs, enums and protocols, this is a 1:1 mapping; for classes, | ||
// we increase the depth of each generic parameter by 1 so that we can | ||
// introduce a class-bound 'Self' parameter. | ||
auto substConcreteType = concreteType; | ||
SubstitutionMap conformanceToSyntheticEnvMap; | ||
if (conformanceSig) { | ||
conformanceToSyntheticEnvMap = conformanceSig->getSubstitutionMap( | ||
[&](SubstitutableType *type) { | ||
auto *genericParam = cast<GenericTypeParamType>(type); | ||
if (covariantSelf) { | ||
return GenericTypeParamType::get( | ||
genericParam->getDepth() + 1, | ||
genericParam->getIndex(), | ||
ctx); | ||
} | ||
|
||
return GenericTypeParamType::get( | ||
genericParam->getDepth(), | ||
genericParam->getIndex(), | ||
ctx); | ||
}, | ||
MakeAbstractConformanceForGenericType()); | ||
|
||
substConcreteType = concreteType.subst(conformanceToSyntheticEnvMap); | ||
} | ||
|
||
// Calculate the depth at which the requirement's generic parameters | ||
// appear in the synthetic signature. | ||
unsigned depth = 0; | ||
if (covariantSelf) { | ||
depth++; | ||
} | ||
if (conformanceSig) { | ||
depth += conformanceSig->getGenericParams().back()->getDepth() + 1; | ||
} | ||
|
||
// Build a substitution map to replace the protocol's \c Self and the type | ||
// parameters of the requirement into a combined context that provides the | ||
// type parameters of the conformance context and the parameters of the | ||
// requirement. | ||
auto selfType = cast<GenericTypeParamType>( | ||
proto->getSelfInterfaceType()->getCanonicalType()); | ||
|
||
reqToSyntheticEnvMap = reqSig->getSubstitutionMap( | ||
[selfType, substConcreteType, depth, covariantSelf, &ctx] | ||
(SubstitutableType *type) -> Type { | ||
// If the conforming type is a class, the protocol 'Self' maps to | ||
// the class-constrained 'Self'. Otherwise, it maps to the concrete | ||
// type. | ||
if (type->isEqual(selfType)) { | ||
if (covariantSelf) | ||
return GenericTypeParamType::get(/*depth=*/0, /*index=*/0, ctx); | ||
return substConcreteType; | ||
} | ||
// Other requirement generic parameters map 1:1 with their depth | ||
// increased appropriately. | ||
auto *genericParam = cast<GenericTypeParamType>(type); | ||
// In a protocol requirement, the only generic parameter at depth 0 | ||
// should be 'Self', and all others at depth 1. Anything else is | ||
// invalid code. | ||
if (genericParam->getDepth() != 1) | ||
return Type(); | ||
auto substGenericParam = | ||
GenericTypeParamType::get(depth, genericParam->getIndex(), ctx); | ||
return substGenericParam; | ||
}, | ||
[selfType, substConcreteType, conformance, conformanceDC, &ctx]( | ||
CanType type, Type replacement, ProtocolType *protoType) | ||
-> Optional<ProtocolConformanceRef> { | ||
auto proto = protoType->getDecl(); | ||
|
||
// The protocol 'Self' conforms concretely to the conforming type. | ||
if (type->isEqual(selfType)) { | ||
ProtocolConformance *specialized = conformance; | ||
if (conformance && conformance->getGenericSignature()) { | ||
auto concreteSubs = | ||
substConcreteType->getContextSubstitutionMap( | ||
conformanceDC->getParentModule(), conformanceDC); | ||
specialized = | ||
ctx.getSpecializedConformance(substConcreteType, conformance, | ||
concreteSubs); | ||
} | ||
|
||
if (specialized) | ||
return ProtocolConformanceRef(specialized); | ||
} | ||
|
||
// All other generic parameters come from the requirement itself | ||
// and conform abstractly. | ||
return ProtocolConformanceRef(proto); | ||
}); | ||
|
||
// If the requirement itself is non-generic, the synthetic signature | ||
// is that of the conformance context. | ||
if (!covariantSelf && | ||
reqSig->getGenericParams().size() == 1 && | ||
reqSig->getRequirements().size() == 1) { | ||
syntheticSignature = conformanceDC->getGenericSignatureOfContext(); | ||
if (syntheticSignature) { | ||
syntheticSignature = syntheticSignature->getCanonicalSignature(); | ||
syntheticEnvironment = | ||
syntheticSignature->createGenericEnvironment(); | ||
} | ||
|
||
return; | ||
} | ||
|
||
// Construct a generic signature builder by collecting the constraints | ||
// from the requirement and the context of the conformance together, | ||
// because both define the capabilities of the requirement. | ||
GenericSignatureBuilder builder(ctx); | ||
|
||
auto source = | ||
GenericSignatureBuilder::FloatingRequirementSource::forAbstract(); | ||
|
||
// If the conforming type is a class, add a class-constrained 'Self' | ||
// parameter. | ||
if (covariantSelf) { | ||
auto paramTy = GenericTypeParamType::get(/*depth=*/0, /*index=*/0, ctx); | ||
builder.addGenericParameter(paramTy); | ||
} | ||
|
||
// Now, add all generic parameters from the conforming type. | ||
if (conformanceSig) { | ||
for (auto param : conformanceSig->getGenericParams()) { | ||
builder.addGenericParameter( | ||
Type(param).subst(conformanceToSyntheticEnvMap) | ||
->castTo<GenericTypeParamType>()); | ||
} | ||
} | ||
|
||
// Next, add requirements. | ||
if (covariantSelf) { | ||
auto paramTy = GenericTypeParamType::get(/*depth=*/0, /*index=*/0, ctx); | ||
Requirement reqt(RequirementKind::Superclass, paramTy, substConcreteType); | ||
builder.addRequirement(reqt, source, nullptr); | ||
} | ||
|
||
if (conformanceSig) { | ||
for (auto &rawReq : conformanceSig->getRequirements()) { | ||
if (auto req = rawReq.subst(conformanceToSyntheticEnvMap)) | ||
builder.addRequirement(*req, source, nullptr); | ||
} | ||
} | ||
|
||
// Finally, add the generic parameters from the requirement. | ||
for (auto genericParam : reqSig->getGenericParams().slice(1)) { | ||
// The only depth that makes sense is depth == 1, the generic parameters | ||
// of the requirement itself. Anything else is from invalid code. | ||
if (genericParam->getDepth() != 1) { | ||
return; | ||
} | ||
|
||
// Create an equivalent generic parameter at the next depth. | ||
auto substGenericParam = | ||
GenericTypeParamType::get(depth, genericParam->getIndex(), ctx); | ||
|
||
builder.addGenericParameter(substGenericParam); | ||
} | ||
|
||
++NumRequirementEnvironments; | ||
|
||
// Next, add each of the requirements (mapped from the requirement's | ||
// interface types into the abstract type parameters). | ||
for (auto &rawReq : reqSig->getRequirements()) { | ||
// FIXME: This should not be necessary, since the constraint is redundant, | ||
// but we need it to work around some crashes for now. | ||
if (rawReq.getKind() == RequirementKind::Conformance && | ||
rawReq.getFirstType()->isEqual(selfType) && | ||
rawReq.getSecondType()->isEqual(proto->getDeclaredType())) | ||
continue; | ||
|
||
if (auto req = rawReq.subst(reqToSyntheticEnvMap)) | ||
builder.addRequirement(*req, source, conformanceDC->getParentModule()); | ||
} | ||
|
||
// Produce the generic signature and environment. | ||
// FIXME: Pass in a source location for the conformance, perhaps? It seems | ||
// like this could fail. | ||
syntheticSignature = | ||
std::move(builder).computeGenericSignature(SourceLoc()); | ||
syntheticEnvironment = syntheticSignature->createGenericEnvironment(); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrong description here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Drat, okay. Thanks!