From d350bc719825569700598277efb44a6a336ec7f8 Mon Sep 17 00:00:00 2001 From: Clack Cole Date: Wed, 19 Nov 2025 16:15:30 -0700 Subject: [PATCH 1/2] [CSOptimizer] test: Add test case for issue #85020 Overload resolution picks wrong initializer for some metatype arguments https://github.com/swiftlang/swift/issues/85020 --- test/Interpreter/issue-85020.swift | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 test/Interpreter/issue-85020.swift diff --git a/test/Interpreter/issue-85020.swift b/test/Interpreter/issue-85020.swift new file mode 100644 index 0000000000000..f4bd32ce931fe --- /dev/null +++ b/test/Interpreter/issue-85020.swift @@ -0,0 +1,33 @@ +// RUN: %target-run-simple-swift | %FileCheck %s + +// https://github.com/swiftlang/swift/issues/85020 + +struct Store { + let theType: Any.Type + + init(of theType: Any.Type) { + print("init from TYPE: \(theType)") + self.theType = theType + } + + init(of instance: Any) { + print("init from VALUE: \(instance)") + self.init(of: type(of: instance)) + } +} + +let a: (any Numeric)? = 42 +print("a: \(type(of: a))") +// CHECK: a: Optional + +let storeA = Store(of: a!) +// CHECK-NEXT: init from VALUE: 42 +// CHECK-NEXT: init from TYPE: Int + +let b: (any Numeric.Type)? = type(of: 42) +print("b: \(type(of: b))") +// CHECK-NEXT: b: Optional + +let storeB = Store(of: b!) +// CHECK-NEXT: init from TYPE: Int + From a405fffebb9fd3259a7ae4d309546006d6232207 Mon Sep 17 00:00:00 2001 From: Clack Cole Date: Fri, 21 Nov 2025 12:43:07 -0700 Subject: [PATCH 2/2] [CSOptimizer] favor Any.Type for metatype args alongside Any fast-path Resolves https://github.com/swiftlang/swift/issues/85020 --- lib/Sema/CSOptimizer.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/Sema/CSOptimizer.cpp b/lib/Sema/CSOptimizer.cpp index aab3431df14bb..c822f51f5457a 100644 --- a/lib/Sema/CSOptimizer.cpp +++ b/lib/Sema/CSOptimizer.cpp @@ -1429,11 +1429,22 @@ static void determineBestChoicesInContext( } } - // If the parameter is `Any` we assume that all candidates are - // convertible to it, which makes it a perfect match. The solver - // would then decide whether erasing to an existential is preferable. - if (paramType->isAny()) - return 1; + if (paramType->isAnyExistentialType()) { + // If the parameter is `Any` we assume that all candidates are + // convertible to it, which makes it a perfect match. The solver + // would then decide whether erasing to an existential is preferable. + if (paramType->isAny()) + return 1; + + // If the parameter is `Any.Type` we assume that all metatype + // candidates are convertible to it. + if (auto *EMT = paramType->getAs()) { + if (EMT->getExistentialInstanceType()->isAny() && + (candidateType->is() || + candidateType->is())) + return 1; + } + } // Check if a candidate could be matched to a parameter by // an existential opening.