Sema: Fix handling of getter typed throws in associated type inference

We must replace type parameters with archetypes in the thrown error type
of the witness. The requirement type remains an interface type.
This commit is contained in:
Slava Pestov
2025-04-01 14:46:21 -04:00
parent de3cf4d982
commit b26c59962b
2 changed files with 96 additions and 30 deletions

View File

@@ -1908,27 +1908,16 @@ static Type getWithoutProtocolTypeAliases(Type type) {
///
/// Also see simplifyCurrentTypeWitnesses().
static Type getWitnessTypeForMatching(NormalProtocolConformance *conformance,
ValueDecl *witness) {
if (witness->isRecursiveValidation()) {
LLVM_DEBUG(llvm::dbgs() << "Recursive validation\n";);
return Type();
}
if (witness->isInvalid()) {
LLVM_DEBUG(llvm::dbgs() << "Invalid witness\n";);
return Type();
}
ValueDecl *witness, Type type) {
if (!witness->getDeclContext()->isTypeContext()) {
// FIXME: Could we infer from 'Self' to make these work?
return witness->getInterfaceType();
return type;
}
// Retrieve the set of substitutions to be applied to the witness.
Type model =
conformance->getDeclContext()->mapTypeIntoContext(conformance->getType());
TypeSubstitutionMap substitutions = model->getMemberSubstitutions(witness);
Type type = witness->getInterfaceType()->getReferenceStorageReferent();
type = getWithoutProtocolTypeAliases(type);
@@ -2082,14 +2071,20 @@ AssociatedTypeInference::inferTypeWitnessesViaAssociatedType(
!witnessHasImplementsAttrForRequiredName(typeDecl, assocType))
continue;
// Determine the witness type.
Type witnessType = getWitnessTypeForMatching(conformance, typeDecl);
if (!witnessType) continue;
if (auto witnessMetaType = witnessType->getAs<AnyMetatypeType>())
witnessType = witnessMetaType->getInstanceType();
else
if (typeDecl->isInvalid()) {
LLVM_DEBUG(llvm::dbgs() << "Recursive validation\n";);
continue;
}
if (typeDecl->isRecursiveValidation()) {
LLVM_DEBUG(llvm::dbgs() << "Recursive validation\n";);
continue;
}
// Determine the witness type.
Type witnessType = getWitnessTypeForMatching(conformance, typeDecl,
typeDecl->getDeclaredInterfaceType());
if (!witnessType) continue;
if (result.empty()) {
// If we found at least one default candidate, we must allow for the
@@ -2177,18 +2172,29 @@ AssociatedTypeInference::getPotentialTypeWitnessesByMatchingTypes(ValueDecl *req
InferredAssociatedTypesByWitness inferred;
inferred.Witness = witness;
// Compute the requirement and witness types we'll use for matching.
Type fullWitnessType = getWitnessTypeForMatching(conformance, witness);
if (!fullWitnessType) {
auto reqType = removeSelfParam(req, req->getInterfaceType());
Type witnessType;
if (witness->isRecursiveValidation()) {
LLVM_DEBUG(llvm::dbgs() << "Recursive validation\n";);
return inferred;
}
LLVM_DEBUG(llvm::dbgs() << "Witness type for matching is "
<< fullWitnessType << "\n";);
if (witness->isInvalid()) {
LLVM_DEBUG(llvm::dbgs() << "Invalid witness\n";);
return inferred;
}
auto setup =
[&]() -> std::tuple<std::optional<RequirementMatch>, Type, Type, Type, Type> {
fullWitnessType = removeSelfParam(witness, fullWitnessType);
// Compute the requirement and witness types we'll use for matching.
witnessType = witness->getInterfaceType()->getReferenceStorageReferent();
witnessType = getWitnessTypeForMatching(conformance, witness, witnessType);
LLVM_DEBUG(llvm::dbgs() << "Witness type for matching is "
<< witnessType << "\n";);
witnessType = removeSelfParam(witness, witnessType);
Type reqThrownError;
Type witnessThrownError;
@@ -2211,12 +2217,15 @@ AssociatedTypeInference::getPotentialTypeWitnessesByMatchingTypes(ValueDecl *req
};
reqThrownError = getThrownErrorType(reqASD);
witnessThrownError = getThrownErrorType(witnessASD);
witnessThrownError = getWitnessTypeForMatching(conformance, witness,
witnessThrownError);
}
return std::make_tuple(std::nullopt,
removeSelfParam(req, req->getInterfaceType()),
fullWitnessType, reqThrownError, witnessThrownError);
reqType, witnessType,
reqThrownError, witnessThrownError);
};
/// Visits a requirement type to match it to a potential witness for
@@ -2352,7 +2361,7 @@ AssociatedTypeInference::getPotentialTypeWitnessesByMatchingTypes(ValueDecl *req
Type witnessType) -> std::optional<RequirementMatch> {
if (!matchVisitor.match(reqType, witnessType)) {
return RequirementMatch(witness, MatchKind::TypeConflict,
fullWitnessType);
witnessType);
}
return std::nullopt;
@@ -2365,7 +2374,7 @@ AssociatedTypeInference::getPotentialTypeWitnessesByMatchingTypes(ValueDecl *req
return RequirementMatch(witness,
anyRenaming ? MatchKind::RenamedMatch
: MatchKind::ExactMatch,
fullWitnessType);
witnessType);
};

View File

@@ -0,0 +1,57 @@
// RUN: %target-typecheck-verify-swift
protocol P {
associatedtype E: Error
var prop: Int { get throws(E) } // expected-note {{protocol requires property 'prop' with type 'Int'}}
}
struct S0: P {
var prop: Int {
get {}
}
}
struct S1: P {
var prop: Int {
get throws(Never) {}
}
}
struct S2: P {
var prop: Int {
get throws {}
}
}
struct S3: P {
var prop: Int {
get throws(any Error) {}
}
}
struct MyError: Error {}
struct S4: P {
var prop: Int {
get throws(MyError) {}
}
}
struct S5<E: Error>: P {
var prop: Int {
get throws(E) {}
}
}
// Invalid example
struct S6: P { // expected-error {{type 'S6' does not conform to protocol 'P'}}
// expected-note@-1 {{add stubs for conformance}}
typealias E = MyError
var prop: Int { // expected-note {{candidate has non-matching type 'Int'}}
get throws(any Error) {
fatalError()
}
}
}