Skip to content

Commit 46df852

Browse files
committed
[Sema] Reject placeholders in type resolution for param and result types
Not all clients can properly handle the presence of placeholders in interface types and it doesn't seem worth the complexity for the type replacement diagnostic.
1 parent b39dfdf commit 46df852

File tree

9 files changed

+46
-201
lines changed

9 files changed

+46
-201
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4863,10 +4863,6 @@ NOTE(descriptive_generic_type_declared_here,none,
48634863

48644864
ERROR(placeholder_type_not_allowed,none,
48654865
"type placeholder not allowed here", ())
4866-
ERROR(placeholder_type_not_allowed_in_return_type,none,
4867-
"type placeholder may not appear in function return type", ())
4868-
ERROR(placeholder_type_not_allowed_in_parameter,none,
4869-
"type placeholder may not appear in top-level parameter", ())
48704866
ERROR(placeholder_type_not_allowed_in_pattern,none,
48714867
"placeholder type may not appear as type of a variable", ())
48724868
NOTE(replace_placeholder_with_inferred_type,none,

lib/AST/TypeCheckRequests.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,6 +1227,11 @@ void InterfaceTypeRequest::cacheResult(Type type) const {
12271227
ASSERT(!type->hasPrimaryArchetype() && "Archetype in interface type");
12281228
ASSERT(decl->getDeclContext()->isLocalContext() || !type->hasLocalArchetype() &&
12291229
"Local archetype in interface type of non-local declaration");
1230+
// Placeholders are only permitted in closure parameters.
1231+
if (!(isa<ParamDecl>(decl) &&
1232+
isa<AbstractClosureExpr>(decl->getDeclContext()))) {
1233+
ASSERT(!type->hasPlaceholder() && "Placeholder in interface type");
1234+
}
12301235
}
12311236
decl->TypeAndAccess.setPointer(type);
12321237
}

lib/Sema/MiscDiagnostics.cpp

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -3868,71 +3868,6 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
38683868
}
38693869
};
38703870

3871-
class ReturnTypePlaceholderReplacer : public ASTWalker {
3872-
FuncDecl *Implementation;
3873-
BraceStmt *Body;
3874-
SmallVector<Type, 4> Candidates;
3875-
3876-
bool HasInvalidReturn = false;
3877-
3878-
public:
3879-
ReturnTypePlaceholderReplacer(FuncDecl *Implementation, BraceStmt *Body)
3880-
: Implementation(Implementation), Body(Body) {}
3881-
3882-
void check() {
3883-
auto *resultRepr = Implementation->getResultTypeRepr();
3884-
if (!resultRepr) {
3885-
return;
3886-
}
3887-
3888-
Implementation->getASTContext()
3889-
.Diags
3890-
.diagnose(resultRepr->getLoc(),
3891-
diag::placeholder_type_not_allowed_in_return_type)
3892-
.highlight(resultRepr->getSourceRange());
3893-
3894-
Body->walk(*this);
3895-
3896-
// If given function has any invalid returns in the body
3897-
// let's not try to validate the types, since it wouldn't
3898-
// be accurate.
3899-
if (HasInvalidReturn)
3900-
return;
3901-
3902-
auto writtenType = Implementation->getResultInterfaceType();
3903-
llvm::SmallPtrSet<TypeBase *, 8> seenTypes;
3904-
for (auto candidate : Candidates) {
3905-
if (!seenTypes.insert(candidate.getPointer()).second) {
3906-
continue;
3907-
}
3908-
TypeChecker::notePlaceholderReplacementTypes(writtenType, candidate);
3909-
}
3910-
}
3911-
3912-
MacroWalking getMacroWalkingBehavior() const override {
3913-
return MacroWalking::ArgumentsAndExpansion;
3914-
}
3915-
3916-
PreWalkResult<Expr *> walkToExprPre(Expr *E) override { return Action::Continue(E); }
3917-
3918-
PreWalkResult<Stmt *> walkToStmtPre(Stmt *S) override {
3919-
if (auto *RS = dyn_cast<ReturnStmt>(S)) {
3920-
if (RS->hasResult()) {
3921-
auto resultTy = RS->getResult()->getType();
3922-
HasInvalidReturn |= resultTy.isNull() || resultTy->hasError();
3923-
Candidates.push_back(resultTy);
3924-
}
3925-
}
3926-
3927-
return Action::Continue(S);
3928-
}
3929-
3930-
// Don't descend into nested decls.
3931-
PreWalkAction walkToDeclPre(Decl *D) override {
3932-
return Action::SkipNode();
3933-
}
3934-
};
3935-
39363871
} // end anonymous namespace
39373872

39383873
SourceLoc swift::getFixItLocForVarToLet(VarDecl *var) {
@@ -4545,13 +4480,6 @@ void swift::performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD) {
45454480
OpaqueUnderlyingTypeChecker(AFD, opaqueResultTy, body).check();
45464481
}
45474482
}
4548-
} else if (auto *FD = dyn_cast<FuncDecl>(AFD)) {
4549-
auto resultIFaceTy = FD->getResultInterfaceType();
4550-
// If the result has a placeholder, we need to try to use the contextual
4551-
// type inferred in the body to replace it.
4552-
if (resultIFaceTy && resultIFaceTy->hasPlaceholder()) {
4553-
ReturnTypePlaceholderReplacer(FD, body).check();
4554-
}
45554483
}
45564484
}
45574485

lib/Sema/TypeCheckDecl.cpp

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2121,17 +2121,10 @@ ResultTypeRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
21212121
if (decl->preconcurrency())
21222122
options |= TypeResolutionFlags::Preconcurrency;
21232123

2124-
// Placeholders are only currently allowed for FuncDecls with bodies, which
2125-
// we diagnose in ReturnTypePlaceholderReplacer.
2126-
HandlePlaceholderTypeReprFn placeholderOpener;
2127-
if (auto *FD = dyn_cast<FuncDecl>(decl)) {
2128-
if (FD->hasBody() && !FD->isBodySkipped())
2129-
placeholderOpener = PlaceholderType::get;
2130-
}
21312124
auto *const dc = decl->getInnermostDeclContext();
21322125
return TypeResolution::forInterface(dc, options,
21332126
/*unboundTyOpener*/ nullptr,
2134-
placeholderOpener,
2127+
/*placeholderOpener*/ nullptr,
21352128
/*packElementOpener*/ nullptr)
21362129
.resolveType(resultTyRepr);
21372130
}
@@ -2259,6 +2252,7 @@ static Type validateParameterType(ParamDecl *decl) {
22592252

22602253
TypeResolutionOptions options(std::nullopt);
22612254
OpenUnboundGenericTypeFn unboundTyOpener = nullptr;
2255+
HandlePlaceholderTypeReprFn placeholderOpener = nullptr;
22622256
if (isa<AbstractClosureExpr>(dc)) {
22632257
options = TypeResolutionOptions(TypeResolverContext::ClosureExpr);
22642258
options |= TypeResolutionFlags::AllowUnspecifiedTypes;
@@ -2267,8 +2261,9 @@ static Type validateParameterType(ParamDecl *decl) {
22672261
// For now, just return the unbound generic type.
22682262
return unboundTy;
22692263
};
2270-
// FIXME: Don't let placeholder types escape type resolution.
2271-
// For now, just return the placeholder type.
2264+
// FIXME: Don't let placeholder types escape type resolution. For now, just
2265+
// return the placeholder type, which we open in `inferClosureType`.
2266+
placeholderOpener = PlaceholderType::get;
22722267
} else if (isa<AbstractFunctionDecl>(dc)) {
22732268
options = TypeResolutionOptions(TypeResolverContext::AbstractFunctionDecl);
22742269
} else if (isa<SubscriptDecl>(dc)) {
@@ -2317,14 +2312,6 @@ static Type validateParameterType(ParamDecl *decl) {
23172312
: TypeResolverContext::FunctionInput);
23182313
options |= TypeResolutionFlags::Direct;
23192314

2320-
// We allow placeholders in parameter types to improve recovery since if a
2321-
// default argument is present we can suggest the inferred type. Avoid doing
2322-
// this for protocol requirements though since those can't ever have default
2323-
// arguments anyway.
2324-
HandlePlaceholderTypeReprFn placeholderOpener;
2325-
if (!isa<ProtocolDecl>(dc->getParent()))
2326-
placeholderOpener = PlaceholderType::get;
2327-
23282315
const auto resolution =
23292316
TypeResolution::forInterface(dc, options, unboundTyOpener,
23302317
placeholderOpener,

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 2 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,73 +1193,18 @@ static bool checkExpressionMacroDefaultValueRestrictions(ParamDecl *param) {
11931193
#endif
11941194
}
11951195

1196-
void TypeChecker::notePlaceholderReplacementTypes(Type writtenType,
1197-
Type inferredType) {
1198-
assert(writtenType && inferredType &&
1199-
"Must provide both written and inferred types");
1200-
assert(writtenType->hasPlaceholder() && "Written type has no placeholder?");
1201-
1202-
class PlaceholderNotator
1203-
: public CanTypeDifferenceVisitor<PlaceholderNotator> {
1204-
public:
1205-
bool visitDifferentComponentTypes(CanType t1, CanType t2) {
1206-
// Never replace anything the user wrote with an error type.
1207-
if (t2->hasError() || t2->hasUnresolvedType()) {
1208-
return false;
1209-
}
1210-
1211-
auto *placeholder = t1->getAs<PlaceholderType>();
1212-
if (!placeholder) {
1213-
return false;
1214-
}
1215-
1216-
if (auto *origRepr =
1217-
placeholder->getOriginator().dyn_cast<TypeRepr *>()) {
1218-
assert(isa<PlaceholderTypeRepr>(origRepr));
1219-
t1->getASTContext()
1220-
.Diags
1221-
.diagnose(origRepr->getLoc(),
1222-
diag::replace_placeholder_with_inferred_type, t2)
1223-
.fixItReplace(origRepr->getSourceRange(), t2.getString());
1224-
}
1225-
return false;
1226-
}
1227-
1228-
bool check(Type t1, Type t2) {
1229-
return !visit(t1->getCanonicalType(), t2->getCanonicalType());
1230-
};
1231-
};
1232-
1233-
PlaceholderNotator().check(writtenType, inferredType);
1234-
}
1235-
12361196
/// Check the default arguments that occur within this parameter list.
12371197
static void checkDefaultArguments(ParameterList *params) {
12381198
// Force the default values in case they produce diagnostics.
12391199
for (auto *param : *params) {
1240-
auto ifacety = param->getInterfaceType();
1241-
auto *expr = param->getTypeCheckedDefaultExpr();
1200+
(void)param->getTypeCheckedDefaultExpr();
12421201

12431202
// Force captures since this can emit diagnostics.
1244-
(void) param->getDefaultArgumentCaptureInfo();
1203+
(void)param->getDefaultArgumentCaptureInfo();
12451204

12461205
// If the default argument has isolation, it must match the
12471206
// isolation of the decl context.
12481207
(void)param->getInitializerIsolation();
1249-
1250-
if (!ifacety->hasPlaceholder()) {
1251-
continue;
1252-
}
1253-
1254-
// Placeholder types are banned for all parameter decls. We try to use the
1255-
// freshly-checked default expression's contextual type to suggest a
1256-
// reasonable type to insert.
1257-
param->diagnose(diag::placeholder_type_not_allowed_in_parameter)
1258-
.highlight(param->getSourceRange());
1259-
if (expr && !expr->getType()->hasError()) {
1260-
TypeChecker::notePlaceholderReplacementTypes(
1261-
ifacety, expr->getType()->mapTypeOutOfContext());
1262-
}
12631208
}
12641209
}
12651210

lib/Sema/TypeChecker.h

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,24 +1238,6 @@ bool diagnoseInvalidFunctionType(FunctionType *fnTy, SourceLoc loc,
12381238
std::optional<FunctionTypeRepr *> repr,
12391239
DeclContext *dc,
12401240
std::optional<TypeResolutionStage> stage);
1241-
1242-
/// Walk the parallel structure of a type with user-provided placeholders and
1243-
/// an inferred type produced by the type checker. Where placeholders can be
1244-
/// found, suggest the corresponding inferred type.
1245-
///
1246-
/// For example,
1247-
///
1248-
/// \code
1249-
/// func foo(_ x: [_] = [0])
1250-
/// \endcode
1251-
///
1252-
/// Has a written type of `(ArraySlice (Placeholder))` and an inferred type of
1253-
/// `(ArraySlice Int)`, so we walk to `Placeholder` and `Int` in each type and
1254-
/// suggest replacing `_` with `Int`.
1255-
///
1256-
/// \param writtenType The interface type usually derived from a user-written
1257-
/// type repr. \param inferredType The type inferred by the type checker.
1258-
void notePlaceholderReplacementTypes(Type writtenType, Type inferredType);
12591241
} // namespace TypeChecker
12601242

12611243
/// Returns the protocol requirement kind of the given declaration.

test/Sema/placeholder_type.swift

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ let dict2: [Character: _] = ["h": 0]
1010

1111
let arr = [_](repeating: "hi", count: 3)
1212

13-
func foo(_ arr: [_] = [0]) {} // expected-error {{type placeholder may not appear in top-level parameter}}
14-
// expected-note@-1 {{replace the placeholder with the inferred type 'Int'}}
13+
func foo(_ arr: [_] = [0]) {} // expected-error {{type placeholder not allowed here}}
1514

1615
let foo = _.foo // expected-error {{type placeholder not allowed here}}
1716
let zero: _ = .zero // expected-error {{cannot infer contextual base in reference to member 'zero'}}
@@ -78,33 +77,25 @@ where T: ExpressibleByIntegerLiteral, U: ExpressibleByIntegerLiteral {
7877
}
7978

8079
extension Bar {
81-
func frobnicate2() -> Bar<_, _> { // expected-error {{type placeholder may not appear in function return type}}
82-
// expected-note@-1 {{replace the placeholder with the inferred type 'T'}}
83-
// expected-note@-2 {{replace the placeholder with the inferred type 'U'}}
80+
func frobnicate2() -> Bar<_, _> { // expected-error {{type placeholder not allowed here}}
8481
return Bar(t: 42, u: 42)
8582
}
8683
func frobnicate3() -> Bar {
8784
return Bar<_, _>(t: 42, u: 42)
8885
}
89-
func frobnicate4() -> Bar<_, _> { // expected-error {{type placeholder may not appear in function return type}}
90-
// expected-note@-1 {{replace the placeholder with the inferred type 'Int'}}
91-
// expected-note@-2 {{replace the placeholder with the inferred type 'Int'}}
86+
func frobnicate4() -> Bar<_, _> { // expected-error {{type placeholder not allowed here}}
9287
return Bar<_, _>(t: 42, u: 42)
9388
}
94-
func frobnicate5() -> Bar<_, U> { // expected-error {{type placeholder may not appear in function return type}}
95-
// expected-note@-1 {{replace the placeholder with the inferred type 'T'}}
89+
func frobnicate5() -> Bar<_, U> { // expected-error {{type placeholder not allowed here}}
9690
return Bar(t: 42, u: 42)
9791
}
9892
func frobnicate6() -> Bar {
9993
return Bar<_, U>(t: 42, u: 42)
10094
}
101-
func frobnicate7() -> Bar<_, _> { // expected-error {{type placeholder may not appear in function return type}}
102-
// expected-note@-1 {{replace the placeholder with the inferred type 'Int'}}
103-
// expected-note@-2 {{replace the placeholder with the inferred type 'U'}}
95+
func frobnicate7() -> Bar<_, _> { // expected-error {{type placeholder not allowed here}}
10496
return Bar<_, U>(t: 42, u: 42)
10597
}
106-
func frobnicate8() -> Bar<_, U> { // expected-error {{type placeholder may not appear in function return type}}
107-
// expected-note@-1 {{replace the placeholder with the inferred type 'Int'}}
98+
func frobnicate8() -> Bar<_, U> { // expected-error {{type placeholder not allowed here}}
10899
return Bar<_, _>(t: 42, u: 42)
109100
}
110101
}
@@ -244,11 +235,11 @@ let _: SetFailureType<Int, String> = Just<Int>().setFailureType(to: _.self).setF
244235
// diagnostic.
245236
func mismatchedDefault<T>(_ x: [_] = [String: T]()) {} // expected-error {{type placeholder not allowed here}}
246237

247-
func mismatchedReturnTypes() -> _ { // expected-error {{type placeholder may not appear in function return type}}
238+
func mismatchedReturnTypes() -> _ { // expected-error {{type placeholder not allowed here}}
248239
if true {
249-
return "" // expected-note@-2 {{replace the placeholder with the inferred type 'String'}}
240+
return ""
250241
} else {
251-
return 0.5 // expected-note@-4 {{replace the placeholder with the inferred type 'Double'}}
242+
return 0.5
252243
}
253244
}
254245

@@ -261,9 +252,8 @@ func opaque() -> some _ { // expected-error {{type placeholder not allowed here}
261252
}
262253

263254
enum EnumWithPlaceholders {
264-
case topLevelPlaceholder(x: _) // expected-error {{type placeholder may not appear in top-level parameter}}
265-
case placeholderWithDefault(x: _ = 5) // expected-error {{type placeholder may not appear in top-level parameter}}
266-
// expected-note@-1 {{replace the placeholder with the inferred type 'Int'}}
255+
case topLevelPlaceholder(x: _) // expected-error {{type placeholder not allowed here}}
256+
case placeholderWithDefault(x: _ = 5) // expected-error {{type placeholder not allowed here}}
267257
}
268258

269259
func deferredInit(_ c: Bool) {
@@ -303,30 +293,29 @@ protocol TestPlaceholderRequirement {
303293
subscript() -> _ { get } // expected-error {{type placeholder not allowed here}}
304294
}
305295

306-
func testPlaceholderFn1(_:_) {} // expected-error {{type placeholder may not appear in top-level parameter}}
307-
func testPlaceholderFn2() -> _ {} // expected-error {{type placeholder may not appear in function return type}}
296+
func testPlaceholderFn1(_:_) {} // expected-error {{type placeholder not allowed here}}
297+
func testPlaceholderFn2() -> _ {} // expected-error {{type placeholder not allowed here}}
308298

309299
var testPlaceholderComputed1: _ { 0 } // expected-error {{type placeholder not allowed here}}
310300
var testPlaceholderComputed2: [_] { [0] } // expected-error {{type placeholder not allowed here}}
311301

312302
struct TestPlaceholderSubscript {
313-
// FIXME: Shouldn't diagnose twice.
314-
subscript(_: _) -> Void { () } // expected-error 2{{type placeholder may not appear in top-level parameter}}
315-
subscript(_: [_]) -> Void { () } // expected-error 2{{type placeholder may not appear in top-level parameter}}
303+
subscript(_: _) -> Void { () } // expected-error {{type placeholder not allowed here}}
304+
subscript(_: [_]) -> Void { () } // expected-error {{type placeholder not allowed here}}
316305
subscript() -> _ { () } // expected-error {{type placeholder not allowed here}}
317306
subscript() -> [_] { [0] } // expected-error {{type placeholder not allowed here}}
318307
}
319308

320309
enum TestPlaceholderInEnumElement {
321-
case a(_) // expected-error {{type placeholder may not appear in top-level parameter}}
322-
case b([_]) // expected-error {{type placeholder may not appear in top-level parameter}}
310+
case a(_) // expected-error {{type placeholder not allowed here}}
311+
case b([_]) // expected-error {{type placeholder not allowed here}}
323312
}
324313

325314
@freestanding(expression) macro testPlaceholderMacro(_ x: _) -> String = #file
326-
// expected-error@-1 {{type placeholder may not appear in top-level parameter}}
315+
// expected-error@-1 {{type placeholder not allowed here}}
327316

328317
@freestanding(expression) macro testPlaceholderMacro(_ x: [_]) -> String = #file
329-
// expected-error@-1 {{type placeholder may not appear in top-level parameter}}
318+
// expected-error@-1 {{type placeholder not allowed here}}
330319

331320
@freestanding(expression) macro testPlaceholderMacro() -> _ = #file
332321
// expected-error@-1 {{type placeholder not allowed here}}
@@ -351,8 +340,10 @@ func usePlaceholderDecls(
351340
_ = TestPlaceholderInEnumElement.a(0)
352341
_ = TestPlaceholderInEnumElement.b([])
353342

354-
_ = #testPlaceholderMacro(0)
355-
_ = #testPlaceholderMacro([])
343+
// There are marked invalid so get removed from the overload set.
344+
// FIXME: We ought to turn them into holes instead
345+
_ = #testPlaceholderMacro(0) // expected-error {{no macro named 'testPlaceholderMacro'}}
346+
_ = #testPlaceholderMacro([]) // expected-error {{no macro named 'testPlaceholderMacro'}}
356347

357348
_ = testPlaceholderFn1(0)
358349
_ = testPlaceholderFn2()
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// {"kind":"typecheck","original":"1bb3e6fd","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+
@propertyWrapper struct a<b {
4+
wrappedValue: b
5+
}
6+
func c(@a _ )

0 commit comments

Comments
 (0)