Skip to content

Commit b9f4ae0

Browse files
authored
Merge pull request #59082 from xedin/se-0352-as-any-coercion-5.7
[5.7][TypeChecker] SE-0352: Require coercion if result type contains exist…
2 parents 412f0e0 + 938d147 commit b9f4ae0

File tree

9 files changed

+500
-3
lines changed

9 files changed

+500
-3
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6340,5 +6340,19 @@ ERROR(attr_incompatible_with_back_deploy,none,
63406340
"'%0' cannot be applied to a back deployed %1",
63416341
(DeclAttribute, DescriptiveDeclKind))
63426342

6343+
//------------------------------------------------------------------------------
6344+
// MARK: Implicit opening of existential types
6345+
//------------------------------------------------------------------------------
6346+
6347+
ERROR(result_requires_explicit_coercion,none,
6348+
"inferred result type %0 requires explicit coercion due to "
6349+
"loss of generic requirements",
6350+
(Type))
6351+
6352+
NOTE(candidate_result_requires_explicit_coercion,none,
6353+
"inferred result type %0 requires explicit coercion due to "
6354+
"loss of generic requirements",
6355+
(Type))
6356+
63436357
#define UNDEFINE_DIAGNOSTIC_MACROS
63446358
#include "DefineDiagnosticMacros.h"

include/swift/Sema/CSFix.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,10 @@ enum class FixKind : uint8_t {
386386
/// Ignore a type mismatch while trying to infer generic parameter type
387387
/// from default expression.
388388
IgnoreDefaultExprTypeMismatch,
389+
390+
/// Coerce a result type of a call to a particular existential type
391+
/// by adding `as any <#Type#>`.
392+
AddExplicitExistentialCoercion,
389393
};
390394

391395
class ConstraintFix {
@@ -2925,6 +2929,37 @@ class IgnoreDefaultExprTypeMismatch : public AllowArgumentMismatch {
29252929
}
29262930
};
29272931

2932+
class AddExplicitExistentialCoercion final : public ConstraintFix {
2933+
Type ErasedResultType;
2934+
2935+
AddExplicitExistentialCoercion(ConstraintSystem &cs, Type erasedResultTy,
2936+
ConstraintLocator *locator)
2937+
: ConstraintFix(cs, FixKind::AddExplicitExistentialCoercion, locator),
2938+
ErasedResultType(erasedResultTy) {}
2939+
2940+
public:
2941+
std::string getName() const override {
2942+
return "add explicit existential type coercion";
2943+
}
2944+
2945+
bool diagnose(const Solution &solution, bool asNote = false) const override;
2946+
2947+
static bool
2948+
isRequired(ConstraintSystem &cs, Type resultTy,
2949+
ArrayRef<std::pair<TypeVariableType *, OpenedArchetypeType *>>
2950+
openedExistentials,
2951+
ConstraintLocatorBuilder locator);
2952+
2953+
static bool isRequired(ConstraintSystem &cs, Type resultTy,
2954+
llvm::function_ref<Optional<Type>(TypeVariableType *)>
2955+
findExistentialType,
2956+
ConstraintLocatorBuilder locator);
2957+
2958+
static AddExplicitExistentialCoercion *create(ConstraintSystem &cs,
2959+
Type resultTy,
2960+
ConstraintLocator *locator);
2961+
};
2962+
29282963
} // end namespace constraints
29292964
} // end namespace swift
29302965

lib/Sema/CSDiagnostics.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8105,3 +8105,54 @@ bool DefaultExprTypeMismatch::diagnoseAsError() {
81058105

81068106
return true;
81078107
}
8108+
8109+
bool MissingExplicitExistentialCoercion::diagnoseAsError() {
8110+
auto diagnostic = emitDiagnostic(diag::result_requires_explicit_coercion,
8111+
ErasedResultType);
8112+
fixIt(diagnostic);
8113+
return true;
8114+
}
8115+
8116+
bool MissingExplicitExistentialCoercion::diagnoseAsNote() {
8117+
auto diagnostic = emitDiagnostic(
8118+
diag::candidate_result_requires_explicit_coercion, ErasedResultType);
8119+
fixIt(diagnostic);
8120+
return true;
8121+
}
8122+
8123+
bool MissingExplicitExistentialCoercion::fixItRequiresParens() const {
8124+
auto anchor = getAsExpr(getRawAnchor());
8125+
8126+
// If it's a member reference an an existential metatype, let's
8127+
// use the parent "call" expression.
8128+
if (auto *UDE = dyn_cast_or_null<UnresolvedDotExpr>(anchor))
8129+
anchor = findParentExpr(UDE);
8130+
8131+
if (!anchor)
8132+
return false;
8133+
8134+
const auto &solution = getSolution();
8135+
return llvm::any_of(
8136+
solution.OpenedExistentialTypes,
8137+
[&anchor](const auto &openedExistential) {
8138+
if (auto openedLoc = simplifyLocatorToAnchor(openedExistential.first)) {
8139+
return anchor == getAsExpr(openedLoc);
8140+
}
8141+
return false;
8142+
});
8143+
}
8144+
8145+
void MissingExplicitExistentialCoercion::fixIt(
8146+
InFlightDiagnostic &diagnostic) const {
8147+
bool requiresParens = fixItRequiresParens();
8148+
8149+
auto callRange = getSourceRange();
8150+
8151+
if (requiresParens)
8152+
diagnostic.fixItInsert(callRange.Start, "(");
8153+
8154+
auto printOpts = PrintOptions::forDiagnosticArguments();
8155+
diagnostic.fixItInsertAfter(callRange.End,
8156+
"as " + ErasedResultType->getString(printOpts) +
8157+
(requiresParens ? ")" : ""));
8158+
}

lib/Sema/CSDiagnostics.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2693,6 +2693,53 @@ class DefaultExprTypeMismatch final : public ContextualFailure {
26932693
bool diagnoseAsError() override;
26942694
};
26952695

2696+
/// Diagnose situations where inferring existential type for result of
2697+
/// a call would result in loss of generic requirements.
2698+
///
2699+
/// \code
2700+
/// protocol P {
2701+
/// associatedtype A
2702+
/// }
2703+
///
2704+
/// protocol Q {
2705+
/// associatedtype B: P where B.A == Int
2706+
/// }
2707+
///
2708+
/// func getB<T: Q>(_: T) -> T.B { ... }
2709+
///
2710+
/// func test(v: any Q) {
2711+
/// let _ = getB(v) // <- produces `any P` which looses A == Int
2712+
/// }
2713+
/// \endcode
2714+
class MissingExplicitExistentialCoercion final : public FailureDiagnostic {
2715+
Type ErasedResultType;
2716+
2717+
public:
2718+
MissingExplicitExistentialCoercion(const Solution &solution,
2719+
Type erasedResultTy,
2720+
ConstraintLocator *locator)
2721+
: FailureDiagnostic(solution, locator),
2722+
ErasedResultType(resolveType(erasedResultTy)) {}
2723+
2724+
SourceRange getSourceRange() const override {
2725+
auto rawAnchor = getRawAnchor();
2726+
return {rawAnchor.getStartLoc(), rawAnchor.getEndLoc()};
2727+
}
2728+
2729+
bool diagnoseAsError() override;
2730+
bool diagnoseAsNote() override;
2731+
2732+
private:
2733+
void fixIt(InFlightDiagnostic &diagnostic) const;
2734+
2735+
/// Determine whether the fix-it to add `as any ...` requires parens.
2736+
///
2737+
/// Parens are required to avoid suppressing existential opening
2738+
/// if result of the call is passed as an argument to another call
2739+
/// that requires such opening.
2740+
bool fixItRequiresParens() const;
2741+
};
2742+
26962743
} // end namespace constraints
26972744
} // end namespace swift
26982745

0 commit comments

Comments
 (0)