Skip to content

Commit 9134725

Browse files
authored
Merge pull request swiftlang#41163 from CodaFi/strikedown
Consistently Reject Top-Level Placeholder Types
2 parents b97795d + 74bb62b commit 9134725

File tree

9 files changed

+78
-28
lines changed

9 files changed

+78
-28
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3746,6 +3746,8 @@ ERROR(placeholder_type_not_allowed_in_return_type,none,
37463746
"type placeholder may not appear in function return type", ())
37473747
ERROR(placeholder_type_not_allowed_in_parameter,none,
37483748
"type placeholder may not appear in top-level parameter", ())
3749+
ERROR(placeholder_type_not_allowed_in_pattern,none,
3750+
"placeholder type may not appear as type of a variable", ())
37493751
NOTE(replace_placeholder_with_inferred_type,none,
37503752
"replace the placeholder with the inferred type %0", (Type))
37513753

include/swift/AST/TypeRepr.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,14 @@ class alignas(1 << TypeReprAlignInBits) TypeRepr
172172
/// `OpaqueReturnTypeRepr`s.
173173
CollectedOpaqueReprs collectOpaqueReturnTypeReprs();
174174

175+
/// Retrieve the type repr without any parentheses around it.
176+
///
177+
/// The use of this function must be restricted to contexts where
178+
/// user-written types are provided, and a syntactic analysis is appropriate.
179+
/// Most use cases should analyze the resolved \c Type instead and use
180+
/// \c Type::getCanonicalType() or \c Type::getWithoutParens().
181+
TypeRepr *getWithoutParens() const;
182+
175183
//*** Allocation Routines ************************************************/
176184

177185
void print(raw_ostream &OS, const PrintOptions &Opts = PrintOptions()) const;

lib/AST/TypeRepr.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ bool TypeRepr::hasOpaque() {
102102
findIf([](TypeRepr *ty) { return isa<OpaqueReturnTypeRepr>(ty); });
103103
}
104104

105+
TypeRepr *TypeRepr::getWithoutParens() const {
106+
auto *repr = const_cast<TypeRepr *>(this);
107+
while (auto *tupleRepr = dyn_cast<TupleTypeRepr>(repr)) {
108+
if (!tupleRepr->isParenType())
109+
break;
110+
repr = tupleRepr->getElementType(0);
111+
}
112+
return repr;
113+
}
114+
105115
CollectedOpaqueReprs TypeRepr::collectOpaqueReturnTypeReprs() {
106116
class Walker : public ASTWalker {
107117
CollectedOpaqueReprs &Reprs;

lib/Sema/CSGen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1364,7 +1364,7 @@ namespace {
13641364
return Type();
13651365
}
13661366
// Diagnose top-level usages of placeholder types.
1367-
if (isa<TopLevelCodeDecl>(CS.DC) && isa<PlaceholderTypeRepr>(repr)) {
1367+
if (isa<PlaceholderTypeRepr>(repr->getWithoutParens())) {
13681368
CS.getASTContext().Diags.diagnose(repr->getLoc(),
13691369
diag::placeholder_type_not_allowed);
13701370
}

lib/Sema/TypeCheckDecl.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2061,15 +2061,9 @@ ParamSpecifierRequest::evaluate(Evaluator &evaluator,
20612061
assert(typeRepr != nullptr && "Should call setSpecifier() on "
20622062
"synthesized parameter declarations");
20632063

2064-
auto *nestedRepr = typeRepr;
2065-
20662064
// Look through parens here; other than parens, specifiers
20672065
// must appear at the top level of a parameter type.
2068-
while (auto *tupleRepr = dyn_cast<TupleTypeRepr>(nestedRepr)) {
2069-
if (!tupleRepr->isParenType())
2070-
break;
2071-
nestedRepr = tupleRepr->getElementType(0);
2072-
}
2066+
auto *nestedRepr = typeRepr->getWithoutParens();
20732067

20742068
if (auto isolated = dyn_cast<IsolatedTypeRepr>(nestedRepr))
20752069
nestedRepr = isolated->getBase();

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,6 +1664,30 @@ ApplyAccessNoteRequest::evaluate(Evaluator &evaluator, ValueDecl *VD) const {
16641664
return {};
16651665
}
16661666

1667+
static void diagnoseWrittenPlaceholderTypes(ASTContext &Ctx,
1668+
const Pattern *P,
1669+
Expr *init) {
1670+
// Make sure we have a user-written type annotation.
1671+
auto *TP = dyn_cast<TypedPattern>(P);
1672+
if (!TP || !TP->getTypeRepr()) {
1673+
return;
1674+
}
1675+
1676+
auto *repr = TP->getTypeRepr()->getWithoutParens();
1677+
if (auto *PTR = dyn_cast<PlaceholderTypeRepr>(repr)) {
1678+
Ctx.Diags.diagnose(P->getLoc(),
1679+
diag::placeholder_type_not_allowed_in_pattern)
1680+
.highlight(P->getSourceRange());
1681+
if (init && !init->getType()->hasError()) {
1682+
auto initTy = init->getType()->mapTypeOutOfContext();
1683+
Ctx.Diags
1684+
.diagnose(PTR->getLoc(),
1685+
diag::replace_placeholder_with_inferred_type, initTy)
1686+
.fixItReplace(PTR->getSourceRange(), initTy.getString());
1687+
}
1688+
}
1689+
}
1690+
16671691
namespace {
16681692
class DeclChecker : public DeclVisitor<DeclChecker> {
16691693
public:
@@ -2078,6 +2102,11 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
20782102
PBD->getEqualLoc(i),
20792103
init);
20802104

2105+
// Written placeholder types are banned in the signatures of pattern
2106+
// bindings. If there's a valid initializer, try to offer its type
2107+
// as a replacement.
2108+
diagnoseWrittenPlaceholderTypes(Ctx, PBD->getPattern(i), init);
2109+
20812110
// If we entered an initializer context, contextualize any
20822111
// auto-closures we might have created.
20832112
// Note that we don't contextualize the initializer for a property

lib/Sema/TypeCheckType.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2834,15 +2834,9 @@ TypeResolver::resolveASTFunctionTypeParams(TupleTypeRepr *inputRepr,
28342834

28352835
ValueOwnership ownership = ValueOwnership::Default;
28362836

2837-
auto *nestedRepr = eltTypeRepr;
2838-
28392837
// Look through parens here; other than parens, specifiers
28402838
// must appear at the top level of a parameter type.
2841-
while (auto *tupleRepr = dyn_cast<TupleTypeRepr>(nestedRepr)) {
2842-
if (!tupleRepr->isParenType())
2843-
break;
2844-
nestedRepr = tupleRepr->getElementType(0);
2845-
}
2839+
auto *nestedRepr = eltTypeRepr->getWithoutParens();
28462840

28472841
bool isolated = false;
28482842
bool compileTimeConst = false;

test/Constraints/diagnostics.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,7 @@ func badTypes() {
11381138
func unresolvedTypeExistential() -> Bool {
11391139
return (Int.self==_{})
11401140
// expected-error@-1 {{type of expression is ambiguous without more context}}
1141+
// expected-error@-2 {{type placeholder not allowed here}}
11411142
}
11421143

11431144
do {

test/Sema/placeholder_type.swift

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// RUN: %target-typecheck-verify-swift
22

3-
let x: _ = 0
3+
let x: _ = 0 // expected-error {{placeholder type may not appear as type of a variable}} expected-note {{replace the placeholder with the inferred type 'Int'}} {{8-9=Int}}
44
let x2 = x
5+
var x3: _ { // expected-error {{type placeholder not allowed here}}
6+
get { 42 }
7+
}
58
let dict1: [_: Int] = ["hi": 0]
69
let dict2: [Character: _] = ["h": 0]
710

@@ -112,10 +115,10 @@ let _: () -> Int = { _() } // expected-error 2 {{type placeholder not allowed he
112115
let _: Int = _.init() // expected-error {{type placeholder not allowed here}} expected-error {{could not infer type for placeholder}}
113116
let _: () -> Int = { _.init() } // expected-error 2 {{type placeholder not allowed here}} expected-error {{could not infer type for placeholder}}
114117

115-
func returnsInt() -> Int { _() } // expected-error {{type of expression is ambiguous without more context}}
116-
func returnsIntClosure() -> () -> Int { { _() } } // expected-error {{unable to infer closure type in the current context}}
117-
func returnsInt2() -> Int { _.init() } // expected-error {{could not infer type for placeholder}}
118-
func returnsIntClosure2() -> () -> Int { { _.init() } } // expected-error {{could not infer type for placeholder}}
118+
func returnsInt() -> Int { _() } // expected-error {{type placeholder not allowed here}} expected-error {{type of expression is ambiguous without more context}}
119+
func returnsIntClosure() -> () -> Int { { _() } } // expected-error 2 {{type placeholder not allowed here}} expected-error {{unable to infer closure type in the current context}}
120+
func returnsInt2() -> Int { _.init() } // expected-error {{type placeholder not allowed here}} expected-error {{could not infer type for placeholder}}
121+
func returnsIntClosure2() -> () -> Int { { _.init() } } // expected-error 2 {{type placeholder not allowed here}} expected-error {{could not infer type for placeholder}}
119122

120123
let _: Int.Type = _ // expected-error {{'_' can only appear in a pattern or on the left side of an assignment}}
121124
let _: Int.Type = _.self // expected-error {{type placeholder not allowed here}}
@@ -144,13 +147,13 @@ let _ = [_].otherStaticMember.method()
144147
func f(x: Any, arr: [Int]) {
145148
// FIXME: Better diagnostics here. Maybe we should suggest replacing placeholders with 'Any'?
146149

147-
if x is _ {} // expected-error {{type of expression is ambiguous without more context}}
150+
if x is _ {} // expected-error {{type placeholder not allowed here}} expected-error {{type of expression is ambiguous without more context}}
148151
if x is [_] {} // expected-error {{type of expression is ambiguous without more context}}
149152
if x is () -> _ {} // expected-error {{type of expression is ambiguous without more context}}
150-
if let y = x as? _ {} // expected-error {{type of expression is ambiguous without more context}}
153+
if let y = x as? _ {} // expected-error {{type placeholder not allowed here}} expected-error {{type of expression is ambiguous without more context}}
151154
if let y = x as? [_] {} // expected-error {{type of expression is ambiguous without more context}}
152155
if let y = x as? () -> _ {} // expected-error {{type of expression is ambiguous without more context}}
153-
let y1 = x as! _ // expected-error {{type of expression is ambiguous without more context}}
156+
let y1 = x as! _ // expected-error {{type placeholder not allowed here}} expected-error {{type of expression is ambiguous without more context}}
154157
let y2 = x as! [_] // expected-error {{type of expression is ambiguous without more context}}
155158
let y3 = x as! () -> _ // expected-error {{type of expression is ambiguous without more context}}
156159

@@ -163,13 +166,13 @@ func f(x: Any, arr: [Int]) {
163166
case let y as () -> _: break // expected-error {{type placeholder not allowed here}}
164167
}
165168

166-
if arr is _ {} // expected-error {{type of expression is ambiguous without more context}}
169+
if arr is _ {} // expected-error {{type placeholder not allowed here}} expected-error {{type of expression is ambiguous without more context}}
167170
if arr is [_] {} // expected-error {{type of expression is ambiguous without more context}}
168171
if arr is () -> _ {} // expected-error {{type of expression is ambiguous without more context}}
169-
if let y = arr as? _ {} // expected-error {{type of expression is ambiguous without more context}}
172+
if let y = arr as? _ {} // expected-error {{type placeholder not allowed here}} expected-error {{type of expression is ambiguous without more context}}
170173
if let y = arr as? [_] {} // expected-error {{type of expression is ambiguous without more context}}
171174
if let y = arr as? () -> _ {} // expected-error {{type of expression is ambiguous without more context}}
172-
let y1 = arr as! _ // expected-error {{type of expression is ambiguous without more context}}
175+
let y1 = arr as! _ // expected-error {{type placeholder not allowed here}} expected-error {{type of expression is ambiguous without more context}}
173176
let y2 = arr as! [_] // expected-error {{type of expression is ambiguous without more context}}
174177
let y3 = arr as! () -> _ // expected-error {{type of expression is ambiguous without more context}}
175178

@@ -208,8 +211,9 @@ let _: SetFailureType<Int, (String, Double)> = Just<Int>().setFailureType(to: (_
208211
// TODO: Better error message here? Would be nice if we could point to the placeholder...
209212
let _: SetFailureType<Int, String> = Just<Int>().setFailureType(to: _.self).setFailureType(to: String.self) // expected-error {{type placeholder not allowed here}} expected-error {{generic parameter 'T' could not be inferred}}
210213

211-
let _: (_) = 0 as Int
212-
let _: Int = 0 as (_)
214+
let _: (_) = 0 as Int // expected-error {{placeholder type may not appear as type of a variable}} expected-note {{replace the placeholder with the inferred type 'Int'}} {{9-10=Int}}
215+
let _: Int = 0 as (_) // expected-error {{type placeholder not allowed here}}
216+
let _: Int = 0 as (((((_))))) // expected-error {{type placeholder not allowed here}}
213217

214218
_ = (1...10)
215219
.map {
@@ -254,3 +258,11 @@ enum EnumWithPlaceholders {
254258
// expected-note@-1 {{replace the placeholder with the inferred type 'Int'}}
255259
}
256260

261+
func deferredInit(_ c: Bool) {
262+
let x: _ // expected-error {{type placeholder not allowed here}}
263+
if c {
264+
x = "Hello"
265+
} else {
266+
x = "Goodbye"
267+
}
268+
}

0 commit comments

Comments
 (0)