Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions lib/Sema/CSOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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>
Expand Down Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion test/Constraints/old_hack_related_ambiguities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
)
}
}