Skip to content

Commit db0a9de

Browse files
committed
[CSOptimizer] Account for the fact that sometimes all initializer choices are failable
If all of the viable initializer overloads are failable, the only valid inference choice is an optional candidate type.
1 parent 9e97b8e commit db0a9de

File tree

2 files changed

+99
-38
lines changed

2 files changed

+99
-38
lines changed

lib/Sema/CSOptimizer.cpp

Lines changed: 65 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,43 @@ static bool isSupportedDisjunction(Constraint *disjunction) {
344344
});
345345
}
346346

347+
/// Determine whether the given overload choice constitutes a
348+
/// valid choice that would be attempted during normal solving
349+
/// without any score increases.
350+
static ValueDecl *isViableOverloadChoice(ConstraintSystem &cs,
351+
Constraint *constraint,
352+
ConstraintLocator *locator) {
353+
if (constraint->isDisabled())
354+
return nullptr;
355+
356+
if (constraint->getKind() != ConstraintKind::BindOverload)
357+
return nullptr;
358+
359+
auto choice = constraint->getOverloadChoice();
360+
auto *decl = choice.getDeclOrNull();
361+
if (!decl)
362+
return nullptr;
363+
364+
// Ignore declarations that come from implicitly imported modules
365+
// when `MemberImportVisibility` feature is enabled otherwise
366+
// we might end up favoring an overload that would be diagnosed
367+
// as unavailable later.
368+
if (cs.getASTContext().LangOpts.hasFeature(Feature::MemberImportVisibility)) {
369+
if (auto *useDC = constraint->getOverloadUseDC()) {
370+
if (!useDC->isDeclImported(decl))
371+
return nullptr;
372+
}
373+
}
374+
375+
// If disjunction choice is unavailable or disfavored we cannot
376+
// do anything with it.
377+
if (decl->getAttrs().hasAttribute<DisfavoredOverloadAttr>() ||
378+
cs.isDeclUnavailable(decl, locator))
379+
return nullptr;
380+
381+
return decl;
382+
}
383+
347384
/// Given the type variable that represents a result type of a
348385
/// function call, check whether that call is to an initializer
349386
/// and based on that deduce possible type for the result.
@@ -389,16 +426,30 @@ inferTypeFromInitializerResultType(ConstraintSystem &cs,
389426
if (initRef == disjunctions.end())
390427
return {};
391428

392-
bool hasFailable =
393-
llvm::any_of((*initRef)->getNestedConstraints(), [](Constraint *choice) {
394-
if (choice->isDisabled())
395-
return false;
396-
auto *decl =
397-
dyn_cast_or_null<ConstructorDecl>(getOverloadChoiceDecl(choice));
398-
return decl && decl->isFailable();
399-
});
429+
unsigned numFailable = 0;
430+
unsigned total = 0;
431+
for (auto *choice : (*initRef)->getNestedConstraints()) {
432+
auto *decl = isViableOverloadChoice(cs, choice, ctorLocator);
433+
if (!decl || !isa<ConstructorDecl>(decl))
434+
continue;
400435

401-
return {instanceTy, hasFailable};
436+
auto *ctor = cast<ConstructorDecl>(decl);
437+
if (ctor->isFailable())
438+
++numFailable;
439+
440+
++total;
441+
}
442+
443+
if (numFailable > 0) {
444+
// If all of the active choices are failable, produce an optional
445+
// type only.
446+
if (numFailable == total)
447+
return {instanceTy->wrapInOptionalType(), /*hasFailable=*/false};
448+
// Otherwise there are two options.
449+
return {instanceTy, /*hasFailable*/ true};
450+
}
451+
452+
return {instanceTy, /*hasFailable=*/false};
402453
}
403454

404455
/// If the given expression represents a chain of operators that only have
@@ -502,38 +553,14 @@ void forEachDisjunctionChoice(
502553
llvm::function_ref<void(Constraint *, ValueDecl *decl, FunctionType *)>
503554
callback) {
504555
for (auto constraint : disjunction->getNestedConstraints()) {
505-
if (constraint->isDisabled())
506-
continue;
507-
508-
if (constraint->getKind() != ConstraintKind::BindOverload)
509-
continue;
510-
511-
auto choice = constraint->getOverloadChoice();
512-
auto *decl = choice.getDeclOrNull();
556+
auto *decl =
557+
isViableOverloadChoice(cs, constraint, disjunction->getLocator());
513558
if (!decl)
514559
continue;
515560

516-
// Ignore declarations that come from implicitly imported modules
517-
// when `MemberImportVisibility` feature is enabled otherwise
518-
// we might end up favoring an overload that would be diagnosed
519-
// as unavailable later.
520-
if (cs.getASTContext().LangOpts.hasFeature(
521-
Feature::MemberImportVisibility)) {
522-
if (auto *useDC = constraint->getDeclContext()) {
523-
if (!useDC->isDeclImported(decl))
524-
continue;
525-
}
526-
}
527-
528-
// If disjunction choice is unavailable or disfavored we cannot
529-
// do anything with it.
530-
if (decl->getAttrs().hasAttribute<DisfavoredOverloadAttr>() ||
531-
cs.isDeclUnavailable(decl, disjunction->getLocator()))
532-
continue;
533-
534-
Type overloadType =
535-
cs.getEffectiveOverloadType(disjunction->getLocator(), choice,
536-
/*allowMembers=*/true, cs.DC);
561+
Type overloadType = cs.getEffectiveOverloadType(
562+
disjunction->getLocator(), constraint->getOverloadChoice(),
563+
/*allowMembers=*/true, constraint->getOverloadUseDC());
537564

538565
if (!overloadType || !overloadType->is<FunctionType>())
539566
continue;

test/Constraints/old_hack_related_ambiguities.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,37 @@ struct TestUnary {
326326
}
327327
}
328328
}
329+
330+
// Prevent non-optional overload of `??` to be favored when all initializers are failable.
331+
332+
class A {}
333+
class B {}
334+
335+
protocol P {
336+
init()
337+
}
338+
339+
extension P {
340+
init?(v: A) { self.init() }
341+
}
342+
343+
struct V : P {
344+
init() {}
345+
346+
@_disfavoredOverload
347+
init?(v: B?) {}
348+
349+
// Important to keep this to make sure that disabled constraints
350+
// are handled properly.
351+
init<T: Collection>(other: T) where T.Element == Character {}
352+
}
353+
354+
class TestFailableOnly {
355+
var v: V?
356+
357+
func test(defaultB: B) {
358+
guard let _ = self.v ?? V(v: defaultB) else { // OK (no warnings)
359+
return
360+
}
361+
}
362+
}

0 commit comments

Comments
 (0)