From e1a607711767c27a5ba74b59fd87c3d0ed2997e4 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 27 Oct 2025 18:07:10 +0900 Subject: [PATCH] [CSOptimizer] Update unary call favoring to include resolved member references Update special favoring logic for unlabeled unary calls to support non-overloads member references in argument positions. The original hack missed a case where a type of a member is known in advance (i.e. a property without overloads) because there as another hack (shrink) for that. This helps in situations like `Double(x)` where `x` is a property of some type that is referenced using an implicit `self.` injected by the compiler. Resolves: rdar://161419917 --- lib/Sema/CSOptimizer.cpp | 24 +++++++++++++---- .../old_hack_related_ambiguities.swift | 2 +- ...gle_arg_init_in_operator_context.swift.gyb | 27 +++++++++++++++++++ 3 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 validation-test/Sema/type_checker_perf/fast/property_passed_to_single_arg_init_in_operator_context.swift.gyb diff --git a/lib/Sema/CSOptimizer.cpp b/lib/Sema/CSOptimizer.cpp index c9237dd9cfa0e..df67f91bfdd3d 100644 --- a/lib/Sema/CSOptimizer.cpp +++ b/lib/Sema/CSOptimizer.cpp @@ -14,10 +14,11 @@ // //===----------------------------------------------------------------------===// -#include "TypeChecker.h" #include "OpenedExistentials.h" +#include "TypeChecker.h" #include "swift/AST/ConformanceLookup.h" #include "swift/AST/ExistentialLayout.h" +#include "swift/AST/Expr.h" #include "swift/AST/GenericSignature.h" #include "swift/Basic/Defer.h" #include "swift/Basic/OptionSet.h" @@ -28,7 +29,6 @@ #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/TinyPtrVector.h" -#include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -709,15 +709,29 @@ static std::optional preserveFavoringOfUnlabeledUnaryArgument( auto *argument = argumentList->getUnlabeledUnaryExpr()->getSemanticsProvidingExpr(); + + // If the type associated with a member expression doesn't have any type + // variables it means that it was resolved during pre-check to a single + // declaration. + // + // This helps in situations like `Double(x)` where `x` is a property of some + // type that is referenced using an implicit `self.` injected by the compiler. + auto isResolvedMemberReference = [&cs](Expr *expr) -> bool { + auto *UDE = dyn_cast(expr); + return UDE && !cs.getType(UDE)->hasTypeVariable(); + }; + // The hack operated on "favored" types and only declaration references, // applications, and (dynamic) subscripts had them if they managed to // get an overload choice selected during constraint generation. - // It's sometimes possible to infer a type of a literal and an operator - // chain, so it should be allowed as well. + // + // It's sometimes possible to infer a type of a literal, an operator + // chain, and a member, so it should be allowed as well. if (!(isExpr(argument) || isExpr(argument) || isExpr(argument) || isExpr(argument) || - isExpr(argument) || isExpr(argument))) + isExpr(argument) || isExpr(argument) || + isResolvedMemberReference(argument))) return DisjunctionInfo::none(); auto argumentType = cs.getType(argument)->getRValueType(); diff --git a/test/Constraints/old_hack_related_ambiguities.swift b/test/Constraints/old_hack_related_ambiguities.swift index d94dc86cc3ae3..ff5248595cf09 100644 --- a/test/Constraints/old_hack_related_ambiguities.swift +++ b/test/Constraints/old_hack_related_ambiguities.swift @@ -160,8 +160,8 @@ do { var p: UnsafeMutableRawPointer { get { fatalError() } } func f(_ p: UnsafeMutableRawPointer) { - // The old hack (which is now removed) couldn't handle member references, only direct declaration references. guard let x = UnsafeMutablePointer(OpaquePointer(self.p)) else { + // expected-error@-1 {{initializer for conditional binding must have Optional type, not 'UnsafeMutablePointer'}} return } _ = x diff --git a/validation-test/Sema/type_checker_perf/fast/property_passed_to_single_arg_init_in_operator_context.swift.gyb b/validation-test/Sema/type_checker_perf/fast/property_passed_to_single_arg_init_in_operator_context.swift.gyb new file mode 100644 index 0000000000000..68d74dc63d1ff --- /dev/null +++ b/validation-test/Sema/type_checker_perf/fast/property_passed_to_single_arg_init_in_operator_context.swift.gyb @@ -0,0 +1,27 @@ +// RUN: %scale-test --begin 1 --end 16 --step 1 --select NumLeafScopes %s +// REQUIRES: asserts,no_asan + +// There was a performance hack that handled calls with a single unlabeled argument +// in a very specific way. For source compatibility reasons old behavior has to be +// preserved in the disjunction optimizer as well, but the old hack missed a case +// where a type of a member is known in advance (i.e. a property without overloads) +// because there as another hack (shrink) for that. This test makes sure that +// performance for such cases won't regress in the future. + +struct Test { + var v = 0 + + func test() { + let _ = 1.0 * ( + 1.0 * Double(v) + +%for i in range(1, N): + %if i % 2 == 0: + 1.0 * Double(v) + + %else: + 1.0 * Double(self.v) + + %end +%end + 1.0 * Double(v) + ) + } +}