Skip to content

TypeChecker::conformsToProtocol() => ModuleDecl::checkConformance() #70937

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
merged 2 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 20 additions & 13 deletions include/swift/AST/Module.h
Original file line number Diff line number Diff line change
Expand Up @@ -823,10 +823,7 @@ class ModuleDecl
DeclName name,
SmallVectorImpl<ValueDecl*> &results) const;

/// Look for the conformance of the given type to the given protocol.
///
/// This routine determines whether the given \c type conforms to the given
/// \c protocol.
/// Global conformance lookup, does not check conditional requirements.
///
/// \param type The type for which we are computing conformance.
///
Expand All @@ -836,23 +833,33 @@ class ModuleDecl
/// might include "missing" conformances, which are synthesized for some
/// protocols as an error recovery mechanism.
///
/// \returns The result of the conformance search, which will be
/// None if the type does not conform to the protocol or contain a
/// ProtocolConformanceRef if it does conform.
/// \returns An invalid conformance if the search failed, otherwise an
/// abstract, concrete or pack conformance, depending on the lookup type.
ProtocolConformanceRef lookupConformance(Type type, ProtocolDecl *protocol,
bool allowMissing = false);

/// Global conformance lookup, checks conditional requirements.
///
/// \param type The type for which we are computing conformance. Must not
/// contain type parameters.
///
/// \param protocol The protocol to which we are computing conformance.
///
/// \param allowMissing When \c true, the resulting conformance reference
/// might include "missing" conformances, which are synthesized for some
/// protocols as an error recovery mechanism.
///
/// \returns An invalid conformance if the search failed, otherwise an
/// abstract, concrete or pack conformance, depending on the lookup type.
ProtocolConformanceRef checkConformance(Type type, ProtocolDecl *protocol,
// Note: different default than above
bool allowMissing = true);

/// Look for the conformance of the given existential type to the given
/// protocol.
ProtocolConformanceRef lookupExistentialConformance(Type type,
ProtocolDecl *protocol);

/// Exposes TypeChecker functionality for querying protocol conformance.
/// Returns a valid ProtocolConformanceRef only if all conditional
/// requirements are successfully resolved.
ProtocolConformanceRef conformsToProtocol(Type sourceTy,
ProtocolDecl *targetProtocol);

/// Collect the conformances of \c fromType to each of the protocols of an
/// existential type's layout.
///
Expand Down
23 changes: 23 additions & 0 deletions include/swift/AST/Requirement.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,29 @@ inline void simple_display(llvm::raw_ostream &out, const Requirement &req) {
req.print(out, PrintOptions());
}

enum class CheckRequirementsResult : uint8_t {
Success,

/// One of the requirements was unsatisfied.
RequirementFailure,

/// One of the requirements contained error types, either because of an
/// invalid conformance or because it contained a member type that was
/// dependent on an earlier conformance requirement that failed.
SubstitutionFailure
};

/// Check if each substituted requirement is satisfied. The requirement must
/// not contain any type parameters.
CheckRequirementsResult checkRequirements(ArrayRef<Requirement> requirements);

/// Check if each requirement is satisfied after applying the given
/// substitutions. The substitutions must replace all type parameters that
/// appear in the requirement with concrete types or archetypes.
CheckRequirementsResult checkRequirements(
ModuleDecl *module, ArrayRef<Requirement> requirements,
TypeSubstitutionFn substitutions, SubstOptions options=llvm::None);

/// A requirement as written in source, together with a source location. See
/// ProtocolDecl::getStructuralRequirements().
struct StructuralRequirement {
Expand Down
25 changes: 25 additions & 0 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2249,6 +2249,31 @@ LookupConformanceInModuleRequest::evaluate(
return ProtocolConformanceRef(conformance);
}

ProtocolConformanceRef
ModuleDecl::checkConformance(Type type, ProtocolDecl *proto,
bool allowMissing) {
auto lookupResult = lookupConformance(type, proto, allowMissing);
if (lookupResult.isInvalid()) {
return ProtocolConformanceRef::forInvalid();
}

auto condReqs = lookupResult.getConditionalRequirements();

// If we have a conditional requirements that we need to check, do so now.
if (!condReqs.empty()) {
switch (checkRequirements(condReqs)) {
case CheckRequirementsResult::Success:
break;

case CheckRequirementsResult::RequirementFailure:
case CheckRequirementsResult::SubstitutionFailure:
return ProtocolConformanceRef::forInvalid();
}
}

return lookupResult;
}

Fingerprint SourceFile::getInterfaceHash() const {
assert(hasInterfaceHash() && "Interface hash not enabled");
auto &eval = getASTContext().evaluator;
Expand Down
40 changes: 40 additions & 0 deletions lib/AST/Requirement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,46 @@ int Requirement::compare(const Requirement &other) const {
return compareProtos;
}

CheckRequirementsResult swift::checkRequirements(ArrayRef<Requirement> requirements) {
SmallVector<Requirement, 4> worklist(requirements.begin(), requirements.end());

bool hadSubstFailure = false;

while (!worklist.empty()) {
auto req = worklist.pop_back_val();
switch (req.checkRequirement(worklist, /*allowMissing=*/true)) {
case CheckRequirementResult::Success:
case CheckRequirementResult::ConditionalConformance:
case CheckRequirementResult::PackRequirement:
break;

case CheckRequirementResult::RequirementFailure:
return CheckRequirementsResult::RequirementFailure;

case CheckRequirementResult::SubstitutionFailure:
hadSubstFailure = true;
break;
}
}

if (hadSubstFailure)
return CheckRequirementsResult::SubstitutionFailure;

return CheckRequirementsResult::Success;
}

CheckRequirementsResult swift::checkRequirements(
ModuleDecl *module, ArrayRef<Requirement> requirements,
TypeSubstitutionFn substitutions, SubstOptions options) {
SmallVector<Requirement, 4> substReqs;
for (auto req : requirements) {
substReqs.push_back(req.subst(substitutions,
LookUpConformanceInModule(module), options));
}

return checkRequirements(substReqs);
}

InverseRequirement::InverseRequirement(Type subject,
ProtocolDecl *protocol,
SourceLoc loc)
Expand Down
2 changes: 1 addition & 1 deletion lib/IDE/ConformingMethodList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ void ConformingMethodListCallbacks::getMatchingMethods(

// The return type conforms to any of the requested protocols.
for (auto Proto : ExpectedTypes) {
if (CurModule->conformsToProtocol(resultTy, Proto))
if (CurModule->checkConformance(resultTy, Proto))
return true;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/IDE/IDETypeChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ class ExpressionTypeCollector: public SourceEntityWalker {

// Collecting protocols conformed by this expressions that are in the list.
for (auto Proto: InterestedProtocols) {
if (Module.conformsToProtocol(E->getType(), Proto.first)) {
if (Module.checkConformance(E->getType(), Proto.first)) {
Conformances.push_back(Proto.second);
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/Refactoring/Async/AsyncHandlerDesc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ using namespace swift::refactoring::asyncrefactorings;
static bool isErrorType(Type Ty, ModuleDecl *MD) {
if (!Ty)
return false;
return !MD->conformsToProtocol(Ty, Ty->getASTContext().getErrorDecl())
return !MD->checkConformance(Ty, Ty->getASTContext().getErrorDecl())
.isInvalid();
}

Expand Down
2 changes: 1 addition & 1 deletion lib/SIL/IR/SILType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1252,7 +1252,7 @@ SILType SILType::removingMoveOnlyWrapperToBoxedType(const SILFunction *fn) {

ProtocolConformanceRef
SILType::conformsToProtocol(SILFunction *fn, ProtocolDecl *protocol) const {
return fn->getParentModule()->conformsToProtocol(getASTType(), protocol);
return fn->getParentModule()->checkConformance(getASTType(), protocol);
}

bool SILType::isSendable(SILFunction *fn) const {
Expand Down
6 changes: 3 additions & 3 deletions lib/SIL/IR/TypeLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3027,7 +3027,7 @@ void TypeConverter::verifyTrivialLowering(const TypeLowering &lowering,
if (!bitwiseCopyableProtocol)
return;

auto conformance = M.conformsToProtocol(substType, bitwiseCopyableProtocol);
auto conformance = M.checkConformance(substType, bitwiseCopyableProtocol);

if (lowering.isTrivial() && !conformance) {
// A trivial type can only lack a conformance if one of its leaves is a
Expand Down Expand Up @@ -3063,7 +3063,7 @@ void TypeConverter::verifyTrivialLowering(const TypeLowering &lowering,

// A BitwiseCopyable conformer appearing within its layout doesn't
// explain why substType doesn't itself conform.
if (M.conformsToProtocol(ty, bitwiseCopyableProtocol))
if (M.checkConformance(ty, bitwiseCopyableProtocol))
return true;

// ModuleTypes are trivial but don't warrant being given a conformance
Expand Down Expand Up @@ -3143,7 +3143,7 @@ void TypeConverter::verifyTrivialLowering(const TypeLowering &lowering,

// Unfortunately, the type parameter's conformance may not be visible
// here.
assert(M.conformsToProtocol(ty, bitwiseCopyableProtocol) &&
assert(M.checkConformance(ty, bitwiseCopyableProtocol) &&
"leaf of non-trivial BitwiseCopyable type that doesn't "
"conform to BitwiseCopyable!?");

Expand Down
12 changes: 6 additions & 6 deletions lib/SIL/Utils/DynamicCasts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,17 +103,17 @@ classifyDynamicCastToProtocol(ModuleDecl *M, CanType source, CanType target,
if (!TargetProtocol)
return DynamicCastFeasibility::MaySucceed;

// If the target is a parameterized protocol type, conformsToProtocol
// If the target is a parameterized protocol type, checkConformance
// is insufficient to prove the feasibility of the cast as it does not
// check the additional requirements.
// FIXME: This is a weak predicate that doesn't take into account
// class compositions - since any C & P<T> doesn't work yet anyways.
if (isa<ParameterizedProtocolType>(unwrapExistential(target)))
return DynamicCastFeasibility::MaySucceed;

// If conformsToProtocol returns a valid conformance, then all requirements
// were proven by the type checker.
if (M->conformsToProtocol(source, TargetProtocol))
// If checkConformance() returns a valid conformance, then all conditional
// requirements were satisfied.
if (M->checkConformance(source, TargetProtocol))
return DynamicCastFeasibility::WillSucceed;

auto *SourceNominalTy = source.getAnyNominal();
Expand Down Expand Up @@ -141,14 +141,14 @@ classifyDynamicCastToProtocol(ModuleDecl *M, CanType source, CanType target,
}

// The WillFail conditions below assume any possible conformance on the
// nominal source type has been ruled out. The prior conformsToProtocol query
// nominal source type has been ruled out. The prior checkConformance query
// identified any definite conformance. Now check if there is already a known
// conditional conformance on the nominal type with requirements that were
// not proven.
//
// TODO: The TypeChecker can easily prove that some requirements cannot be
// met. Returning WillFail in those cases would be more optimal. To do that,
// the conformsToProtocol interface needs to be reformulated as a query, and
// the checkConformance interface needs to be reformulated as a query, and
// the implementation, including checkGenericArguments, needs to be taught to
// recognize that types with archetypes may potentially succeed.
if (auto conformance = M->lookupConformance(source, TargetProtocol)) {
Expand Down
2 changes: 1 addition & 1 deletion lib/SILGen/SILGenApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5866,7 +5866,7 @@ SILValue SILGenFunction::emitApplyWithRethrow(SILLocation loc, SILValue fn,
assert(outerErrorType == SILType::getExceptionType(getASTContext()));

ProtocolConformanceRef conformances[1] = {
getModule().getSwiftModule()->conformsToProtocol(
getModule().getSwiftModule()->checkConformance(
innerError->getType().getASTType(),
getASTContext().getErrorDecl())
};
Expand Down
2 changes: 1 addition & 1 deletion lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5949,7 +5949,7 @@ static void diagnoseImplicitRawConversion(Type sourceTy, Type pointerTy,
auto *SM = SGF.getModule().getSwiftModule();
if (auto *fixedWidthIntegerDecl = SM->getASTContext().getProtocol(
KnownProtocolKind::FixedWidthInteger)) {
if (SM->conformsToProtocol(eltTy, fixedWidthIntegerDecl))
if (SM->checkConformance(eltTy, fixedWidthIntegerDecl))
return;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/SILGen/SILGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1606,7 +1606,7 @@ void SILGenFunction::emitThrow(SILLocation loc, ManagedValue exnMV,
assert(destErrorType == SILType::getExceptionType(getASTContext()));

ProtocolConformanceRef conformances[1] = {
getModule().getSwiftModule()->conformsToProtocol(
getModule().getSwiftModule()->checkConformance(
exn->getType().getASTType(), getASTContext().getErrorDecl())
};

Expand Down
4 changes: 2 additions & 2 deletions lib/SILOptimizer/Utils/CastOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1461,14 +1461,14 @@ static bool optimizeStaticallyKnownProtocolConformance(
// SourceType is a non-existential type with a non-conditional
// conformance to a protocol represented by the TargetType.
//
// TypeChecker::conformsToProtocol checks any conditional conformances. If
// ModuleDecl::checkConformance() checks any conditional conformances. If
// they depend on information not known until runtime, the conformance
// will not be returned. For instance, if `X: P` where `T == Int` in `func
// foo<T>(_: T) { ... X<T>() as? P ... }`, the cast will succeed for
// `foo(0)` but not for `foo("string")`. There are many cases where
// everything is completely static (`X<Int>() as? P`), in which case a
// valid conformance will be returned.
auto Conformance = SM->conformsToProtocol(SourceType, Proto);
auto Conformance = SM->checkConformance(SourceType, Proto);
if (Conformance.isInvalid())
return false;

Expand Down
2 changes: 1 addition & 1 deletion lib/SILOptimizer/Utils/Existential.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ ConcreteExistentialInfo::ConcreteExistentialInfo(SILValue existential,

// We have the open_existential; we still need the conformance.
auto ConformanceRef =
M->getSwiftModule()->conformsToProtocol(ConcreteTypeCandidate, Protocol);
M->getSwiftModule()->checkConformance(ConcreteTypeCandidate, Protocol);
if (ConformanceRef.isInvalid())
return;

Expand Down
Loading