Skip to content

Commit 248c026

Browse files
authored
Merge pull request swiftlang#78949 from xedin/rdar-143475850-part-2
[CSOptimizer] Don't consider CGFloat widening when explicit initializ…
2 parents 694e659 + 3cc76ea commit 248c026

File tree

2 files changed

+55
-24
lines changed

2 files changed

+55
-24
lines changed

lib/Sema/CSOptimizer.cpp

+40-24
Original file line numberDiff line numberDiff line change
@@ -370,22 +370,36 @@ static void determineBestChoicesInContext(
370370
FunctionType::relabelParams(argsWithLabels, argumentList);
371371
}
372372

373-
SmallVector<SmallVector<std::pair<Type, /*fromLiteral=*/bool>, 2>, 2>
374-
candidateArgumentTypes;
375-
candidateArgumentTypes.resize(argFuncType->getNumParams());
373+
struct ArgumentCandidate {
374+
Type type;
375+
// The candidate type is derived from a literal expression.
376+
bool fromLiteral : 1;
377+
// The candidate type is derived from a call to an
378+
// initializer i.e. `Double(...)`.
379+
bool fromInitializerCall : 1;
380+
381+
ArgumentCandidate(Type type, bool fromLiteral = false,
382+
bool fromInitializerCall = false)
383+
: type(type), fromLiteral(fromLiteral),
384+
fromInitializerCall(fromInitializerCall) {}
385+
};
386+
387+
SmallVector<SmallVector<ArgumentCandidate, 2>, 2>
388+
argumentCandidates;
389+
argumentCandidates.resize(argFuncType->getNumParams());
376390

377391
llvm::TinyPtrVector<Type> resultTypes;
378392

379393
for (unsigned i = 0, n = argFuncType->getNumParams(); i != n; ++i) {
380394
const auto &param = argFuncType->getParams()[i];
381395
auto argType = cs.simplifyType(param.getPlainType());
382396

383-
SmallVector<std::pair<Type, bool>, 2> types;
397+
SmallVector<ArgumentCandidate, 2> types;
384398
if (auto *typeVar = argType->getAs<TypeVariableType>()) {
385399
auto bindingSet = cs.getBindingsFor(typeVar);
386400

387401
for (const auto &binding : bindingSet.Bindings) {
388-
types.push_back({binding.BindingType, /*fromLiteral=*/false});
402+
types.push_back({binding.BindingType});
389403
}
390404

391405
for (const auto &literal : bindingSet.Literals) {
@@ -408,15 +422,16 @@ static void determineBestChoicesInContext(
408422
if (auto *typeExpr = dyn_cast<TypeExpr>(call->getFn())) {
409423
auto instanceTy = cs.getType(typeExpr)->getMetatypeInstanceType();
410424
if (instanceTy->isDouble() || instanceTy->isCGFloat())
411-
types.push_back({instanceTy, /*fromLiteral=*/false});
425+
types.push_back({instanceTy, /*fromLiteral=*/false,
426+
/*fromInitializerCall=*/true});
412427
}
413428
}
414429
}
415430
} else {
416431
types.push_back({argType, /*fromLiteral=*/false});
417432
}
418433

419-
candidateArgumentTypes[i].append(types);
434+
argumentCandidates[i].append(types);
420435
}
421436

422437
auto resultType = cs.simplifyType(argFuncType->getResult());
@@ -437,9 +452,9 @@ static void determineBestChoicesInContext(
437452
argFuncType->getNumParams() > 0 &&
438453
llvm::none_of(
439454
indices(argFuncType->getParams()), [&](const unsigned argIdx) {
440-
auto &candidates = candidateArgumentTypes[argIdx];
455+
auto &candidates = argumentCandidates[argIdx];
441456
return llvm::any_of(candidates, [&](const auto &candidate) {
442-
return !candidate.second;
457+
return !candidate.fromLiteral;
443458
});
444459
});
445460

@@ -846,7 +861,7 @@ static void determineBestChoicesInContext(
846861
auto argIdx = argIndices.front();
847862

848863
// Looks like there is nothing know about the argument.
849-
if (candidateArgumentTypes[argIdx].empty())
864+
if (argumentCandidates[argIdx].empty())
850865
continue;
851866

852867
const auto paramFlags = param.getParameterFlags();
@@ -873,32 +888,25 @@ static void determineBestChoicesInContext(
873888
// at this parameter position and remove the overload choice
874889
// from consideration.
875890
double bestCandidateScore = 0;
876-
llvm::BitVector mismatches(candidateArgumentTypes[argIdx].size());
891+
llvm::BitVector mismatches(argumentCandidates[argIdx].size());
877892

878893
for (unsigned candidateIdx :
879-
indices(candidateArgumentTypes[argIdx])) {
894+
indices(argumentCandidates[argIdx])) {
880895
// If one of the candidates matched exactly there is no reason
881896
// to continue checking.
882897
if (bestCandidateScore == 1)
883898
break;
884899

885-
Type candidateType;
886-
bool isLiteralDefault;
887-
888-
std::tie(candidateType, isLiteralDefault) =
889-
candidateArgumentTypes[argIdx][candidateIdx];
900+
auto candidate = argumentCandidates[argIdx][candidateIdx];
890901

891902
// `inout` parameter accepts only l-value argument.
892-
if (paramFlags.isInOut() && !candidateType->is<LValueType>()) {
903+
if (paramFlags.isInOut() && !candidate.type->is<LValueType>()) {
893904
mismatches.set(candidateIdx);
894905
continue;
895906
}
896907

897-
// The specifier only matters for `inout` check.
898-
candidateType = candidateType->getWithoutSpecifierType();
899-
900908
MatchOptions options(MatchFlag::OnParam);
901-
if (isLiteralDefault)
909+
if (candidate.fromLiteral)
902910
options |= MatchFlag::Literal;
903911
if (favorExactMatchesOnly)
904912
options |= MatchFlag::ExactOnly;
@@ -913,8 +921,16 @@ static void determineBestChoicesInContext(
913921
if (n == 1 && decl->isOperator())
914922
options |= MatchFlag::DisableCGFloatDoubleConversion;
915923

924+
// Disable implicit CGFloat -> Double widening conversion if
925+
// argument is an explicit call to `CGFloat` initializer.
926+
if (candidate.type->isCGFloat() &&
927+
candidate.fromInitializerCall)
928+
options |= MatchFlag::DisableCGFloatDoubleConversion;
929+
930+
// The specifier for a candidate only matters for `inout` check.
916931
auto candidateScore = scoreCandidateMatch(
917-
genericSig, decl, candidateType, paramType, options);
932+
genericSig, decl, candidate.type->getWithoutSpecifierType(),
933+
paramType, options);
918934

919935
if (!candidateScore)
920936
continue;
@@ -928,7 +944,7 @@ static void determineBestChoicesInContext(
928944
// Only established arguments could be considered mismatches,
929945
// literal default types should be regarded as holes if they
930946
// didn't match.
931-
if (!isLiteralDefault && !candidateType->hasTypeVariable())
947+
if (!candidate.fromLiteral && !candidate.type->hasTypeVariable())
932948
mismatches.set(candidateIdx);
933949
}
934950

test/Constraints/implicit_double_cgfloat_conversion.swift

+15
Original file line numberDiff line numberDiff line change
@@ -366,3 +366,18 @@ func test_cgfloat_operator_is_attempted_with_literal_arguments(v: CGFloat?) {
366366
let ratio = v ?? (2.0 / 16.0)
367367
let _: CGFloat = ratio // Ok
368368
}
369+
370+
// Make sure that optimizer doesn't favor CGFloat -> Double conversion
371+
// in presence of CGFloat initializer, otherwise it could lead to ambiguities.
372+
func test_explicit_cgfloat_use_avoids_ambiguity(v: Int) {
373+
func test(_: CGFloat) -> CGFloat { 0 }
374+
func test(_: Double) -> Double { 0 }
375+
376+
func hasCGFloatElement<C: Collection>(_: C) where C.Element == CGFloat {}
377+
378+
let arr = [test(CGFloat(v))]
379+
hasCGFloatElement(arr) // Ok
380+
381+
var total = 0.0 // This is Double by default
382+
total += test(CGFloat(v)) + CGFloat(v) // Ok
383+
}

0 commit comments

Comments
 (0)