Skip to content

Commit 8102e39

Browse files
committed
[CS] Record fix when encountering decl reference with placeholder type
We allow placeholder types in interface types in certain cases to allow better recovery since we can suggest the inferred type as a replacement. When referencing those decls though we need to make sure we record a fix since we cannot form a valid solution with them.
1 parent ab64e53 commit 8102e39

File tree

8 files changed

+104
-2
lines changed

8 files changed

+104
-2
lines changed

include/swift/Sema/CSFix.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,9 @@ enum class FixKind : uint8_t {
362362
/// resolved.
363363
SpecifyTypeForPlaceholder,
364364

365+
/// Ignore an invalid placeholder in a decl's interface type.
366+
IgnoreInvalidPlaceholderInDeclRef,
367+
365368
/// Allow Swift -> C pointer conversion in an argument position
366369
/// of a Swift function.
367370
AllowSwiftToCPointerConversion,
@@ -3191,6 +3194,30 @@ class SpecifyTypeForPlaceholder final : public ConstraintFix {
31913194
}
31923195
};
31933196

3197+
class IgnoreInvalidPlaceholderInDeclRef final : public ConstraintFix {
3198+
IgnoreInvalidPlaceholderInDeclRef(ConstraintSystem &cs,
3199+
ConstraintLocator *locator)
3200+
: ConstraintFix(cs, FixKind::IgnoreInvalidPlaceholderInDeclRef, locator) {}
3201+
3202+
public:
3203+
std::string getName() const override {
3204+
return "ignore invalid placeholder in decl ref";
3205+
}
3206+
3207+
bool diagnose(const Solution &solution, bool asNote = false) const override;
3208+
3209+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
3210+
return diagnose(*commonFixes.front().first);
3211+
}
3212+
3213+
static IgnoreInvalidPlaceholderInDeclRef *create(ConstraintSystem &cs,
3214+
ConstraintLocator *locator);
3215+
3216+
static bool classof(const ConstraintFix *fix) {
3217+
return fix->getKind() == FixKind::IgnoreInvalidPlaceholderInDeclRef;
3218+
}
3219+
};
3220+
31943221
class AllowRefToInvalidDecl final : public ConstraintFix {
31953222
AllowRefToInvalidDecl(ConstraintSystem &cs, ConstraintLocator *locator)
31963223
: ConstraintFix(cs, FixKind::AllowRefToInvalidDecl, locator) {}

lib/Sema/CSFix.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2264,6 +2264,20 @@ SpecifyTypeForPlaceholder::create(ConstraintSystem &cs,
22642264
return new (cs.getAllocator()) SpecifyTypeForPlaceholder(cs, locator);
22652265
}
22662266

2267+
IgnoreInvalidPlaceholderInDeclRef *
2268+
IgnoreInvalidPlaceholderInDeclRef::create(ConstraintSystem &cs, ConstraintLocator *locator) {
2269+
return new (cs.getAllocator()) IgnoreInvalidPlaceholderInDeclRef(cs, locator);
2270+
}
2271+
2272+
bool
2273+
IgnoreInvalidPlaceholderInDeclRef::diagnose(const Solution &solution,
2274+
bool asNote) const {
2275+
// These are diagnosed separately. Unfortunately we can't enforce that a
2276+
// diagnostic has already been emitted since their diagnosis depends on e.g
2277+
// type-checking a function body for a placeholder result of a function.
2278+
return true;
2279+
}
2280+
22672281
bool AllowRefToInvalidDecl::diagnose(const Solution &solution,
22682282
bool asNote) const {
22692283
ReferenceToInvalidDeclaration failure(solution, getLocator());

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16109,6 +16109,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1610916109
case FixKind::AllowValueExpansionWithoutPackReferences:
1611016110
case FixKind::IgnoreInvalidPatternInExpr:
1611116111
case FixKind::IgnoreInvalidPlaceholder:
16112+
case FixKind::IgnoreInvalidPlaceholderInDeclRef:
1611216113
case FixKind::IgnoreOutOfPlaceThenStmt:
1611316114
case FixKind::IgnoreMissingEachKeyword:
1611416115
case FixKind::AllowInlineArrayLiteralCountMismatch:

lib/Sema/TypeOfReference.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,13 +1080,39 @@ static Type replaceParamErrorTypeByPlaceholder(Type type, ValueDecl *value, bool
10801080
funcType->getExtInfo());
10811081
}
10821082

1083+
/// We allow placeholder types in interface types in certain cases to allow
1084+
/// better recovery since we can suggest the inferred type as a replacement.
1085+
/// When referencing those decls though we need to make sure we record a fix
1086+
/// since we cannot form a valid solution with them.
1087+
static void
1088+
recordFixIfNeededForPlaceholderInDecl(ConstraintSystem &cs, ValueDecl *D,
1089+
ConstraintLocatorBuilder locator) {
1090+
auto mayHavePlaceholder = [&]() -> bool {
1091+
// Parameters with types may have placeholders, since this allows us to
1092+
// suggest an inferred type from a default argument. Match the logic in
1093+
// `getUnopenedTypeOfReference` and only query the interface type if we have
1094+
// one already.
1095+
if (auto *PD = dyn_cast<ParamDecl>(D))
1096+
return PD->hasInterfaceType();
1097+
1098+
// Therefore decls with parameter lists may also have placeholders.
1099+
return D->getParameterList();
1100+
};
1101+
if (!mayHavePlaceholder() || !D->getInterfaceType()->hasPlaceholder())
1102+
return;
1103+
1104+
auto *loc = cs.getConstraintLocator(locator);
1105+
cs.recordFix(IgnoreInvalidPlaceholderInDeclRef::create(cs, loc));
1106+
}
1107+
10831108
DeclReferenceType
10841109
ConstraintSystem::getTypeOfReference(ValueDecl *value,
10851110
FunctionRefInfo functionRefInfo,
10861111
ConstraintLocatorBuilder locator,
10871112
DeclContext *useDC,
10881113
PreparedOverloadBuilder *preparedOverload) {
10891114
ASSERT(!!preparedOverload == PreparingOverload);
1115+
recordFixIfNeededForPlaceholderInDecl(*this, value, locator);
10901116

10911117
if (value->getDeclContext()->isTypeContext() && isa<FuncDecl>(value)) {
10921118
// Unqualified lookup can find operator names within nominal types.
@@ -1777,6 +1803,7 @@ DeclReferenceType ConstraintSystem::getTypeOfMemberReference(
17771803
SmallVectorImpl<OpenedType> *replacementsPtr,
17781804
PreparedOverloadBuilder *preparedOverload) {
17791805
ASSERT(!!preparedOverload == PreparingOverload);
1806+
recordFixIfNeededForPlaceholderInDecl(*this, value, locator);
17801807

17811808
// Figure out the instance type used for the base.
17821809
Type baseRValueTy = baseTy->getRValueType();

test/Sema/placeholder_type.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,30 @@ enum TestPlaceholderInEnumElement {
330330

331331
@freestanding(expression) macro testPlaceholderMacro() -> _ = #file
332332
// expected-error@-1 {{type placeholder not allowed here}}
333+
334+
// Make sure we can use decls with placeholders in their interface type.
335+
func usePlaceholderDecls(
336+
_ fromProto: some TestPlaceholderRequirement, _ hasSubscript: TestPlaceholderSubscript
337+
) {
338+
_ = optInt
339+
340+
_ = testPlaceholderComputed1
341+
_ = testPlaceholderComputed2
342+
343+
fromProto.foo(0)
344+
_ = fromProto.bar()
345+
_ = fromProto.baz()
346+
fromProto.qux([])
347+
fromProto[0]
348+
349+
_ = hasSubscript[0]
350+
351+
_ = TestPlaceholderInEnumElement.a(0)
352+
_ = TestPlaceholderInEnumElement.b([])
353+
354+
_ = #testPlaceholderMacro(0)
355+
_ = #testPlaceholderMacro([])
356+
357+
_ = testPlaceholderFn1(0)
358+
_ = testPlaceholderFn2()
359+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
// {"kind":"typecheck","signature":"(anonymous namespace)::ExprRewriter::finishApply(swift::ApplyExpr*, swift::Type, swift::constraints::ConstraintLocatorBuilder, swift::constraints::ConstraintLocatorBuilder)","signatureAssert":"Assertion failed: (isa<To>(Val) && \"cast<Ty>() argument of incompatible type!\"), function cast"}
2-
// RUN: not --crash %target-swift-frontend -typecheck %s
2+
// RUN: not %target-swift-frontend -typecheck %s
33
func a(b : ()->Void c : _->Void) { withoutActuallyEscaping(b do : c
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// {"kind":"typecheck","signature":"(anonymous namespace)::ExprRewriter::coerceToType(swift::Expr*, swift::Type, swift::constraints::ConstraintLocatorBuilder)::$_3::operator()(swift::Type, swift::Type) const","signatureAssert":"Assertion failed: (restriction == ConversionRestrictionKind::DeepEquality), function operator()"}
2-
// RUN: not --crash %target-swift-frontend -typecheck %s
2+
// RUN: not %target-swift-frontend -typecheck %s
33
func a(inout _ )
44
var b = String
55
a(b
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// {"kind":"typecheck","original":"0e81571d","signature":"swift::constraints::ConstraintSystem::applySolution(swift::constraints::Solution&, swift::constraints::SyntacticElementTarget)","signatureAssert":"Assertion failed: (isValidType(type) && \"type binding has invalid type\"), function applySolution"}
2+
// RUN: not %target-swift-frontend -typecheck %s
3+
subscript(a: _) -> <#type#> {
4+
a
5+
0
6+
}

0 commit comments

Comments
 (0)