Skip to content

Commit b300068

Browse files
committed
Sema: Fix handling of getter typed throws in witness matching
Witness matching didn't handle the case where either the requirement or the witness is a property with a throwing getter, and the thrown error type contains a type parameter. We must open the thrown error types first, replacing type parameters with type variables, for the matching to work. Associated type inference needs a similar fix. I'll land a combined test case for both once I fix that. Fixes #80288. Fixes rdar://problem/147874955.
1 parent d5937e6 commit b300068

File tree

4 files changed

+83
-42
lines changed

4 files changed

+83
-42
lines changed

lib/Sema/AssociatedTypeInference.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2187,11 +2187,36 @@ AssociatedTypeInference::getPotentialTypeWitnessesByMatchingTypes(ValueDecl *req
21872187
<< fullWitnessType << "\n";);
21882188

21892189
auto setup =
2190-
[&]() -> std::tuple<std::optional<RequirementMatch>, Type, Type> {
2190+
[&]() -> std::tuple<std::optional<RequirementMatch>, Type, Type, Type, Type> {
21912191
fullWitnessType = removeSelfParam(witness, fullWitnessType);
2192+
2193+
Type reqThrownError;
2194+
Type witnessThrownError;
2195+
2196+
if (auto *witnessASD = dyn_cast<AbstractStorageDecl>(witness)) {
2197+
auto *reqASD = cast<AbstractStorageDecl>(req);
2198+
2199+
// Dig out the thrown error types from the getter so we can compare them
2200+
// later.
2201+
auto getThrownErrorType = [](AbstractStorageDecl *asd) -> Type {
2202+
if (auto getter = asd->getEffectfulGetAccessor()) {
2203+
if (Type thrownErrorType = getter->getThrownInterfaceType()) {
2204+
return thrownErrorType;
2205+
} else if (getter->hasThrows()) {
2206+
return asd->getASTContext().getErrorExistentialType();
2207+
}
2208+
}
2209+
2210+
return asd->getASTContext().getNeverType();
2211+
};
2212+
2213+
reqThrownError = getThrownErrorType(reqASD);
2214+
witnessThrownError = getThrownErrorType(witnessASD);
2215+
}
2216+
21922217
return std::make_tuple(std::nullopt,
21932218
removeSelfParam(req, req->getInterfaceType()),
2194-
fullWitnessType);
2219+
fullWitnessType, reqThrownError, witnessThrownError);
21952220
};
21962221

21972222
/// Visits a requirement type to match it to a potential witness for

lib/Sema/TypeCheckEffects.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4897,7 +4897,11 @@ static ThrownErrorClassification classifyThrownErrorType(Type type) {
48974897
return ThrownErrorClassification::AnyError;
48984898
}
48994899

4900-
if (type->hasTypeVariable() || type->hasTypeParameter())
4900+
// All three cases come up. The first one from the "real" witness matcher,
4901+
// and the other two from associated type inference.
4902+
if (type->hasTypeVariable() ||
4903+
type->hasTypeParameter() ||
4904+
type->hasPrimaryArchetype())
49014905
return ThrownErrorClassification::Dependent;
49024906

49034907
return ThrownErrorClassification::Specific;

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -512,8 +512,7 @@ checkEffects(AbstractStorageDecl *witness, AbstractStorageDecl *req) {
512512
/// to be used by `matchWitness`.
513513
static std::optional<RequirementMatch>
514514
matchWitnessStructureImpl(ValueDecl *req, ValueDecl *witness,
515-
bool &decomposeFunctionType, bool &ignoreReturnType,
516-
Type &reqThrownError, Type &witnessThrownError) {
515+
bool &decomposeFunctionType, bool &ignoreReturnType) {
517516
assert(!req->isInvalid() && "Cannot have an invalid requirement here");
518517

519518
/// Make sure the witness is of the same kind as the requirement.
@@ -658,23 +657,6 @@ matchWitnessStructureImpl(ValueDecl *req, ValueDecl *witness,
658657

659658
// Decompose the parameters for subscript declarations.
660659
decomposeFunctionType = isa<SubscriptDecl>(req);
661-
662-
// Dig out the thrown error types from the getter so we can compare them
663-
// later.
664-
auto getThrownErrorType = [](AbstractStorageDecl *asd) -> Type {
665-
if (auto getter = asd->getEffectfulGetAccessor()) {
666-
if (Type thrownErrorType = getter->getThrownInterfaceType()) {
667-
return thrownErrorType;
668-
} else if (getter->hasThrows()) {
669-
return asd->getASTContext().getAnyExistentialType();
670-
}
671-
}
672-
673-
return asd->getASTContext().getNeverType();
674-
};
675-
676-
reqThrownError = getThrownErrorType(reqASD);
677-
witnessThrownError = getThrownErrorType(witnessASD);
678660
} else if (isa<ConstructorDecl>(witness)) {
679661
decomposeFunctionType = true;
680662
ignoreReturnType = true;
@@ -713,39 +695,33 @@ bool swift::TypeChecker::witnessStructureMatches(ValueDecl *req,
713695
const ValueDecl *witness) {
714696
bool decomposeFunctionType = false;
715697
bool ignoreReturnType = false;
716-
Type reqThrownError;
717-
Type witnessThrownError;
718698
return matchWitnessStructureImpl(req, const_cast<ValueDecl *>(witness),
719-
decomposeFunctionType, ignoreReturnType,
720-
reqThrownError,
721-
witnessThrownError) == std::nullopt;
699+
decomposeFunctionType, ignoreReturnType)
700+
== std::nullopt;
722701
}
723702

724703
RequirementMatch swift::matchWitness(
725704
DeclContext *dc, ValueDecl *req, ValueDecl *witness,
726705
llvm::function_ref<
727-
std::tuple<std::optional<RequirementMatch>, Type, Type>(void)>
706+
std::tuple<std::optional<RequirementMatch>, Type, Type, Type, Type>(void)>
728707
setup,
729708
llvm::function_ref<std::optional<RequirementMatch>(Type, Type)> matchTypes,
730709
llvm::function_ref<RequirementMatch(bool, ArrayRef<OptionalAdjustment>)>
731710
finalize) {
732711
bool decomposeFunctionType = false;
733712
bool ignoreReturnType = false;
734-
Type reqThrownError;
735-
Type witnessThrownError;
736713

737714
if (auto StructuralMismatch = matchWitnessStructureImpl(
738-
req, witness, decomposeFunctionType, ignoreReturnType, reqThrownError,
739-
witnessThrownError)) {
715+
req, witness, decomposeFunctionType, ignoreReturnType)) {
740716
return *StructuralMismatch;
741717
}
742718

743719
// Set up the match, determining the requirement and witness types
744720
// in the process.
745-
Type reqType, witnessType;
721+
Type reqType, witnessType, reqThrownError, witnessThrownError;
746722
{
747723
std::optional<RequirementMatch> result;
748-
std::tie(result, reqType, witnessType) = setup();
724+
std::tie(result, reqType, witnessType, reqThrownError, witnessThrownError) = setup();
749725
if (result) {
750726
return std::move(result.value());
751727
}
@@ -936,7 +912,8 @@ RequirementMatch swift::matchWitness(
936912

937913
case ThrownErrorSubtyping::Subtype:
938914
// If there were no type parameters, we're done.
939-
if (!reqThrownError->hasTypeParameter())
915+
if (!reqThrownError->hasTypeVariable() &&
916+
!reqThrownError->hasTypeParameter())
940917
break;
941918

942919
LLVM_FALLTHROUGH;
@@ -1186,7 +1163,7 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
11861163

11871164
// Set up the constraint system for matching.
11881165
auto setup =
1189-
[&]() -> std::tuple<std::optional<RequirementMatch>, Type, Type> {
1166+
[&]() -> std::tuple<std::optional<RequirementMatch>, Type, Type, Type, Type> {
11901167
// Construct a constraint system to use to solve the equality between
11911168
// the required type and the witness type.
11921169
cs.emplace(dc, ConstraintSystemFlags::AllowFixes);
@@ -1199,10 +1176,12 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
11991176
if (syntheticEnv)
12001177
selfTy = syntheticEnv->mapTypeIntoContext(selfTy);
12011178

1179+
12021180
// Open up the type of the requirement.
1181+
SmallVector<OpenedType, 4> reqReplacements;
1182+
12031183
reqLocator =
12041184
cs->getConstraintLocator(req, ConstraintLocator::ProtocolRequirement);
1205-
SmallVector<OpenedType, 4> reqReplacements;
12061185
reqType =
12071186
cs->getTypeOfMemberReference(selfTy, req, dc,
12081187
/*isDynamicResult=*/false,
@@ -1235,14 +1214,17 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
12351214
}
12361215

12371216
// Open up the witness type.
1217+
SmallVector<OpenedType, 4> witnessReplacements;
1218+
12381219
witnessType = witness->getInterfaceType();
12391220
witnessLocator =
12401221
cs->getConstraintLocator(req, LocatorPathElt::Witness(witness));
12411222
if (witness->getDeclContext()->isTypeContext()) {
12421223
openWitnessType =
1243-
cs->getTypeOfMemberReference(
1244-
selfTy, witness, dc, /*isDynamicResult=*/false,
1245-
FunctionRefInfo::doubleBaseNameApply(), witnessLocator)
1224+
cs->getTypeOfMemberReference(selfTy, witness, dc,
1225+
/*isDynamicResult=*/false,
1226+
FunctionRefInfo::doubleBaseNameApply(),
1227+
witnessLocator, &witnessReplacements)
12461228
.adjustedReferenceType;
12471229
} else {
12481230
openWitnessType =
@@ -1253,7 +1235,37 @@ swift::matchWitness(WitnessChecker::RequirementEnvironmentCache &reqEnvCache,
12531235
}
12541236
openWitnessType = openWitnessType->getRValueType();
12551237

1256-
return std::make_tuple(std::nullopt, reqType, openWitnessType);
1238+
Type reqThrownError;
1239+
Type witnessThrownError;
1240+
1241+
if (auto *witnessASD = dyn_cast<AbstractStorageDecl>(witness)) {
1242+
auto *reqASD = cast<AbstractStorageDecl>(req);
1243+
1244+
// Dig out the thrown error types from the getter so we can compare them
1245+
// later.
1246+
auto getThrownErrorType = [](AbstractStorageDecl *asd) -> Type {
1247+
if (auto getter = asd->getEffectfulGetAccessor()) {
1248+
if (Type thrownErrorType = getter->getThrownInterfaceType()) {
1249+
return thrownErrorType;
1250+
} else if (getter->hasThrows()) {
1251+
return asd->getASTContext().getErrorExistentialType();
1252+
}
1253+
}
1254+
1255+
return asd->getASTContext().getNeverType();
1256+
};
1257+
1258+
reqThrownError = getThrownErrorType(reqASD);
1259+
reqThrownError = cs->openType(reqThrownError, reqReplacements,
1260+
reqLocator);
1261+
1262+
witnessThrownError = getThrownErrorType(witnessASD);
1263+
witnessThrownError = cs->openType(witnessThrownError, witnessReplacements,
1264+
witnessLocator);
1265+
}
1266+
1267+
return std::make_tuple(std::nullopt, reqType, openWitnessType,
1268+
reqThrownError, witnessThrownError);
12571269
};
12581270

12591271
// Match a type in the requirement to a type in the witness.

lib/Sema/TypeCheckProtocol.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ class ConformanceChecker : public WitnessChecker {
180180
RequirementMatch matchWitness(
181181
DeclContext *dc, ValueDecl *req, ValueDecl *witness,
182182
llvm::function_ref<
183-
std::tuple<std::optional<RequirementMatch>, Type, Type>(void)>
183+
std::tuple<std::optional<RequirementMatch>, Type, Type, Type, Type>(void)>
184184
setup,
185185
llvm::function_ref<std::optional<RequirementMatch>(Type, Type)> matchTypes,
186186
llvm::function_ref<RequirementMatch(bool, ArrayRef<OptionalAdjustment>)>

0 commit comments

Comments
 (0)