Skip to content

Commit 0e70841

Browse files
committed
[Diagnostics] Diagnose returning existential values that lose requirements
Diagnose situations where inferring existential type for result of a call would result in loss of generic requirements. ```swift protocol P { associatedtype A } protocol Q { associatedtype B: P where B.A == Int } func getB<T: Q>(_: T) -> T.B { ... } func test(v: any Q) { let _ = getB(v) // <- produces `any P` which looses A == Int } ```
1 parent 8306724 commit 0e70841

File tree

3 files changed

+86
-0
lines changed

3 files changed

+86
-0
lines changed

include/swift/AST/DiagnosticsSema.def

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

6328+
//------------------------------------------------------------------------------
6329+
// MARK: Implicit opening of existential types
6330+
//------------------------------------------------------------------------------
6331+
6332+
ERROR(result_requires_explicit_coercion,none,
6333+
"inferred result type %0 requires explicit coercion due to "
6334+
"loss of generic requirements",
6335+
(Type))
6336+
6337+
NOTE(candidate_result_requires_explicit_coercion,none,
6338+
"inferred result type %0 requires explicit coercion due to "
6339+
"loss of generic requirements",
6340+
(Type))
6341+
63286342
#define UNDEFINE_DIAGNOSTIC_MACROS
63296343
#include "DefineDiagnosticMacros.h"

lib/Sema/CSDiagnostics.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8143,3 +8143,35 @@ bool DefaultExprTypeMismatch::diagnoseAsError() {
81438143

81448144
return true;
81458145
}
8146+
8147+
bool MissingExplicitExistentialCoercion::diagnoseAsError() {
8148+
auto diagnostic = emitDiagnostic(diag::result_requires_explicit_coercion,
8149+
ErasedResultType);
8150+
fixIt(diagnostic);
8151+
return true;
8152+
}
8153+
8154+
bool MissingExplicitExistentialCoercion::diagnoseAsNote() {
8155+
auto selectedOverload = getCalleeOverloadChoiceIfAvailable(getLocator());
8156+
if (!selectedOverload)
8157+
return false;
8158+
8159+
const auto &choice = selectedOverload->choice;
8160+
8161+
if (auto *decl = choice.getDeclOrNull()) {
8162+
auto diagnostic = emitDiagnosticAt(
8163+
decl, diag::candidate_result_requires_explicit_coercion,
8164+
ErasedResultType);
8165+
fixIt(diagnostic);
8166+
return true;
8167+
}
8168+
8169+
return false;
8170+
}
8171+
8172+
void MissingExplicitExistentialCoercion::fixIt(
8173+
InFlightDiagnostic &diagnostic) const {
8174+
auto printOpts = PrintOptions::forDiagnosticArguments();
8175+
diagnostic.fixItInsertAfter(getSourceRange().End,
8176+
"as " + ErasedResultType->getString(printOpts));
8177+
}

lib/Sema/CSDiagnostics.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2717,6 +2717,46 @@ class DefaultExprTypeMismatch final : public ContextualFailure {
27172717
bool diagnoseAsError() override;
27182718
};
27192719

2720+
/// Diagnose situations where inferring existential type for result of
2721+
/// a call would result in loss of generic requirements.
2722+
///
2723+
/// \code
2724+
/// protocol P {
2725+
/// associatedtype A
2726+
/// }
2727+
///
2728+
/// protocol Q {
2729+
/// associatedtype B: P where B.A == Int
2730+
/// }
2731+
///
2732+
/// func getB<T: Q>(_: T) -> T.B { ... }
2733+
///
2734+
/// func test(v: any Q) {
2735+
/// let _ = getB(v) // <- produces `any P` which looses A == Int
2736+
/// }
2737+
/// \endcode
2738+
class MissingExplicitExistentialCoercion final : public FailureDiagnostic {
2739+
Type ErasedResultType;
2740+
2741+
public:
2742+
MissingExplicitExistentialCoercion(const Solution &solution,
2743+
Type erasedResultTy,
2744+
ConstraintLocator *locator)
2745+
: FailureDiagnostic(solution, locator),
2746+
ErasedResultType(resolveType(erasedResultTy)) {}
2747+
2748+
SourceRange getSourceRange() const override {
2749+
auto rawAnchor = getRawAnchor();
2750+
return {rawAnchor.getStartLoc(), rawAnchor.getEndLoc()};
2751+
}
2752+
2753+
bool diagnoseAsError() override;
2754+
bool diagnoseAsNote() override;
2755+
2756+
private:
2757+
void fixIt(InFlightDiagnostic &diagnostic) const;
2758+
};
2759+
27202760
} // end namespace constraints
27212761
} // end namespace swift
27222762

0 commit comments

Comments
 (0)