[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
This commit is contained in:
Pavel Yaskevich
2025-10-27 18:07:10 +09:00
parent a0d33c3e7e
commit e1a6077117
3 changed files with 47 additions and 6 deletions

View File

@@ -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 <cstddef>
#include <functional>
@@ -709,15 +709,29 @@ static std::optional<DisjunctionInfo> 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<UnresolvedDotExpr>(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<DeclRefExpr>(argument) || isExpr<ApplyExpr>(argument) ||
isExpr<SubscriptExpr>(argument) ||
isExpr<DynamicSubscriptExpr>(argument) ||
isExpr<LiteralExpr>(argument) || isExpr<BinaryExpr>(argument)))
isExpr<LiteralExpr>(argument) || isExpr<BinaryExpr>(argument) ||
isResolvedMemberReference(argument)))
return DisjunctionInfo::none();
auto argumentType = cs.getType(argument)->getRValueType();

View File

@@ -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<Double>(OpaquePointer(self.p)) else {
// expected-error@-1 {{initializer for conditional binding must have Optional type, not 'UnsafeMutablePointer<Double>'}}
return
}
_ = x

View File

@@ -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)
)
}
}