Skip to content

Commit 689e9e3

Browse files
authored
Merge pull request #42559 from xedin/se-0352-as-any-coercion
[TypeChecker] SE-0352: Require coercion if result type contains existential(s) that would loose generic requirements
2 parents ab08af4 + 8eba890 commit 689e9e3

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
@@ -6330,5 +6330,19 @@ ERROR(attr_incompatible_with_back_deploy,none,
63306330
"'%0' cannot be applied to a back deployed %1",
63316331
(DeclAttribute, DescriptiveDeclKind))
63326332

6333+
//------------------------------------------------------------------------------
6334+
// MARK: Implicit opening of existential types
6335+
//------------------------------------------------------------------------------
6336+
6337+
ERROR(result_requires_explicit_coercion,none,
6338+
"inferred result type %0 requires explicit coercion due to "
6339+
"loss of generic requirements",
6340+
(Type))
6341+
6342+
NOTE(candidate_result_requires_explicit_coercion,none,
6343+
"inferred result type %0 requires explicit coercion due to "
6344+
"loss of generic requirements",
6345+
(Type))
6346+
63336347
#define UNDEFINE_DIAGNOSTIC_MACROS
63346348
#include "DefineDiagnosticMacros.h"

include/swift/Sema/CSFix.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,10 @@ enum class FixKind : uint8_t {
394394
/// Ignore a type mismatch while trying to infer generic parameter type
395395
/// from default expression.
396396
IgnoreDefaultExprTypeMismatch,
397+
398+
/// Coerce a result type of a call to a particular existential type
399+
/// by adding `as any <#Type#>`.
400+
AddExplicitExistentialCoercion,
397401
};
398402

399403
class ConstraintFix {
@@ -2981,6 +2985,37 @@ class IgnoreDefaultExprTypeMismatch : public AllowArgumentMismatch {
29812985
}
29822986
};
29832987

2988+
class AddExplicitExistentialCoercion final : public ConstraintFix {
2989+
Type ErasedResultType;
2990+
2991+
AddExplicitExistentialCoercion(ConstraintSystem &cs, Type erasedResultTy,
2992+
ConstraintLocator *locator)
2993+
: ConstraintFix(cs, FixKind::AddExplicitExistentialCoercion, locator),
2994+
ErasedResultType(erasedResultTy) {}
2995+
2996+
public:
2997+
std::string getName() const override {
2998+
return "add explicit existential type coercion";
2999+
}
3000+
3001+
bool diagnose(const Solution &solution, bool asNote = false) const override;
3002+
3003+
static bool
3004+
isRequired(ConstraintSystem &cs, Type resultTy,
3005+
ArrayRef<std::pair<TypeVariableType *, OpenedArchetypeType *>>
3006+
openedExistentials,
3007+
ConstraintLocatorBuilder locator);
3008+
3009+
static bool isRequired(ConstraintSystem &cs, Type resultTy,
3010+
llvm::function_ref<Optional<Type>(TypeVariableType *)>
3011+
findExistentialType,
3012+
ConstraintLocatorBuilder locator);
3013+
3014+
static AddExplicitExistentialCoercion *create(ConstraintSystem &cs,
3015+
Type resultTy,
3016+
ConstraintLocator *locator);
3017+
};
3018+
29843019
} // end namespace constraints
29853020
} // end namespace swift
29863021

lib/Sema/CSDiagnostics.cpp

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

81458145
return true;
81468146
}
8147+
8148+
bool MissingExplicitExistentialCoercion::diagnoseAsError() {
8149+
auto diagnostic = emitDiagnostic(diag::result_requires_explicit_coercion,
8150+
ErasedResultType);
8151+
fixIt(diagnostic);
8152+
return true;
8153+
}
8154+
8155+
bool MissingExplicitExistentialCoercion::diagnoseAsNote() {
8156+
auto diagnostic = emitDiagnostic(
8157+
diag::candidate_result_requires_explicit_coercion, ErasedResultType);
8158+
fixIt(diagnostic);
8159+
return true;
8160+
}
8161+
8162+
bool MissingExplicitExistentialCoercion::fixItRequiresParens() const {
8163+
auto anchor = getAsExpr(getRawAnchor());
8164+
8165+
// If it's a member reference an an existential metatype, let's
8166+
// use the parent "call" expression.
8167+
if (auto *UDE = dyn_cast_or_null<UnresolvedDotExpr>(anchor))
8168+
anchor = findParentExpr(UDE);
8169+
8170+
if (!anchor)
8171+
return false;
8172+
8173+
const auto &solution = getSolution();
8174+
return llvm::any_of(
8175+
solution.OpenedExistentialTypes,
8176+
[&anchor](const auto &openedExistential) {
8177+
if (auto openedLoc = simplifyLocatorToAnchor(openedExistential.first)) {
8178+
return anchor == getAsExpr(openedLoc);
8179+
}
8180+
return false;
8181+
});
8182+
}
8183+
8184+
void MissingExplicitExistentialCoercion::fixIt(
8185+
InFlightDiagnostic &diagnostic) const {
8186+
bool requiresParens = fixItRequiresParens();
8187+
8188+
auto callRange = getSourceRange();
8189+
8190+
if (requiresParens)
8191+
diagnostic.fixItInsert(callRange.Start, "(");
8192+
8193+
auto printOpts = PrintOptions::forDiagnosticArguments();
8194+
diagnostic.fixItInsertAfter(callRange.End,
8195+
"as " + ErasedResultType->getString(printOpts) +
8196+
(requiresParens ? ")" : ""));
8197+
}

lib/Sema/CSDiagnostics.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2717,6 +2717,53 @@ 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+
/// Determine whether the fix-it to add `as any ...` requires parens.
2760+
///
2761+
/// Parens are required to avoid suppressing existential opening
2762+
/// if result of the call is passed as an argument to another call
2763+
/// that requires such opening.
2764+
bool fixItRequiresParens() const;
2765+
};
2766+
27202767
} // end namespace constraints
27212768
} // end namespace swift
27222769

0 commit comments

Comments
 (0)