Skip to content

Commit c333b0b

Browse files
committed
[AST] Generalize TypeBase::canOverride to TypeBase::matches.
This allows fuzzy matching of top-level optionals in types, using the same logic we use to fuzzy-match potential overrides.
1 parent c98e9bb commit c333b0b

File tree

6 files changed

+174
-143
lines changed

6 files changed

+174
-143
lines changed

include/swift/AST/Types.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -221,19 +221,20 @@ enum class TypeTraitResult {
221221

222222
/// Specifies which normally-unsafe type mismatches should be accepted when
223223
/// checking overrides.
224-
enum class OverrideMatchMode {
225-
/// Only accept overrides that are properly covariant.
226-
Strict,
224+
enum class TypeMatchFlags {
225+
/// Allow properly-covariant overrides.
226+
AllowOverride = 1 << 0,
227227
/// Allow a parameter with IUO type to be overridden by a parameter with non-
228228
/// optional type.
229-
AllowNonOptionalForIUOParam,
229+
AllowNonOptionalForIUOParam = 1 << 1,
230230
/// Allow any mismatches of Optional or ImplicitlyUnwrappedOptional at the
231231
/// top level of a type.
232232
///
233233
/// This includes function parameters and result types as well as tuple
234234
/// elements, but excludes generic parameters.
235-
AllowTopLevelOptionalMismatch
235+
AllowTopLevelOptionalMismatch = 1 << 2
236236
};
237+
using TypeMatchOptions = OptionSet<TypeMatchFlags>;
237238

238239
/// TypeBase - Base class for all types in Swift.
239240
class alignas(1 << TypeAlignInBits) TypeBase {
@@ -702,10 +703,9 @@ class alignas(1 << TypeAlignInBits) TypeBase {
702703
/// concrete types to form the argument type.
703704
bool isBindableTo(Type ty);
704705

705-
/// \brief Determines whether this type is permitted as a method override
706-
/// of the \p other.
707-
bool canOverride(Type other, OverrideMatchMode matchMode,
708-
LazyResolver *resolver);
706+
/// \brief Determines whether this type is similar to \p other as defined by
707+
/// \p matchOptions.
708+
bool matches(Type other, TypeMatchOptions matchOptions, LazyResolver *resolver);
709709

710710
/// \brief Determines whether this type has a retainable pointer
711711
/// representation, i.e. whether it is representable as a single,

lib/AST/Type.cpp

Lines changed: 47 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2316,13 +2316,9 @@ bool TypeBase::isTriviallyRepresentableIn(ForeignLanguage language,
23162316
llvm_unreachable("Unhandled ForeignRepresentableKind in switch.");
23172317
}
23182318

2319-
/// Is t1 not just a subtype of t2, but one such that its values are
2320-
/// trivially convertible to values of the other?
2321-
static bool canOverride(CanType t1, CanType t2,
2322-
OverrideMatchMode matchMode,
2323-
bool isParameter,
2324-
bool insideOptional,
2325-
LazyResolver *resolver) {
2319+
static bool matches(CanType t1, CanType t2, TypeMatchOptions matchMode,
2320+
bool isParameter, bool insideOptional,
2321+
LazyResolver *resolver) {
23262322
if (t1 == t2) return true;
23272323

23282324
// First try unwrapping optionals.
@@ -2332,26 +2328,24 @@ static bool canOverride(CanType t1, CanType t2,
23322328
if (auto obj2 = t2.getAnyOptionalObjectType()) {
23332329
// Optional-to-optional.
23342330
if (auto obj1 = t1.getAnyOptionalObjectType()) {
2335-
// Allow T? and T! to freely override one another.
2336-
return canOverride(obj1, obj2, matchMode,
2337-
/*isParameter=*/false,
2338-
/*insideOptional=*/true,
2339-
resolver);
2331+
// Allow T? and T! to freely match one another.
2332+
return matches(obj1, obj2, matchMode, /*isParameter=*/false,
2333+
/*insideOptional=*/true, resolver);
23402334
}
23412335

23422336
// Value-to-optional.
2343-
return canOverride(t1, obj2, matchMode,
2344-
/*isParameter=*/false,
2345-
/*insideOptional=*/true,
2346-
resolver);
2337+
if (matchMode.contains(TypeMatchFlags::AllowOverride) ||
2338+
matchMode.contains(TypeMatchFlags::AllowTopLevelOptionalMismatch)) {
2339+
return matches(t1, obj2, matchMode, /*isParameter=*/false,
2340+
/*insideOptional=*/true, resolver);
2341+
}
23472342

2348-
} else if (matchMode == OverrideMatchMode::AllowTopLevelOptionalMismatch) {
2343+
} else if (matchMode.contains(
2344+
TypeMatchFlags::AllowTopLevelOptionalMismatch)) {
23492345
// Optional-to-value, normally disallowed.
23502346
if (auto obj1 = t1.getAnyOptionalObjectType()) {
2351-
return canOverride(obj1, t2, matchMode,
2352-
/*isParameter=*/false,
2353-
/*insideOptional=*/true,
2354-
resolver);
2347+
return matches(obj1, t2, matchMode, /*isParameter=*/false,
2348+
/*insideOptional=*/true, resolver);
23552349
}
23562350
}
23572351
}
@@ -2362,54 +2356,49 @@ static bool canOverride(CanType t1, CanType t2,
23622356
// certain that the LHS isn't also a singleton tuple.
23632357
auto tuple1 = dyn_cast<TupleType>(t1);
23642358
if (!tuple1 || tuple1->getNumElements() != tuple2->getNumElements()) {
2365-
if (tuple2->getNumElements() == 1)
2366-
return canOverride(t1, tuple2.getElementType(0),
2367-
matchMode,
2368-
isParameter,
2369-
/*insideOptional=*/false,
2370-
resolver);
2359+
if (tuple2->getNumElements() == 1) {
2360+
return matches(t1, tuple2.getElementType(0), matchMode, isParameter,
2361+
/*insideOptional=*/false, resolver);
2362+
}
23712363
return false;
23722364
}
23732365

23742366
for (auto i : indices(tuple1.getElementTypes())) {
2375-
if (!canOverride(tuple1.getElementType(i),
2376-
tuple2.getElementType(i),
2377-
matchMode,
2378-
isParameter,
2379-
/*insideOptional=*/false,
2380-
resolver))
2367+
if (!matches(tuple1.getElementType(i), tuple2.getElementType(i),
2368+
matchMode, isParameter, /*insideOptional=*/false, resolver)){
23812369
return false;
2370+
}
23822371
}
23832372
return true;
23842373
}
23852374

2386-
// Function-to-function. (Polymorphic functions?)
2375+
// Function-to-function.
2376+
// FIXME: This completely leaves out generic functions.
23872377
if (auto fn2 = dyn_cast<FunctionType>(t2)) {
23882378
auto fn1 = dyn_cast<FunctionType>(t1);
23892379
if (!fn1)
23902380
return false;
23912381

2392-
// Allow the base type to be throwing even if the overriding type isn't
2382+
// When checking overrides, allow the base type to be throwing even if the
2383+
// overriding type isn't.
23932384
auto ext1 = fn1->getExtInfo();
23942385
auto ext2 = fn2->getExtInfo();
2395-
if (ext2.throws()) ext1 = ext1.withThrows(true);
2386+
if (matchMode.contains(TypeMatchFlags::AllowOverride)) {
2387+
if (ext2.throws()) {
2388+
ext1 = ext1.withThrows(true);
2389+
}
2390+
}
23962391
if (ext1 != ext2)
23972392
return false;
23982393

23992394
// Inputs are contravariant, results are covariant.
2400-
return (canOverride(fn2.getInput(), fn1.getInput(),
2401-
matchMode,
2402-
/*isParameter=*/true,
2403-
/*insideOptional=*/false,
2404-
resolver) &&
2405-
canOverride(fn1.getResult(), fn2.getResult(),
2406-
matchMode,
2407-
/*isParameter=*/false,
2408-
/*insideOptional=*/false,
2409-
resolver));
2410-
}
2411-
2412-
if (matchMode == OverrideMatchMode::AllowNonOptionalForIUOParam &&
2395+
return (matches(fn2.getInput(), fn1.getInput(), matchMode,
2396+
/*isParameter=*/true, /*insideOptional=*/false, resolver) &&
2397+
matches(fn1.getResult(), fn2.getResult(), matchMode,
2398+
/*isParameter=*/false, /*insideOptional=*/false, resolver));
2399+
}
2400+
2401+
if (matchMode.contains(TypeMatchFlags::AllowNonOptionalForIUOParam) &&
24132402
isParameter && !insideOptional) {
24142403
// Allow T to override T! in certain cases.
24152404
if (auto obj1 = t1->getImplicitlyUnwrappedOptionalObjectType()) {
@@ -2419,16 +2408,17 @@ static bool canOverride(CanType t1, CanType t2,
24192408
}
24202409

24212410
// Class-to-class.
2422-
return t2->isExactSuperclassOf(t1);
2411+
if (matchMode.contains(TypeMatchFlags::AllowOverride))
2412+
if (t2->isExactSuperclassOf(t1))
2413+
return true;
2414+
2415+
return false;
24232416
}
24242417

2425-
bool TypeBase::canOverride(Type other, OverrideMatchMode matchMode,
2426-
LazyResolver *resolver) {
2427-
return ::canOverride(getCanonicalType(), other->getCanonicalType(),
2428-
matchMode,
2429-
/*isParameter=*/false,
2430-
/*insideOptional=*/false,
2431-
resolver);
2418+
bool TypeBase::matches(Type other, TypeMatchOptions matchMode,
2419+
LazyResolver *resolver) {
2420+
return ::matches(getCanonicalType(), other->getCanonicalType(), matchMode,
2421+
/*isParameter=*/false, /*insideOptional=*/false, resolver);
24322422
}
24332423

24342424
/// getNamedElementId - If this tuple has a field with the specified name,

lib/Sema/TypeCheckDecl.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5767,15 +5767,15 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
57675767
}
57685768

57695769
// Failing that, check for subtyping.
5770-
auto matchMode = OverrideMatchMode::Strict;
5770+
TypeMatchOptions matchMode = TypeMatchFlags::AllowOverride;
57715771
if (attempt == OverrideCheckingAttempt::MismatchedOptional ||
57725772
attempt == OverrideCheckingAttempt::BaseNameWithMismatchedOptional){
5773-
matchMode = OverrideMatchMode::AllowTopLevelOptionalMismatch;
5773+
matchMode |= TypeMatchFlags::AllowTopLevelOptionalMismatch;
57745774
} else if (parentDecl->isObjC()) {
5775-
matchMode = OverrideMatchMode::AllowNonOptionalForIUOParam;
5775+
matchMode |= TypeMatchFlags::AllowNonOptionalForIUOParam;
57765776
}
57775777

5778-
if (declTy->canOverride(parentDeclTy, matchMode, &TC)) {
5778+
if (declTy->matches(parentDeclTy, matchMode, &TC)) {
57795779
// If the Objective-C selectors match, always call it exact.
57805780
matches.push_back({parentDecl, objCMatch, parentDeclTy});
57815781
hadExactMatch |= objCMatch;
@@ -5992,9 +5992,9 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
59925992
auto parentPropertyTy = superclass->adjustSuperclassMemberDeclType(
59935993
matchDecl, decl, matchDecl->getInterfaceType());
59945994

5995-
if (!propertyTy->canOverride(parentPropertyTy,
5996-
OverrideMatchMode::Strict,
5997-
&TC)) {
5995+
if (!propertyTy->matches(parentPropertyTy,
5996+
TypeMatchFlags::AllowOverride,
5997+
&TC)) {
59985998
TC.diagnose(property, diag::override_property_type_mismatch,
59995999
property->getName(), propertyTy, parentPropertyTy);
60006000
noteFixableMismatchedTypes(TC, decl, matchDecl);

unittests/AST/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
add_swift_unittest(SwiftASTTests
2-
OverrideTests.cpp
32
SourceLocTests.cpp
43
TestContext.cpp
4+
TypeMatchTests.cpp
55
VersionRangeLattice.cpp
66
)
77

unittests/AST/TestContext.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "swift/AST/ASTContext.h"
1414
#include "swift/AST/ASTScope.h"
1515
#include "swift/AST/DiagnosticEngine.h"
16+
#include "swift/AST/Module.h"
1617
#include "swift/Basic/LangOptions.h"
1718
#include "swift/Basic/SourceManager.h"
1819

0 commit comments

Comments
 (0)