[ConstraintSystem] Narrowly disable tryOptimizeGenericDisjunction when some of the arguments are number literals

Don't attempt this optimization if call has number literals.
This is intended to narrowly fix situations like:

```swift
func test<T: FloatingPoint>(_: T) { ... }
func test<T: Numeric>(_: T) { ... }

test(42)
```

The call should use `<T: Numeric>` overload even though the
`<T: FloatingPoint>` is a more specialized version because
selecting `<T: Numeric>` doesn't introduce non-default literal
types.
This commit is contained in:
Pavel Yaskevich
2024-09-21 20:50:27 -07:00
parent f2a6677a6d
commit 8d5cb112ef
4 changed files with 38 additions and 0 deletions

View File

@@ -497,6 +497,10 @@ public:
/// literal (represented by `ArrayExpr` and `DictionaryExpr` in AST).
bool isCollectionLiteralType() const;
/// Determine whether this type variable represents a literal such
/// as an integer value, a floating-point value with and without a sign.
bool isNumberLiteralType() const;
/// Determine whether this type variable represents a result type of a
/// function call.
bool isFunctionResult() const;

View File

@@ -1402,6 +1402,27 @@ tryOptimizeGenericDisjunction(ConstraintSystem &cs, Constraint *disjunction,
return nullptr;
}
// Don't attempt this optimization if call has number literals.
// This is intended to narrowly fix situations like:
//
// func test<T: FloatingPoint>(_: T) { ... }
// func test<T: Numeric>(_: T) { ... }
//
// test(42)
//
// The call should use `<T: Numeric>` overload even though the
// `<T: FloatingPoint>` is a more specialized version because
// selecting `<T: Numeric>` doesn't introduce non-default literal
// types.
if (auto *argFnType = cs.getAppliedDisjunctionArgumentFunction(disjunction)) {
if (llvm::any_of(
argFnType->getParams(), [](const AnyFunctionType::Param &param) {
auto *typeVar = param.getPlainType()->getAs<TypeVariableType>();
return typeVar && typeVar->getImpl().isNumberLiteralType();
}))
return nullptr;
}
llvm::SmallVector<Constraint *, 4> choices;
for (auto *choice : constraints) {
if (choices.size() > 2)

View File

@@ -204,6 +204,10 @@ bool TypeVariableType::Implementation::isCollectionLiteralType() const {
locator->directlyAt<DictionaryExpr>());
}
bool TypeVariableType::Implementation::isNumberLiteralType() const {
return locator && locator->directlyAt<NumberLiteralExpr>();
}
bool TypeVariableType::Implementation::isFunctionResult() const {
return locator && locator->isLastElement<LocatorPathElt::FunctionResult>();
}

View File

@@ -178,3 +178,12 @@ do {
}
}
// `tryOptimizeGenericDisjunction` is too aggressive sometimes, make sure that `<T: FloatingPoint>`
// overload is _not_ selected in this case.
do {
func test<T: FloatingPoint>(_ expression1: @autoclosure () throws -> T, accuracy: T) -> T {}
func test<T: Numeric>(_ expression1: @autoclosure () throws -> T, accuracy: T) -> T {}
let result = test(10, accuracy: 1)
let _: Int = result
}