Merge pull request #85933 from slavapestov/fix-ben-regression

Sema: Disambiguate some more bidirectional conversions
This commit is contained in:
Slava Pestov
2025-12-10 06:53:01 -05:00
committed by GitHub
4 changed files with 175 additions and 71 deletions

View File

@@ -852,7 +852,7 @@ Comparison TypeChecker::compareDeclarations(DeclContext *dc,
return decl1Better ? Comparison::Better : Comparison::Worse;
}
static Type getUnlabeledType(Type type, ASTContext &ctx) {
static Type getStrippedType(Type type, ASTContext &ctx) {
return type.transformRec([&](TypeBase *type) -> std::optional<Type> {
if (auto *tupleType = dyn_cast<TupleType>(type)) {
if (tupleType->getNumElements() == 1)
@@ -866,6 +866,31 @@ static Type getUnlabeledType(Type type, ASTContext &ctx) {
return TupleType::get(elts, ctx);
}
if (auto *funcType = dyn_cast<FunctionType>(type)) {
auto params = funcType->getParams();
SmallVector<AnyFunctionType::Param, 4> newParams;
for (auto param : params) {
auto newParam = param;
switch (param.getParameterFlags().getOwnershipSpecifier()) {
case ParamSpecifier::Borrowing:
case ParamSpecifier::Consuming: {
auto flags = param.getParameterFlags()
.withOwnershipSpecifier(ParamSpecifier::Default);
newParams.push_back(param.withFlags(flags));
break;
}
default:
newParams.push_back(newParam);
break;
}
}
auto newExtInfo = funcType->getExtInfo().withRepresentation(
AnyFunctionType::Representation::Swift);
return FunctionType::get(newParams,
funcType->getResult(),
newExtInfo);
}
return std::nullopt;
});
}
@@ -1060,8 +1085,14 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
// In this case solver would produce 2 solutions: one where `count`
// is a property reference on `[Int]` and another one is tuple access
// for a `count:` element.
if (choice1.isDecl() != choice2.isDecl())
if (choice1.isDecl() != choice2.isDecl()) {
if (cs.isDebugMode()) {
llvm::errs().indent(cs.solverState->getCurrentIndent())
<< "- incomparable\n";
}
return SolutionCompareResult::Incomparable;
}
auto decl1 = choice1.getDecl();
auto dc1 = decl1->getDeclContext();
@@ -1438,15 +1469,16 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
if (type2Better)
++score2;
// Prefer the unlabeled form of a type.
auto unlabeled1 = getUnlabeledType(type1, cs.getASTContext());
auto unlabeled2 = getUnlabeledType(type2, cs.getASTContext());
if (unlabeled1->isEqual(unlabeled2)) {
if (type1->isEqual(unlabeled1) && !types.Type1WasLabeled) {
// Prefer the "stripped" form of a type. See getStrippedType()
// for the definition.
auto stripped1 = getStrippedType(type1, cs.getASTContext());
auto stripped2 = getStrippedType(type2, cs.getASTContext());
if (stripped1->isEqual(stripped2)) {
if (type1->isEqual(stripped1) && !types.Type1WasLabeled) {
++score1;
continue;
}
if (type2->isEqual(unlabeled2) && !types.Type2WasLabeled) {
if (type2->isEqual(stripped2) && !types.Type2WasLabeled) {
++score2;
continue;
}
@@ -1527,13 +1559,39 @@ SolutionCompareResult ConstraintSystem::compareSolutions(
// If the scores are different, we have a winner.
if (score1 != score2) {
return score1 > score2? SolutionCompareResult::Better
: SolutionCompareResult::Worse;
if (score1 > score2) {
if (cs.isDebugMode()) {
llvm::errs().indent(cs.solverState->getCurrentIndent())
<< "- better\n";
}
return SolutionCompareResult::Better;
} else {
if (cs.isDebugMode()) {
llvm::errs().indent(cs.solverState->getCurrentIndent())
<< "- worse\n";
}
return SolutionCompareResult::Worse;
}
}
// Neither system wins; report whether they were identical or not.
return identical? SolutionCompareResult::Identical
: SolutionCompareResult::Incomparable;
if (identical) {
if (cs.isDebugMode()) {
llvm::errs().indent(cs.solverState->getCurrentIndent())
<< "- identical\n";
}
return SolutionCompareResult::Identical;
} else {
if (cs.isDebugMode()) {
llvm::errs().indent(cs.solverState->getCurrentIndent())
<< "- incomparable\n";
}
return SolutionCompareResult::Incomparable;
}
}
std::optional<unsigned>

View File

@@ -1,8 +1,4 @@
// RUN: %target-typecheck-verify-swift
// REQUIRES: objc_interop
import Foundation
import CoreGraphics
/////////////
@@ -20,26 +16,6 @@ func foo2(x: (Int, Int)?, y: (x: Int, y: Int)) -> G<(Int, Int)> {
return g
}
func foo3(x: (@convention(block) () -> ())?, y: @escaping () -> ()) -> G<@convention(block) () -> ()> {
let g = G(t: x ?? y)
return g
}
func foo4(x: (() -> ())?, y: @escaping @convention(block) () -> ()) -> G<() -> ()> {
let g = G(t: x ?? y)
return g
}
func foo5(x: CGFloat?, y: Double) -> G<CGFloat> {
let g = G(t: x ?? y)
return g
}
func foo6(x: Double?, y: CGFloat) -> G<Double> {
let g = G(t: x ?? y)
return g
}
/////////////
func id<T>(_: T) -> T {}
@@ -54,28 +30,6 @@ func bar2(x: (Int, Int)) {
f(id(x))
}
func bar3(x: @escaping () -> ()) {
func f(_: @escaping @convention(block) () -> ()) {}
// FIXME
f(id(x)) // expected-error {{conflicting arguments to generic parameter 'T' ('@convention(block) () -> ()' vs. '() -> ()')}}
}
func bar4(x: @escaping @convention(block) () -> ()) {
func f(_: @escaping () -> ()) {}
// FIXME
f(id(x)) // expected-error {{conflicting arguments to generic parameter 'T' ('() -> ()' vs. '@convention(block) () -> ()')}}
}
func bar5(x: Double) {
func f(_: CGFloat) {}
f(id(x))
}
func bar6(x: CGFloat) {
func f(_: Double) {}
f(id(x))
}
/////////////
func unwrap<T>(_: T?) -> T {}
@@ -90,23 +44,36 @@ func baz2(x: (Int, Int)?) {
f(unwrap(x))
}
func baz3(x: (() -> ())?) {
func f(_: @escaping @convention(block) () -> ()) {}
f(unwrap(x))
/////////////
func borrowingFn(fn: @escaping (borrowing AnyObject) -> ()) -> (AnyObject) -> () {
return id(id(fn))
}
func baz4(x: (@convention(block) () -> ())?) {
func f(_: @escaping () -> ()) {}
f(unwrap(x))
/////////////
infix operator <+
infix operator >+
protocol P {
static func <+ (lhs: borrowing Self, rhs: borrowing Self)
static func >+ (lhs: borrowing Self, rhs: borrowing Self)
}
func baz5(x: Double?) {
func f(_: CGFloat) {}
f(unwrap(x))
extension P {
static func >+ (lhs: borrowing Self, rhs: borrowing Self) {}
}
func baz6(x: CGFloat?) {
func f(_: Double) {}
f(unwrap(x))
struct S: P {
static func <+ (lhs: Self, rhs: Self) {}
}
let _: (S, S) -> () = false ? (<+) : (>+)
/////////////
struct MyString: Comparable {
static func < (lhs: Self, rhs: Self) -> Bool { fatalError() }
}
let _: (MyString, MyString) -> Bool = false ? (<) : (>)

View File

@@ -0,0 +1,79 @@
// RUN: %target-typecheck-verify-swift
// REQUIRES: objc_interop
import Foundation
import CoreGraphics
/////////////
struct G<T> {
var t: T
}
func foo3(x: (@convention(block) () -> ())?, y: @escaping () -> ()) -> G<@convention(block) () -> ()> {
let g = G(t: x ?? y)
return g
}
func foo4(x: (() -> ())?, y: @escaping @convention(block) () -> ()) -> G<() -> ()> {
let g = G(t: x ?? y)
return g
}
func foo5(x: CGFloat?, y: Double) -> G<CGFloat> {
let g = G(t: x ?? y)
return g
}
func foo6(x: Double?, y: CGFloat) -> G<Double> {
let g = G(t: x ?? y)
return g
}
/////////////
func id<T>(_: T) -> T {}
func bar3(x: @escaping () -> ()) {
func f(_: @escaping @convention(block) () -> ()) {}
f(id(x))
}
func bar4(x: @escaping @convention(block) () -> ()) {
func f(_: @escaping () -> ()) {}
f(id(x))
}
func bar5(x: Double) {
func f(_: CGFloat) {}
f(id(x))
}
func bar6(x: CGFloat) {
func f(_: Double) {}
f(id(x))
}
/////////////
func unwrap<T>(_: T?) -> T {}
func baz3(x: (() -> ())?) {
func f(_: @escaping @convention(block) () -> ()) {}
f(unwrap(x))
}
func baz4(x: (@convention(block) () -> ())?) {
func f(_: @escaping () -> ()) {}
f(unwrap(x))
}
func baz5(x: Double?) {
func f(_: CGFloat) {}
f(unwrap(x))
}
func baz6(x: CGFloat?) {
func f(_: Double) {}
f(unwrap(x))
}

View File

@@ -551,7 +551,7 @@ do {
let qux: () -> Void
f(qux)
f(id(qux)) // expected-error {{conflicting arguments to generic parameter 'T' ('() -> Void' vs. '@convention(block) () -> Void')}}
f(id(qux))
func forceUnwrap<T>(_: T?) -> T {}