mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
1068 lines
40 KiB
C++
1068 lines
40 KiB
C++
//===--- RequirementMachineRequests.cpp - Request evaluator requests ------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 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 main entry points for computing minimized generic
|
|
// signatures using the requirement machine via the request evaluator.
|
|
//
|
|
// There are three requests:
|
|
//
|
|
// - RequirementSignatureRequest computes protocol requirement signatures from
|
|
// user-written requirements.
|
|
// - AbstractGenericSignatureRequest computes minimal generic signatures from a
|
|
// set of abstract Requirements.
|
|
// - InferredGenericSignatureRequest computes minimal generic signatures from a
|
|
// set of user-written requirements on a parsed generic declaration.
|
|
//
|
|
// Each request begins by constructing some desugared requirements using the
|
|
// entry points in RequirementLowering.cpp.
|
|
//
|
|
// The desugared requirements are fed into a new requirement machine instance,
|
|
// which is then asked to produce a minimal set of rewrite rules. These rules
|
|
// are converted into minimal canonical Requirements using the entry points in
|
|
// RuleBuilder.cpp.
|
|
//
|
|
// The actual logic for finding a minimal set of rewrite rules is implemented in
|
|
// HomotopyReduction.cpp and MinimalConformances.cpp.
|
|
//
|
|
// Routines for constructing Requirements from Rules are implemented in
|
|
// RequirementBuilder.cpp.
|
|
//
|
|
// This process is actually iterated to implement "concrete equivalence class
|
|
// splitting", a compatibility behavior to produce the same results as the
|
|
// GenericSignatureBuilder in certain esoteric edge cases:
|
|
//
|
|
// ------------------------
|
|
// / Desugared Requirement /
|
|
// ------------------------
|
|
// |
|
|
// | +---------------------+
|
|
// | | |
|
|
// v v |
|
|
// +-------------+ |
|
|
// | RuleBuilder | |
|
|
// +-------------+ |
|
|
// | |
|
|
// v |
|
|
// +--------------+ |
|
|
// | Minimization | |
|
|
// +--------------+ |
|
|
// | |
|
|
// v |
|
|
// +--------------------+ |
|
|
// | RequirementBuilder | |
|
|
// +--------------------+ |
|
|
// | |
|
|
// v |
|
|
// -------------- |
|
|
// / Requirement / |
|
|
// -------------- |
|
|
// | |
|
|
// v |
|
|
// +------------------------------------+ |
|
|
// | Split concrete equivalence classes | ----+
|
|
// +------------------------------------+
|
|
// |
|
|
// v
|
|
// --------------
|
|
// / Requirement /
|
|
// --------------
|
|
//
|
|
// This transformation is described in splitConcreteEquivalenceClasses() below.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "RequirementMachine.h"
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/AST/DiagnosticsSema.h"
|
|
#include "swift/AST/GenericSignature.h"
|
|
#include "swift/AST/LazyResolver.h"
|
|
#include "swift/AST/Requirement.h"
|
|
#include "swift/AST/RequirementSignature.h"
|
|
#include "swift/AST/TypeCheckRequests.h"
|
|
#include "swift/AST/TypeRepr.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/Basic/Defer.h"
|
|
#include "swift/Basic/Statistic.h"
|
|
#include <memory>
|
|
#include <vector>
|
|
#include "RequirementLowering.h"
|
|
|
|
using namespace swift;
|
|
using namespace rewriting;
|
|
|
|
/// Hack for GenericSignatureBuilder compatibility. We might end up with a
|
|
/// same-type requirement between type parameters where one of them has an
|
|
/// implied concrete type requirement. In this case, split it up into two
|
|
/// concrete type requirements.
|
|
static bool shouldSplitConcreteEquivalenceClass(
|
|
Requirement req,
|
|
const ProtocolDecl *proto,
|
|
const RequirementMachine *machine) {
|
|
return (req.getKind() == RequirementKind::SameType &&
|
|
req.getSecondType()->isTypeParameter() &&
|
|
machine->isConcreteType(req.getSecondType(), proto));
|
|
}
|
|
|
|
/// Returns true if this generic signature contains abstract same-type
|
|
/// requirements between concrete type parameters. In this case, we split
|
|
/// the abstract same-type requirements into pairs of concrete type
|
|
/// requirements, and minimize the signature again.
|
|
static bool shouldSplitConcreteEquivalenceClasses(
|
|
ArrayRef<Requirement> requirements,
|
|
const ProtocolDecl *proto,
|
|
const RequirementMachine *machine) {
|
|
for (auto req : requirements) {
|
|
if (shouldSplitConcreteEquivalenceClass(req, proto, machine))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Same as the above, but with the requirements of a protocol connected
|
|
/// component.
|
|
static bool shouldSplitConcreteEquivalenceClasses(
|
|
const llvm::DenseMap<const ProtocolDecl *, RequirementSignature> &protos,
|
|
const RequirementMachine *machine) {
|
|
for (const auto &pair : protos) {
|
|
if (shouldSplitConcreteEquivalenceClasses(pair.second.getRequirements(),
|
|
pair.first, machine))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// Replace each same-type requirement 'T == U' where 'T' (and therefore 'U')
|
|
/// is known to equal a concrete type 'C' with a pair of requirements
|
|
/// 'T == C' and 'U == C'. We build the signature again in this case, since
|
|
/// one of the two requirements will be redundant, but we don't know which
|
|
/// ahead of time.
|
|
static void splitConcreteEquivalenceClasses(
|
|
ASTContext &ctx,
|
|
ArrayRef<Requirement> requirements,
|
|
const ProtocolDecl *proto,
|
|
const RequirementMachine *machine,
|
|
ArrayRef<GenericTypeParamType *> genericParams,
|
|
SmallVectorImpl<StructuralRequirement> &splitRequirements,
|
|
unsigned &attempt) {
|
|
bool debug = machine->getDebugOptions().contains(
|
|
DebugFlags::SplitConcreteEquivalenceClass);
|
|
|
|
unsigned maxAttempts =
|
|
ctx.LangOpts.RequirementMachineMaxSplitConcreteEquivClassAttempts;
|
|
|
|
if (attempt >= maxAttempts) {
|
|
ABORT([&](auto &out) {
|
|
out << "Splitting concrete equivalence classes did not "
|
|
<< "reach fixed point after " << attempt << " attempts.\n";
|
|
out << "Last attempt produced these requirements:\n";
|
|
for (auto req : requirements) {
|
|
req.dump(out);
|
|
out << "\n";
|
|
}
|
|
machine->dump(out);
|
|
});
|
|
}
|
|
|
|
splitRequirements.clear();
|
|
|
|
if (debug) {
|
|
llvm::dbgs() << "\n# Splitting concrete equivalence classes:\n";
|
|
}
|
|
|
|
for (auto req : requirements) {
|
|
if (shouldSplitConcreteEquivalenceClass(req, proto, machine)) {
|
|
auto concreteType = machine->getConcreteType(
|
|
req.getSecondType(), genericParams, proto);
|
|
|
|
Requirement firstReq(RequirementKind::SameType,
|
|
req.getFirstType(), concreteType);
|
|
Requirement secondReq(RequirementKind::SameType,
|
|
req.getSecondType(), concreteType);
|
|
splitRequirements.push_back({firstReq, SourceLoc()});
|
|
splitRequirements.push_back({secondReq, SourceLoc()});
|
|
|
|
if (debug) {
|
|
llvm::dbgs() << "- First split: ";
|
|
firstReq.dump(llvm::dbgs());
|
|
llvm::dbgs() << "\n- Second split: ";
|
|
secondReq.dump(llvm::dbgs());
|
|
llvm::dbgs() << "\n";
|
|
}
|
|
continue;
|
|
}
|
|
|
|
splitRequirements.push_back({req, SourceLoc()});
|
|
|
|
if (debug) {
|
|
llvm::dbgs() << "- Not split: ";
|
|
req.dump(llvm::dbgs());
|
|
llvm::dbgs() << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Same as the above, but with the requirements of a protocol connected
|
|
/// component.
|
|
static void splitConcreteEquivalenceClasses(
|
|
ASTContext &ctx,
|
|
const llvm::DenseMap<const ProtocolDecl *, RequirementSignature> &protos,
|
|
const RequirementMachine *machine,
|
|
llvm::DenseMap<const ProtocolDecl *,
|
|
SmallVector<StructuralRequirement, 4>> &splitProtos,
|
|
unsigned &attempt) {
|
|
for (const auto &pair : protos) {
|
|
const auto *proto = pair.first;
|
|
auto genericParams = proto->getGenericSignature().getGenericParams();
|
|
splitConcreteEquivalenceClasses(ctx, pair.second.getRequirements(),
|
|
proto, machine, genericParams,
|
|
splitProtos[proto],
|
|
attempt);
|
|
}
|
|
}
|
|
|
|
/// Builds the requirement signatures for each protocol in this strongly
|
|
/// connected component.
|
|
llvm::DenseMap<const ProtocolDecl *, RequirementSignature>
|
|
RequirementMachine::computeMinimalProtocolRequirements() {
|
|
auto protos = System.getProtocols();
|
|
|
|
ASSERT(protos.size() > 0 &&
|
|
"Not a protocol connected component rewrite system");
|
|
|
|
System.minimizeRewriteSystem(Map);
|
|
|
|
if (Dump) {
|
|
llvm::dbgs() << "Minimized rewrite system:\n";
|
|
dump(llvm::dbgs());
|
|
}
|
|
|
|
auto rules = System.getMinimizedProtocolRules();
|
|
|
|
auto &ctx = Context.getASTContext();
|
|
|
|
// Note that we build 'result' by iterating over 'protos' rather than
|
|
// 'rules'; this is intentional, so that even if a protocol has no
|
|
// rules, we still end up creating an entry for it in 'result'.
|
|
llvm::DenseMap<const ProtocolDecl *, RequirementSignature> result;
|
|
for (const auto *proto : protos) {
|
|
auto genericParams = proto->getGenericSignature().getGenericParams();
|
|
|
|
const auto &entry = rules[proto];
|
|
|
|
std::vector<Requirement> reqs;
|
|
std::vector<ProtocolTypeAlias> aliases;
|
|
buildRequirementsFromRules(entry.Requirements,
|
|
entry.TypeAliases,
|
|
genericParams,
|
|
/*reconstituteSugar=*/true,
|
|
reqs, aliases);
|
|
|
|
result[proto] = RequirementSignature(ctx.AllocateCopy(reqs),
|
|
ctx.AllocateCopy(aliases),
|
|
getErrors());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
RequirementSignature
|
|
RequirementSignatureRequest::evaluate(Evaluator &evaluator,
|
|
ProtocolDecl *proto) const {
|
|
ASTContext &ctx = proto->getASTContext();
|
|
|
|
// First check if we have a deserializable requirement signature.
|
|
if (proto->hasLazyRequirementSignature()) {
|
|
// FIXME: (transitional) increment the redundant "always-on" counter.
|
|
if (ctx.Stats)
|
|
++ctx.Stats->getFrontendCounters().NumLazyRequirementSignaturesLoaded;
|
|
|
|
auto contextData = static_cast<LazyProtocolData *>(
|
|
ctx.getOrCreateLazyContextData(proto, nullptr));
|
|
|
|
SmallVector<Requirement, 2> requirements;
|
|
SmallVector<ProtocolTypeAlias, 2> typeAliases;
|
|
contextData->loader->loadRequirementSignature(
|
|
proto, contextData->requirementSignatureData,
|
|
requirements, typeAliases);
|
|
return RequirementSignature(ctx.AllocateCopy(requirements),
|
|
ctx.AllocateCopy(typeAliases));
|
|
}
|
|
|
|
auto &rewriteCtx = ctx.getRewriteContext();
|
|
|
|
// We build requirement signatures for all protocols in a strongly connected
|
|
// component at the same time.
|
|
auto component = rewriteCtx.startComputingRequirementSignatures(proto);
|
|
|
|
SWIFT_DEFER {
|
|
rewriteCtx.finishComputingRequirementSignatures(proto);
|
|
};
|
|
|
|
SmallVector<RequirementError, 4> errors;
|
|
|
|
// Collect user-written requirements from the protocols in this connected
|
|
// component.
|
|
llvm::DenseMap<const ProtocolDecl *,
|
|
SmallVector<StructuralRequirement, 4>> protos;
|
|
for (const auto *proto : component) {
|
|
auto &requirements = protos[proto];
|
|
for (auto req : proto->getStructuralRequirements())
|
|
requirements.push_back(req);
|
|
for (auto req : proto->getTypeAliasRequirements())
|
|
requirements.push_back({req, SourceLoc()});
|
|
}
|
|
|
|
if (rewriteCtx.getDebugOptions().contains(DebugFlags::Timers)) {
|
|
rewriteCtx.beginTimer("RequirementSignatureRequest");
|
|
llvm::dbgs() << "[";
|
|
for (auto *proto : component)
|
|
llvm::dbgs() << " " << proto->getName();
|
|
llvm::dbgs() << " ]\n";
|
|
}
|
|
|
|
SWIFT_DEFER {
|
|
if (rewriteCtx.getDebugOptions().contains(DebugFlags::Timers)) {
|
|
rewriteCtx.endTimer("RequirementSignatureRequest");
|
|
llvm::dbgs() << "[";
|
|
for (auto *proto : component)
|
|
llvm::dbgs() << " " << proto->getName();
|
|
llvm::dbgs() << " ]\n";
|
|
}
|
|
};
|
|
|
|
unsigned attempt = 0;
|
|
for (;;) {
|
|
for (const auto *otherProto : component) {
|
|
auto &requirements = protos[otherProto];
|
|
|
|
// Preprocess requirements to eliminate conformances on type parameters
|
|
// which are made concrete.
|
|
if (ctx.LangOpts.EnableRequirementMachineConcreteContraction) {
|
|
SmallVector<StructuralRequirement, 4> contractedRequirements;
|
|
|
|
bool debug = rewriteCtx.getDebugOptions()
|
|
.contains(DebugFlags::ConcreteContraction);
|
|
|
|
if (performConcreteContraction(requirements, contractedRequirements,
|
|
errors, debug)) {
|
|
std::swap(contractedRequirements, requirements);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Heap-allocate the requirement machine to save stack space.
|
|
std::unique_ptr<RequirementMachine> machine(new RequirementMachine(
|
|
rewriteCtx));
|
|
|
|
auto status = machine->initWithProtocolWrittenRequirements(component, protos);
|
|
|
|
// If completion failed, diagnose an error and return a dummy signature.
|
|
if (status.first != CompletionResult::Success) {
|
|
// All we can do at this point is diagnose and give each protocol an empty
|
|
// requirement signature.
|
|
for (const auto *otherProto : component) {
|
|
ctx.Diags.diagnose(otherProto->getLoc(),
|
|
diag::requirement_machine_completion_failed,
|
|
/*protocol=*/1,
|
|
unsigned(status.first));
|
|
|
|
auto rule = machine->getRuleAsStringForDiagnostics(status.second);
|
|
ctx.Diags.diagnose(otherProto->getLoc(),
|
|
diag::requirement_machine_completion_rule,
|
|
rule);
|
|
|
|
if (otherProto != proto) {
|
|
ctx.evaluator.cacheOutput(
|
|
RequirementSignatureRequest{const_cast<ProtocolDecl *>(otherProto)},
|
|
RequirementSignature::getPlaceholderRequirementSignature(
|
|
otherProto, GenericSignatureErrorFlags::CompletionFailed));
|
|
}
|
|
}
|
|
|
|
return RequirementSignature::getPlaceholderRequirementSignature(
|
|
proto, GenericSignatureErrorFlags::CompletionFailed);
|
|
}
|
|
|
|
auto minimalRequirements = machine->computeMinimalProtocolRequirements();
|
|
|
|
// Don't bother splitting concrete equivalence classes if there were invalid
|
|
// requirements, because the signature is not going to be ABI anyway.
|
|
if (!machine->getErrors().contains(
|
|
GenericSignatureErrorFlags::HasInvalidRequirements)) {
|
|
if (shouldSplitConcreteEquivalenceClasses(minimalRequirements, machine.get())) {
|
|
++attempt;
|
|
splitConcreteEquivalenceClasses(ctx, minimalRequirements,
|
|
machine.get(), protos, attempt);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
bool debug = machine->getDebugOptions().contains(DebugFlags::Minimization);
|
|
|
|
// The requirement signature for the actual protocol that the result
|
|
// was kicked off with.
|
|
std::optional<RequirementSignature> result;
|
|
|
|
if (debug) {
|
|
llvm::dbgs() << "\nRequirement signatures:\n";
|
|
}
|
|
|
|
// Cache the requirement signatures for all other protocols in this
|
|
// connected component.
|
|
for (const auto &pair : minimalRequirements) {
|
|
auto *otherProto = pair.first;
|
|
const auto &reqs = pair.second;
|
|
|
|
// Dump the result if requested.
|
|
if (debug) {
|
|
llvm::dbgs() << "- Protocol " << otherProto->getName() << ": ";
|
|
|
|
auto sig = GenericSignature::get(
|
|
otherProto->getGenericSignature().getGenericParams(),
|
|
reqs.getRequirements());
|
|
|
|
PrintOptions opts;
|
|
opts.ProtocolQualifiedDependentMemberTypes = true;
|
|
sig.print(llvm::dbgs(), opts);
|
|
llvm::dbgs() << "\n";
|
|
}
|
|
|
|
// Don't call setRequirementSignature() on the original proto; the
|
|
// request evaluator will do it for us.
|
|
if (otherProto == proto)
|
|
result = reqs;
|
|
else {
|
|
auto temp = reqs;
|
|
ctx.evaluator.cacheOutput(
|
|
RequirementSignatureRequest{const_cast<ProtocolDecl *>(otherProto)},
|
|
std::move(temp));
|
|
}
|
|
}
|
|
|
|
// FIXME: We don't have the inverses from desugaring available here!
|
|
SmallVector<InverseRequirement, 2> missingInverses;
|
|
|
|
// Diagnose redundant requirements and conflicting requirements.
|
|
machine->computeRequirementDiagnostics(errors, missingInverses,
|
|
proto->getLoc());
|
|
diagnoseRequirementErrors(ctx, errors,
|
|
AllowConcreteTypePolicy::NestedAssocTypes);
|
|
|
|
for (auto *protocol : machine->System.getProtocols()) {
|
|
auto selfType = protocol->getSelfInterfaceType();
|
|
auto concrete = machine->getConcreteType(selfType,
|
|
machine->getGenericParams(),
|
|
protocol);
|
|
if (!concrete || concrete->hasError())
|
|
continue;
|
|
|
|
protocol->diagnose(diag::requires_generic_param_made_equal_to_concrete,
|
|
selfType);
|
|
}
|
|
|
|
if (!machine->getErrors()) {
|
|
// If this signature was minimized without errors or non-redundant
|
|
// concrete conformances, we can re-use the requirement machine for
|
|
// subsequent queries, instead of building a new requirement machine
|
|
// from the minimized signature.
|
|
rewriteCtx.installRequirementMachine(proto, std::move(machine));
|
|
}
|
|
|
|
// Return the result for the specific protocol this request was kicked off on.
|
|
return *result;
|
|
}
|
|
}
|
|
|
|
/// Builds the top-level generic signature requirements for this rewrite system.
|
|
GenericSignature
|
|
RequirementMachine::computeMinimalGenericSignature(
|
|
bool reconstituteSugar) {
|
|
ASSERT(!Sig &&
|
|
"Already computed minimal generic signature");
|
|
ASSERT(System.getProtocols().empty() &&
|
|
"Not a top-level generic signature rewrite system");
|
|
ASSERT(!Params.empty() &&
|
|
"Not a from-source top-level generic signature rewrite system");
|
|
|
|
System.minimizeRewriteSystem(Map);
|
|
|
|
if (Dump) {
|
|
llvm::dbgs() << "Minimized rewrite system:\n";
|
|
dump(llvm::dbgs());
|
|
}
|
|
|
|
auto rules = System.getMinimizedGenericSignatureRules();
|
|
|
|
std::vector<Requirement> reqs;
|
|
std::vector<ProtocolTypeAlias> aliases;
|
|
|
|
buildRequirementsFromRules(rules, ArrayRef<unsigned>(), getGenericParams(),
|
|
reconstituteSugar, reqs, aliases);
|
|
ASSERT(aliases.empty());
|
|
|
|
auto sig = GenericSignature::get(getGenericParams(), reqs);
|
|
|
|
// Remember the signature for generic signature queries. In particular,
|
|
// getConformancePath() needs the current requirement machine's
|
|
// generic signature.
|
|
Sig = sig.getCanonicalSignature();
|
|
|
|
return sig;
|
|
}
|
|
|
|
/// Check whether the inputs to the \c AbstractGenericSignatureRequest are
|
|
/// all canonical.
|
|
static bool isCanonicalRequest(GenericSignature baseSignature,
|
|
ArrayRef<GenericTypeParamType *> genericParams,
|
|
ArrayRef<Requirement> requirements) {
|
|
if (baseSignature && !baseSignature->isCanonical())
|
|
return false;
|
|
|
|
for (auto gp : genericParams) {
|
|
if (!gp->isCanonical())
|
|
return false;
|
|
}
|
|
|
|
for (const auto &req : requirements) {
|
|
if (!req.isCanonical())
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
GenericSignatureWithError
|
|
AbstractGenericSignatureRequest::evaluate(
|
|
Evaluator &evaluator,
|
|
const GenericSignatureImpl *baseSignatureImpl,
|
|
SmallVector<GenericTypeParamType *, 2> addedParameters,
|
|
SmallVector<Requirement, 2> addedRequirements,
|
|
bool allowInverses) const {
|
|
GenericSignature baseSignature = GenericSignature{baseSignatureImpl};
|
|
// If nothing is added to the base signature, just return the base
|
|
// signature.
|
|
if (addedParameters.empty() && addedRequirements.empty())
|
|
return GenericSignatureWithError(baseSignature, GenericSignatureErrors());
|
|
|
|
ASTContext &ctx = addedParameters.empty()
|
|
? addedRequirements.front().getFirstType()->getASTContext()
|
|
: addedParameters.front()->getASTContext();
|
|
|
|
SmallVector<GenericTypeParamType *, 4> genericParams(
|
|
baseSignature.getGenericParams().begin(),
|
|
baseSignature.getGenericParams().end());
|
|
genericParams.append(
|
|
addedParameters.begin(),
|
|
addedParameters.end());
|
|
|
|
// If there are no added requirements, we can form the signature directly
|
|
// with the added parameters.
|
|
if (addedRequirements.empty() && !allowInverses) {
|
|
auto result = GenericSignature::get(genericParams,
|
|
baseSignature.getRequirements());
|
|
return GenericSignatureWithError(result, GenericSignatureErrors());
|
|
}
|
|
|
|
// If the request is non-canonical, we won't need to build our own
|
|
// generic signature builder.
|
|
if (!isCanonicalRequest(baseSignature, addedParameters, addedRequirements)) {
|
|
// Canonicalize the inputs so we can form the canonical request.
|
|
auto canBaseSignature = baseSignature.getCanonicalSignature();
|
|
|
|
SmallVector<GenericTypeParamType *, 2> canAddedParameters;
|
|
canAddedParameters.reserve(addedParameters.size());
|
|
for (auto gp : addedParameters) {
|
|
auto canGP = gp->getCanonicalType()->castTo<GenericTypeParamType>();
|
|
canAddedParameters.push_back(canGP);
|
|
}
|
|
|
|
SmallVector<Requirement, 2> canAddedRequirements;
|
|
canAddedRequirements.reserve(addedRequirements.size());
|
|
for (const auto &req : addedRequirements) {
|
|
canAddedRequirements.push_back(req.getCanonical());
|
|
}
|
|
|
|
// Build the canonical signature.
|
|
auto canSignatureResult = evaluateOrDefault(
|
|
ctx.evaluator,
|
|
AbstractGenericSignatureRequest{
|
|
canBaseSignature.getPointer(), std::move(canAddedParameters),
|
|
std::move(canAddedRequirements),
|
|
allowInverses},
|
|
GenericSignatureWithError());
|
|
if (!canSignatureResult.getPointer())
|
|
return GenericSignatureWithError();
|
|
|
|
// Substitute in the original generic parameters to form the sugared
|
|
// result the original request wanted.
|
|
auto canSignature = canSignatureResult.getPointer();
|
|
SmallVector<GenericTypeParamType *, 2> resugaredParameters;
|
|
resugaredParameters.reserve(canSignature.getGenericParams().size());
|
|
if (baseSignature) {
|
|
resugaredParameters.append(baseSignature.getGenericParams().begin(),
|
|
baseSignature.getGenericParams().end());
|
|
}
|
|
resugaredParameters.append(addedParameters.begin(), addedParameters.end());
|
|
ASSERT(resugaredParameters.size() ==
|
|
canSignature.getGenericParams().size());
|
|
|
|
SmallVector<Requirement, 2> resugaredRequirements;
|
|
resugaredRequirements.reserve(canSignature.getRequirements().size());
|
|
for (const auto &req : canSignature.getRequirements()) {
|
|
auto resugaredReq = req.subst(
|
|
[&](SubstitutableType *type) {
|
|
if (auto gp = dyn_cast<GenericTypeParamType>(type)) {
|
|
unsigned ordinal = canSignature->getGenericParamOrdinal(gp);
|
|
return Type(resugaredParameters[ordinal]);
|
|
}
|
|
return Type(type);
|
|
},
|
|
LookUpConformanceInModule(),
|
|
SubstFlags::PreservePackExpansionLevel);
|
|
resugaredRequirements.push_back(resugaredReq);
|
|
}
|
|
|
|
return GenericSignatureWithError(
|
|
GenericSignature::get(resugaredParameters, resugaredRequirements),
|
|
canSignatureResult.getInt());
|
|
}
|
|
|
|
// Convert the input Requirements into StructuralRequirements by adding
|
|
// empty source locations.
|
|
SmallVector<StructuralRequirement, 2> requirements;
|
|
for (auto req : baseSignature.getRequirements())
|
|
requirements.push_back({req, SourceLoc()});
|
|
|
|
// Add the new requirements.
|
|
for (auto req : addedRequirements)
|
|
requirements.push_back({req, SourceLoc()});
|
|
|
|
// The requirements passed to this request may have been substituted,
|
|
// meaning the subject type might be a concrete type and not a type
|
|
// parameter.
|
|
//
|
|
// Also, the right hand side of conformance requirements here might be
|
|
// a protocol composition.
|
|
//
|
|
// Desugaring converts these kinds of requirements into "proper"
|
|
// requirements where the subject type is always a type parameter,
|
|
// which is what the RuleBuilder expects.
|
|
SmallVector<RequirementError, 2> errors;
|
|
SmallVector<InverseRequirement, 2> inverses;
|
|
desugarRequirements(requirements, inverses, errors);
|
|
|
|
/// Next, we need to expand default requirements and then apply inverses.
|
|
SmallVector<Type, 2> paramsAsTypes;
|
|
if (allowInverses) {
|
|
for (auto *gtpt : addedParameters)
|
|
paramsAsTypes.push_back(gtpt);
|
|
}
|
|
|
|
SmallVector<StructuralRequirement, 2> defaults;
|
|
InverseRequirement::expandDefaults(ctx, paramsAsTypes, defaults);
|
|
applyInverses(ctx, paramsAsTypes, inverses, requirements,
|
|
defaults, errors);
|
|
requirements.append(defaults);
|
|
|
|
auto &rewriteCtx = ctx.getRewriteContext();
|
|
|
|
if (rewriteCtx.getDebugOptions().contains(DebugFlags::Timers)) {
|
|
rewriteCtx.beginTimer("AbstractGenericSignatureRequest");
|
|
llvm::dbgs() << "\n";
|
|
}
|
|
|
|
unsigned attempt = 0;
|
|
for (;;) {
|
|
// Preprocess requirements to eliminate conformances on generic parameters
|
|
// which are made concrete.
|
|
if (ctx.LangOpts.EnableRequirementMachineConcreteContraction) {
|
|
SmallVector<StructuralRequirement, 4> contractedRequirements;
|
|
bool debug = rewriteCtx.getDebugOptions()
|
|
.contains(DebugFlags::ConcreteContraction);
|
|
if (performConcreteContraction(requirements, contractedRequirements,
|
|
errors, debug)) {
|
|
std::swap(contractedRequirements, requirements);
|
|
}
|
|
}
|
|
|
|
// Heap-allocate the requirement machine to save stack space.
|
|
std::unique_ptr<RequirementMachine> machine(new RequirementMachine(
|
|
rewriteCtx));
|
|
|
|
auto status =
|
|
machine->initWithWrittenRequirements(genericParams, requirements);
|
|
machine->checkCompletionResult(status.first);
|
|
|
|
// We pass reconstituteSugar=false to ensure that if the original
|
|
// requirements were canonical, the final signature remains canonical.
|
|
auto result = machine->computeMinimalGenericSignature(
|
|
/*reconstituteSugar=*/false);
|
|
auto errorFlags = machine->getErrors();
|
|
|
|
// Don't bother splitting concrete equivalence classes if there were invalid
|
|
// requirements, because the signature is not going to be ABI anyway.
|
|
if (!errorFlags.contains(GenericSignatureErrorFlags::HasInvalidRequirements)) {
|
|
if (shouldSplitConcreteEquivalenceClasses(result.getRequirements(),
|
|
/*proto=*/nullptr,
|
|
machine.get())) {
|
|
++attempt;
|
|
splitConcreteEquivalenceClasses(ctx, result.getRequirements(),
|
|
/*proto=*/nullptr, machine.get(),
|
|
result.getGenericParams(),
|
|
requirements, attempt);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!errorFlags) {
|
|
// If this signature was minimized without errors or non-redundant
|
|
// concrete conformances, we can re-use the requirement machine for
|
|
// subsequent queries, instead of building a new requirement machine
|
|
// from the minimized signature. Do this before verify(), which
|
|
// performs queries.
|
|
rewriteCtx.installRequirementMachine(result.getCanonicalSignature(),
|
|
std::move(machine));
|
|
}
|
|
|
|
if (!errorFlags.contains(GenericSignatureErrorFlags::HasInvalidRequirements)) {
|
|
// Check invariants.
|
|
result.verify();
|
|
}
|
|
|
|
if (rewriteCtx.getDebugOptions().contains(DebugFlags::Timers)) {
|
|
rewriteCtx.endTimer("AbstractGenericSignatureRequest");
|
|
llvm::dbgs() << result << "\n";
|
|
}
|
|
|
|
return GenericSignatureWithError(result, errorFlags);
|
|
}
|
|
}
|
|
|
|
GenericSignatureWithError
|
|
InferredGenericSignatureRequest::evaluate(
|
|
Evaluator &evaluator,
|
|
const GenericSignatureImpl *parentSigImpl,
|
|
GenericParamList *genericParamList,
|
|
WhereClauseOwner whereClause,
|
|
SmallVector<Requirement, 2> addedRequirements,
|
|
SmallVector<TypeBase *, 2> inferenceSources,
|
|
SourceLoc loc, ExtensionDecl *forExtension, bool allowInverses) const {
|
|
GenericSignature parentSig(parentSigImpl);
|
|
|
|
SmallVector<GenericTypeParamType *, 4> genericParams(
|
|
parentSig.getGenericParams().begin(),
|
|
parentSig.getGenericParams().end());
|
|
|
|
unsigned numOuterParams = genericParams.size();
|
|
if (forExtension) {
|
|
numOuterParams = 0;
|
|
}
|
|
|
|
SmallVector<StructuralRequirement, 2> requirements;
|
|
SmallVector<RequirementError, 2> errors;
|
|
SmallVector<InverseRequirement, 2> inverses;
|
|
|
|
for (const auto &req : parentSig.getRequirements())
|
|
requirements.push_back({req, loc});
|
|
|
|
DeclContext *lookupDC = nullptr;
|
|
|
|
const auto visitRequirement = [&](const Requirement &req,
|
|
RequirementRepr *reqRepr) {
|
|
realizeRequirement(lookupDC, req, reqRepr, /*inferRequirements=*/true,
|
|
requirements, errors);
|
|
return false;
|
|
};
|
|
|
|
if (genericParamList) {
|
|
// If we have multiple parameter lists, we're in SIL mode, and there's
|
|
// no parent signature from context.
|
|
ASSERT(genericParamList->getOuterParameters() == nullptr || !parentSig);
|
|
|
|
// Collect all outer generic parameter lists.
|
|
SmallVector<GenericParamList *, 2> gpLists;
|
|
for (auto *outerParamList = genericParamList;
|
|
outerParamList != nullptr;
|
|
outerParamList = outerParamList->getOuterParameters()) {
|
|
gpLists.push_back(outerParamList);
|
|
}
|
|
|
|
// The generic parameter lists must appear from innermost to outermost.
|
|
// We walk them backwards to order outer parameters before inner
|
|
// parameters.
|
|
for (auto *gpList : llvm::reverse(gpLists)) {
|
|
ASSERT(gpList->size() > 0 &&
|
|
"Parsed an empty generic parameter list?");
|
|
|
|
for (auto *gpDecl : *gpList) {
|
|
auto *gpType = gpDecl->getDeclaredInterfaceType()
|
|
->castTo<GenericTypeParamType>();
|
|
genericParams.push_back(gpType);
|
|
|
|
realizeInheritedRequirements(gpDecl, gpType,
|
|
/*inferRequirements=*/true,
|
|
requirements, errors);
|
|
}
|
|
|
|
lookupDC = (*gpList->begin())->getDeclContext();
|
|
|
|
// Add the generic parameter list's 'where' clause to the builder.
|
|
//
|
|
// The only time generic parameter lists have a 'where' clause is
|
|
// in SIL mode; all other generic declarations have a free-standing
|
|
// 'where' clause, which will be visited below.
|
|
WhereClauseOwner(lookupDC, gpList)
|
|
.visitRequirements(TypeResolutionStage::Structural,
|
|
visitRequirement);
|
|
}
|
|
}
|
|
|
|
// Realize all requirements in the free-standing 'where' clause, if there
|
|
// is one.
|
|
if (whereClause) {
|
|
lookupDC = whereClause.dc;
|
|
|
|
std::move(whereClause).visitRequirements(
|
|
TypeResolutionStage::Structural,
|
|
visitRequirement);
|
|
}
|
|
|
|
auto *moduleForInference = lookupDC->getParentModule();
|
|
auto &ctx = moduleForInference->getASTContext();
|
|
|
|
// Perform requirement inference from function parameter and result
|
|
// types and such.
|
|
for (auto source : inferenceSources) {
|
|
inferRequirements(source, moduleForInference, lookupDC, requirements);
|
|
}
|
|
|
|
// Finish by adding any remaining requirements. This is used to introduce
|
|
// inferred same-type requirements when building the generic signature of
|
|
// an extension whose extended type is a generic typealias.
|
|
for (const auto &req : addedRequirements)
|
|
requirements.push_back({req, loc});
|
|
|
|
desugarRequirements(requirements, inverses, errors);
|
|
|
|
// After realizing requirements, expand default requirements only for local
|
|
// generic parameters, as the outer parameters have already been expanded.
|
|
SmallVector<Type, 4> paramTypes;
|
|
if (allowInverses) {
|
|
paramTypes.append(genericParams.begin() + numOuterParams,
|
|
genericParams.end());
|
|
}
|
|
|
|
SmallVector<StructuralRequirement, 2> defaults;
|
|
InverseRequirement::expandDefaults(ctx, paramTypes, defaults);
|
|
applyInverses(ctx, paramTypes, inverses, requirements,
|
|
defaults, errors);
|
|
|
|
// Any remaining implicit defaults in a conditional inverse requirement
|
|
// extension must be made explicit.
|
|
if (forExtension) {
|
|
auto invertibleProtocol = forExtension->isAddingConformanceToInvertible();
|
|
// FIXME: to workaround a reverse condfail, always infer the requirements if
|
|
// the extension is in a swiftinterface file. This is temporary and should
|
|
// be removed soon. (rdar://130424971)
|
|
if (auto *sf = forExtension->getOutermostParentSourceFile()) {
|
|
if (sf->Kind == SourceFileKind::Interface
|
|
&& !ctx.LangOpts.hasFeature(Feature::SE427NoInferenceOnExtension)) {
|
|
invertibleProtocol = std::nullopt;
|
|
}
|
|
}
|
|
if (invertibleProtocol) {
|
|
for (auto &def : defaults) {
|
|
// Check whether a corresponding explicit requirement was provided.
|
|
for (auto &req : requirements) {
|
|
// An explicit requirement can match the default exactly.
|
|
if (req.req.getCanonical() == def.req.getCanonical()) {
|
|
goto next;
|
|
}
|
|
|
|
// Disregard requirements on other parameters.
|
|
if (!req.req.getFirstType()->isEqual(def.req.getFirstType())) {
|
|
continue;
|
|
}
|
|
|
|
// Or it can be implied by a requirement on something that's inherently
|
|
// copyable.
|
|
if (req.req.getKind() == RequirementKind::Superclass) {
|
|
// classes are currently always escapable and copyable
|
|
goto next;
|
|
}
|
|
if (req.req.getKind() == RequirementKind::Layout) {
|
|
// layout constraints currently always imply escapable and copyable
|
|
goto next;
|
|
}
|
|
if (req.req.getKind() == RequirementKind::Conformance
|
|
&& req.req.getProtocolDecl()
|
|
->inheritsFrom(def.req.getProtocolDecl())) {
|
|
goto next;
|
|
}
|
|
|
|
// A same-type constraint removes the ability for the copyability
|
|
// to vary independently at all.
|
|
if (req.req.getKind() == RequirementKind::SameType) {
|
|
goto next;
|
|
}
|
|
}
|
|
ctx.Diags.diagnose(loc,diag::inverse_conditional_must_be_fully_explicit,
|
|
ctx.getProtocol(getKnownProtocolKind(*invertibleProtocol)),
|
|
def.req.getFirstType(),
|
|
def.req.getProtocolDecl());
|
|
next:;
|
|
}
|
|
// Don't actually apply the inferred requirements since they should be
|
|
// stated explicitly.
|
|
defaults.clear();
|
|
}
|
|
}
|
|
|
|
requirements.append(defaults);
|
|
|
|
auto &rewriteCtx = ctx.getRewriteContext();
|
|
|
|
if (rewriteCtx.getDebugOptions().contains(DebugFlags::Timers)) {
|
|
rewriteCtx.beginTimer("InferredGenericSignatureRequest");
|
|
|
|
llvm::dbgs() << "@ ";
|
|
auto &sourceMgr = ctx.SourceMgr;
|
|
loc.print(llvm::dbgs(), sourceMgr);
|
|
llvm::dbgs() << "\n";
|
|
}
|
|
|
|
unsigned attempt = 0;
|
|
for (;;) {
|
|
// Preprocess requirements to eliminate conformances on generic parameters
|
|
// which are made concrete.
|
|
if (ctx.LangOpts.EnableRequirementMachineConcreteContraction) {
|
|
SmallVector<StructuralRequirement, 4> contractedRequirements;
|
|
bool debug = rewriteCtx.getDebugOptions()
|
|
.contains(DebugFlags::ConcreteContraction);
|
|
if (performConcreteContraction(requirements, contractedRequirements,
|
|
errors, debug)) {
|
|
std::swap(contractedRequirements, requirements);
|
|
}
|
|
}
|
|
|
|
// Heap-allocate the requirement machine to save stack space.
|
|
std::unique_ptr<RequirementMachine> machine(new RequirementMachine(
|
|
rewriteCtx));
|
|
|
|
auto status =
|
|
machine->initWithWrittenRequirements(genericParams, requirements);
|
|
|
|
// If completion failed, diagnose an error and return a dummy signature.
|
|
if (status.first != CompletionResult::Success) {
|
|
ctx.Diags.diagnose(loc,
|
|
diag::requirement_machine_completion_failed,
|
|
/*protocol=*/0,
|
|
unsigned(status.first));
|
|
|
|
auto rule = machine->getRuleAsStringForDiagnostics(status.second);
|
|
ctx.Diags.diagnose(loc,
|
|
diag::requirement_machine_completion_rule,
|
|
rule);
|
|
|
|
auto result = GenericSignature::forInvalid(genericParams);
|
|
|
|
if (rewriteCtx.getDebugOptions().contains(DebugFlags::Timers)) {
|
|
rewriteCtx.endTimer("InferredGenericSignatureRequest");
|
|
llvm::dbgs() << result << "\n";
|
|
}
|
|
|
|
return GenericSignatureWithError(
|
|
result, GenericSignatureErrorFlags::CompletionFailed);
|
|
}
|
|
|
|
auto result = machine->computeMinimalGenericSignature(
|
|
/*reconstituteSugar=*/true);
|
|
auto errorFlags = machine->getErrors();
|
|
|
|
// Diagnose redundant requirements and conflicting requirements.
|
|
if (attempt == 0) {
|
|
machine->computeRequirementDiagnostics(errors, inverses, loc);
|
|
diagnoseRequirementErrors(ctx, errors,
|
|
(forExtension || !genericParamList)
|
|
? AllowConcreteTypePolicy::All
|
|
: AllowConcreteTypePolicy::AssocTypes);
|
|
}
|
|
|
|
// Don't bother splitting concrete equivalence classes if there were invalid
|
|
// requirements, because the signature is not going to be ABI anyway.
|
|
if (!errorFlags.contains(GenericSignatureErrorFlags::HasInvalidRequirements)) {
|
|
// Check if we need to rebuild the signature.
|
|
if (shouldSplitConcreteEquivalenceClasses(result.getRequirements(),
|
|
/*proto=*/nullptr,
|
|
machine.get())) {
|
|
++attempt;
|
|
splitConcreteEquivalenceClasses(ctx, result.getRequirements(),
|
|
/*proto=*/nullptr, machine.get(),
|
|
result.getGenericParams(),
|
|
requirements, attempt);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!errorFlags) {
|
|
// If this signature was minimized without errors or non-redundant
|
|
// concrete conformances, we can re-use the requirement machine for
|
|
// subsequent queries, instead of building a new requirement machine
|
|
// from the minimized signature. Do this before verify(), which
|
|
// performs queries.
|
|
rewriteCtx.installRequirementMachine(result.getCanonicalSignature(),
|
|
std::move(machine));
|
|
}
|
|
|
|
if (genericParamList && !forExtension) {
|
|
for (auto genericParam : result.getInnermostGenericParams()) {
|
|
auto reduced = result.getReducedType(genericParam);
|
|
|
|
if (reduced->hasError() || reduced->isEqual(genericParam))
|
|
continue;
|
|
|
|
// If one side is a parameter pack and the other is not, this is a
|
|
// same-element requirement that cannot be expressed with only one
|
|
// type parameter.
|
|
if (genericParam->isParameterPack() != reduced->isParameterPack())
|
|
continue;
|
|
|
|
if (reduced->isTypeParameter()) {
|
|
ctx.Diags.diagnose(loc, diag::requires_generic_params_made_equal,
|
|
genericParam, result->getSugaredType(reduced))
|
|
.warnUntilSwiftVersion(6);
|
|
} else {
|
|
ctx.Diags.diagnose(loc,
|
|
diag::requires_generic_param_made_equal_to_concrete,
|
|
genericParam)
|
|
.warnUntilSwiftVersion(6);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!errorFlags.contains(GenericSignatureErrorFlags::HasInvalidRequirements)) {
|
|
// Check invariants.
|
|
result.verify();
|
|
}
|
|
|
|
if (rewriteCtx.getDebugOptions().contains(DebugFlags::Timers)) {
|
|
rewriteCtx.endTimer("InferredGenericSignatureRequest");
|
|
llvm::dbgs() << result << "\n";
|
|
}
|
|
|
|
return GenericSignatureWithError(result, errorFlags);
|
|
}
|
|
}
|