Skip to content

Commit adfceb4

Browse files
committed
IUO: Rework function type matching to use ParamDecls.
Rather than comparing function types directly, use the types from the ParamDecls along with the function result type and the IUO attributes to determine whether two function types "match" by our definition.
1 parent ffa6a55 commit adfceb4

File tree

3 files changed

+100
-8
lines changed

3 files changed

+100
-8
lines changed

include/swift/AST/Types.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,15 @@ class alignas(1 << TypeAlignInBits) TypeBase {
776776
/// \p matchOptions.
777777
bool matches(Type other, TypeMatchOptions matchOptions);
778778

779+
bool matchesParameter(Type other, TypeMatchOptions matchMode);
780+
781+
/// \brief Determines whether this function type is similar to \p
782+
/// other as defined by \p matchOptions and the callback \p
783+
/// paramsAndResultMatch which determines in a client-specific way
784+
/// whether the parameters and result of the types match.
785+
bool matchesFunctionType(Type other, TypeMatchOptions matchOptions,
786+
std::function<bool()> paramsAndResultMatch);
787+
779788
/// \brief Determines whether this type has a retainable pointer
780789
/// representation, i.e. whether it is representable as a single,
781790
/// possibly nil pointer that can be unknown-retained and

lib/AST/Type.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2180,10 +2180,10 @@ namespace {
21802180
};
21812181
} // end anonymous namespace
21822182

2183-
static bool matchFunctionTypes(CanAnyFunctionType fn1, CanAnyFunctionType fn2,
2184-
TypeMatchOptions matchMode,
2185-
OptionalUnwrapping insideOptional,
2186-
std::function<bool()> paramsAndResultMatch) {
2183+
static bool matchesFunctionType(CanAnyFunctionType fn1, CanAnyFunctionType fn2,
2184+
TypeMatchOptions matchMode,
2185+
OptionalUnwrapping insideOptional,
2186+
std::function<bool()> paramsAndResultMatch) {
21872187
// FIXME: Handle generic functions in non-ABI matches.
21882188
if (!matchMode.contains(TypeMatchFlags::AllowABICompatible)) {
21892189
if (!isa<FunctionType>(fn1) || !isa<FunctionType>(fn2))
@@ -2301,8 +2301,8 @@ static bool matches(CanType t1, CanType t2, TypeMatchOptions matchMode,
23012301
OptionalUnwrapping::None));
23022302
};
23032303

2304-
return matchFunctionTypes(fn1, fn2, matchMode, insideOptional,
2305-
paramsAndResultMatch);
2304+
return matchesFunctionType(fn1, fn2, matchMode, insideOptional,
2305+
paramsAndResultMatch);
23062306
}
23072307

23082308
if (matchMode.contains(TypeMatchFlags::AllowNonOptionalForIUOParam) &&
@@ -2333,6 +2333,21 @@ bool TypeBase::matches(Type other, TypeMatchOptions matchMode) {
23332333
ParameterPosition::NotParameter, OptionalUnwrapping::None);
23342334
}
23352335

2336+
bool TypeBase::matchesParameter(Type other, TypeMatchOptions matchMode) {
2337+
return ::matches(getCanonicalType(), other->getCanonicalType(), matchMode,
2338+
ParameterPosition::Parameter, OptionalUnwrapping::None);
2339+
}
2340+
2341+
bool TypeBase::matchesFunctionType(Type other, TypeMatchOptions matchMode,
2342+
std::function<bool()> paramsAndResultMatch) {
2343+
auto thisFnTy = dyn_cast<AnyFunctionType>(getCanonicalType());
2344+
auto otherFnTy = dyn_cast<AnyFunctionType>(other->getCanonicalType());
2345+
2346+
assert(thisFnTy && otherFnTy);
2347+
return ::matchesFunctionType(thisFnTy, otherFnTy, matchMode,
2348+
OptionalUnwrapping::None, paramsAndResultMatch);
2349+
}
2350+
23362351
/// getNamedElementId - If this tuple has a field with the specified name,
23372352
/// return the field index, otherwise return -1.
23382353
int TupleType::getNamedElementId(Identifier I) const {

lib/Sema/TypeCheckDecl.cpp

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5937,6 +5937,60 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
59375937
}
59385938
}
59395939

5940+
static bool parameterTypesMatch(const ValueDecl *derivedDecl,
5941+
const ValueDecl *baseDecl,
5942+
TypeMatchOptions matchMode) {
5943+
const ParameterList *derivedParams;
5944+
const ParameterList *baseParams;
5945+
if (auto *derived = dyn_cast<AbstractFunctionDecl>(derivedDecl)) {
5946+
auto *base = dyn_cast<AbstractFunctionDecl>(baseDecl);
5947+
if (!base)
5948+
return false;
5949+
baseParams = base->getParameterList(1);
5950+
derivedParams = derived->getParameterList(1);
5951+
} else {
5952+
auto *base = dyn_cast<SubscriptDecl>(baseDecl);
5953+
if (!base)
5954+
return false;
5955+
baseParams = base->getIndices();
5956+
derivedParams = cast<SubscriptDecl>(derivedDecl)->getIndices();
5957+
}
5958+
5959+
if (baseParams->size() != derivedParams->size())
5960+
return false;
5961+
5962+
auto subs = SubstitutionMap::getOverrideSubstitutions(baseDecl, derivedDecl,
5963+
/*derivedSubs=*/None);
5964+
5965+
for (auto i : indices(baseParams->getArray())) {
5966+
auto baseItfTy = baseParams->get(i)->getInterfaceType();
5967+
auto baseParamTy =
5968+
baseDecl->getAsGenericContext()->mapTypeIntoContext(baseItfTy);
5969+
baseParamTy = baseParamTy.subst(subs);
5970+
auto derivedParamTy = derivedParams->get(i)->getInterfaceType();
5971+
5972+
// Attempt contravariant match.
5973+
if (baseParamTy->matchesParameter(derivedParamTy, matchMode))
5974+
continue;
5975+
5976+
// Try once more for a match, using the underlying type of an
5977+
// IUO if we're allowing that.
5978+
if (baseParams->get(i)
5979+
->getAttrs()
5980+
.hasAttribute<ImplicitlyUnwrappedOptionalAttr>() &&
5981+
matchMode.contains(TypeMatchFlags::AllowNonOptionalForIUOParam)) {
5982+
baseParamTy = baseParamTy->getAnyOptionalObjectType();
5983+
if (baseParamTy->matches(derivedParamTy, matchMode))
5984+
continue;
5985+
}
5986+
5987+
// If there is no match, then we're done.
5988+
return false;
5989+
}
5990+
5991+
return true;
5992+
}
5993+
59405994
/// Determine which method or subscript this method or subscript overrides
59415995
/// (if any).
59425996
///
@@ -6155,8 +6209,22 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
61556209
TypeMatchFlags::IgnoreNonEscapingForOptionalFunctionParam;
61566210
}
61576211

6158-
if (declTy->matches(parentDeclTy, matchMode)) {
6159-
// If the Objective-C selectors match, always call it exact.
6212+
auto declFnTy = declTy->getAs<AnyFunctionType>();
6213+
auto parentDeclFnTy = parentDeclTy->getAs<AnyFunctionType>();
6214+
if (declFnTy && parentDeclFnTy) {
6215+
auto paramsAndResultMatch = [=]() -> bool {
6216+
return parameterTypesMatch(decl, parentDecl, matchMode) &&
6217+
declFnTy->getResult()->matches(parentDeclFnTy->getResult(),
6218+
matchMode);
6219+
};
6220+
6221+
if (declFnTy->matchesFunctionType(parentDeclFnTy, matchMode,
6222+
paramsAndResultMatch)) {
6223+
matches.push_back({parentDecl, objCMatch, parentDeclTy});
6224+
hadExactMatch |= objCMatch;
6225+
continue;
6226+
}
6227+
} else if (declTy->matches(parentDeclTy, matchMode)) {
61606228
matches.push_back({parentDecl, objCMatch, parentDeclTy});
61616229
hadExactMatch |= objCMatch;
61626230
continue;

0 commit comments

Comments
 (0)