Skip to content

Commit ded4dd2

Browse files
committed
Suggest Replacement Types for Invalid Placeholders
Now that placeholder types are preserved, we can open them wherever they appear in positions where they are used as contextual types. Use the checks we already run in the primaries to ban placeholders in top-level positions. Only now, use the inferred type of any associated expressions or statements to present the user with a contextual return type. For parameters in functions and enum cases, we use any default argument expressions to get the contextual type. For functions, we use a traversal similar to the opaque result type finder to find the type of any return statements in the program and present those as options.
1 parent 1fe2e4b commit ded4dd2

File tree

5 files changed

+219
-36
lines changed

5 files changed

+219
-36
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3703,6 +3703,12 @@ NOTE(descriptive_generic_type_declared_here,none,
37033703

37043704
ERROR(placeholder_type_not_allowed,none,
37053705
"type placeholder not allowed here", ())
3706+
ERROR(placeholder_type_not_allowed_in_return_type,none,
3707+
"type placeholder may not appear in function return type", ())
3708+
ERROR(placeholder_type_not_allowed_in_parameter,none,
3709+
"type placeholder may not appear in top-level parameter", ())
3710+
NOTE(replace_placeholder_with_inferred_type,none,
3711+
"replace the placeholder with the correct type %0", (Type))
37063712

37073713
WARNING(use_of_void_pointer,none,
37083714
"Unsafe%0Pointer<Void> has been replaced by Unsafe%0RawPointer", (StringRef))

lib/Sema/MiscDiagnostics.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2712,6 +2712,65 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
27122712
}
27132713
};
27142714

2715+
class ReturnTypePlaceholderReplacer : public ASTWalker {
2716+
FuncDecl *Implementation;
2717+
BraceStmt *Body;
2718+
SmallVector<Type, 4> Candidates;
2719+
2720+
bool HasInvalidReturn = false;
2721+
2722+
public:
2723+
ReturnTypePlaceholderReplacer(FuncDecl *Implementation, BraceStmt *Body)
2724+
: Implementation(Implementation), Body(Body) {}
2725+
2726+
void check() {
2727+
auto *resultRepr = Implementation->getResultTypeRepr();
2728+
if (!resultRepr) {
2729+
return;
2730+
}
2731+
2732+
Implementation->getASTContext()
2733+
.Diags
2734+
.diagnose(resultRepr->getLoc(),
2735+
diag::placeholder_type_not_allowed_in_return_type)
2736+
.highlight(resultRepr->getSourceRange());
2737+
2738+
Body->walk(*this);
2739+
2740+
// If given function has any invalid returns in the body
2741+
// let's not try to validate the types, since it wouldn't
2742+
// be accurate.
2743+
if (HasInvalidReturn)
2744+
return;
2745+
2746+
auto writtenType = Implementation->getResultInterfaceType();
2747+
llvm::SmallPtrSet<TypeBase *, 8> seenTypes;
2748+
for (auto candidate : Candidates) {
2749+
if (!seenTypes.insert(candidate.getPointer()).second) {
2750+
continue;
2751+
}
2752+
TypeChecker::notePlaceholderReplacementTypes(writtenType, candidate);
2753+
}
2754+
}
2755+
2756+
std::pair<bool, Expr *> walkToExprPre(Expr *E) override { return {true, E}; }
2757+
2758+
std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
2759+
if (auto *RS = dyn_cast<ReturnStmt>(S)) {
2760+
if (RS->hasResult()) {
2761+
auto resultTy = RS->getResult()->getType();
2762+
HasInvalidReturn |= resultTy.isNull() || resultTy->hasError();
2763+
Candidates.push_back(resultTy);
2764+
}
2765+
}
2766+
2767+
return {true, S};
2768+
}
2769+
2770+
// Don't descend into nested decls.
2771+
bool walkToDeclPre(Decl *D) override { return false; }
2772+
};
2773+
27152774
} // end anonymous namespace
27162775

27172776
// After we have scanned the entire region, diagnose variables that could be
@@ -3277,6 +3336,13 @@ void swift::performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD) {
32773336
OpaqueUnderlyingTypeChecker(AFD, opaqueResultTy, body).check();
32783337
}
32793338
}
3339+
} else if (auto *FD = dyn_cast<FuncDecl>(AFD)) {
3340+
auto resultIFaceTy = FD->getResultInterfaceType();
3341+
// If the result has a placeholder, we need to try to use the contextual
3342+
// type inferred in the body to replace it.
3343+
if (resultIFaceTy && resultIFaceTy->hasPlaceholder()) {
3344+
ReturnTypePlaceholderReplacer(FD, body).check();
3345+
}
32803346
}
32813347
}
32823348

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,19 @@
1818

1919
#include "CodeSynthesis.h"
2020
#include "DerivedConformances.h"
21-
#include "TypeChecker.h"
21+
#include "MiscDiagnostics.h"
2222
#include "TypeCheckAccess.h"
23+
#include "TypeCheckAvailability.h"
2324
#include "TypeCheckConcurrency.h"
2425
#include "TypeCheckDecl.h"
25-
#include "TypeCheckAvailability.h"
2626
#include "TypeCheckObjC.h"
2727
#include "TypeCheckType.h"
28-
#include "MiscDiagnostics.h"
29-
#include "swift/AST/AccessNotes.h"
30-
#include "swift/AST/AccessScope.h"
28+
#include "TypeChecker.h"
3129
#include "swift/AST/ASTPrinter.h"
3230
#include "swift/AST/ASTVisitor.h"
3331
#include "swift/AST/ASTWalker.h"
32+
#include "swift/AST/AccessNotes.h"
33+
#include "swift/AST/AccessScope.h"
3434
#include "swift/AST/ExistentialLayout.h"
3535
#include "swift/AST/Expr.h"
3636
#include "swift/AST/ForeignErrorConvention.h"
@@ -42,15 +42,15 @@
4242
#include "swift/AST/PropertyWrappers.h"
4343
#include "swift/AST/ProtocolConformance.h"
4444
#include "swift/AST/SourceFile.h"
45+
#include "swift/AST/TypeCheckRequests.h"
46+
#include "swift/AST/TypeDifferenceVisitor.h"
4547
#include "swift/AST/TypeWalker.h"
48+
#include "swift/Basic/Defer.h"
4649
#include "swift/Basic/Statistic.h"
4750
#include "swift/Parse/Lexer.h"
4851
#include "swift/Parse/Parser.h"
4952
#include "swift/Serialization/SerializedModuleLoader.h"
5053
#include "swift/Strings.h"
51-
#include "swift/AST/NameLookupRequests.h"
52-
#include "swift/AST/TypeCheckRequests.h"
53-
#include "swift/Basic/Defer.h"
5454
#include "llvm/ADT/APFloat.h"
5555
#include "llvm/ADT/APInt.h"
5656
#include "llvm/ADT/APSInt.h"
@@ -967,11 +967,67 @@ static void checkInheritedDefaultValueRestrictions(ParamDecl *PD) {
967967
}
968968
}
969969

970+
void TypeChecker::notePlaceholderReplacementTypes(Type writtenType,
971+
Type inferredType) {
972+
assert(writtenType && inferredType &&
973+
"Must provide both written and inferred types");
974+
assert(writtenType->hasPlaceholder() && "Written type has no placeholder?");
975+
976+
class PlaceholderNotator
977+
: public CanTypeDifferenceVisitor<PlaceholderNotator> {
978+
public:
979+
bool visitDifferentComponentTypes(CanType t1, CanType t2) {
980+
// Never replace anything the user wrote with an error type.
981+
if (t2->hasError() || t2->hasUnresolvedType()) {
982+
return false;
983+
}
984+
985+
auto *placeholder = t1->getAs<PlaceholderType>();
986+
if (!placeholder) {
987+
return false;
988+
}
989+
990+
if (auto *origRepr =
991+
placeholder->getOriginator().dyn_cast<PlaceholderTypeRepr *>()) {
992+
t1->getASTContext()
993+
.Diags
994+
.diagnose(origRepr->getLoc(),
995+
diag::replace_placeholder_with_inferred_type, t2)
996+
.fixItReplace(origRepr->getSourceRange(), t2.getString());
997+
}
998+
return false;
999+
}
1000+
1001+
bool check(Type t1, Type t2) {
1002+
return !visit(t1->getCanonicalType(), t2->getCanonicalType());
1003+
};
1004+
};
1005+
1006+
PlaceholderNotator().check(writtenType, inferredType);
1007+
}
1008+
9701009
/// Check the default arguments that occur within this pattern.
9711010
static void checkDefaultArguments(ParameterList *params) {
9721011
// Force the default values in case they produce diagnostics.
973-
for (auto *param : *params)
974-
(void)param->getTypeCheckedDefaultExpr();
1012+
for (auto *param : *params) {
1013+
auto ifacety = param->getInterfaceType();
1014+
auto *expr = param->getTypeCheckedDefaultExpr();
1015+
if (!ifacety->hasPlaceholder()) {
1016+
continue;
1017+
}
1018+
1019+
// Placeholder types are banned for all parameter decls. We try to use the
1020+
// freshly-checked default expression's contextual type to suggest a
1021+
// reasonable type to insert.
1022+
if (expr && !expr->getType()->hasError()) {
1023+
param->diagnose(diag::placeholder_type_not_allowed_in_parameter)
1024+
.highlight(param->getSourceRange());
1025+
TypeChecker::notePlaceholderReplacementTypes(
1026+
ifacety, expr->getType()->mapTypeOutOfContext());
1027+
} else {
1028+
param->diagnose(diag::placeholder_type_not_allowed);
1029+
}
1030+
}
9751031
}
9761032

9771033
Expr *DefaultArgumentExprRequest::evaluate(Evaluator &evaluator,

lib/Sema/TypeChecker.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,24 @@ bool diagnoseInvalidFunctionType(FunctionType *fnTy, SourceLoc loc,
11381138
DeclContext *dc,
11391139
Optional<TypeResolutionStage> stage);
11401140

1141+
/// Walk the parallel structure of a type with user-provided placeholders and
1142+
/// an inferred type produced by the type checker. Where placeholders can be
1143+
/// found, suggest the corresponding inferred type.
1144+
///
1145+
/// For example,
1146+
///
1147+
/// \code
1148+
/// func foo(_ x: [_] = [0])
1149+
/// \endcode
1150+
///
1151+
/// Has a written type of `(ArraySlice (Placeholder))` and an inferred type of
1152+
/// `(ArraySlice Int)`, so we walk to `Placeholder` and `Int` in each type and
1153+
/// suggest replacing `_` with `Int`.
1154+
///
1155+
/// \param writtenType The interface type usually derived from a user-written
1156+
/// type repr. \param inferredType The type inferred by the type checker.
1157+
void notePlaceholderReplacementTypes(Type writtenType, Type inferredType);
1158+
11411159
}; // namespace TypeChecker
11421160

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

test/Sema/placeholder_type.swift

Lines changed: 63 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ let dict2: [Character: _] = ["h": 0]
77

88
let arr = [_](repeating: "hi", count: 3)
99

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

1213
let foo = _.foo // expected-error {{could not infer type for placeholder}}
1314
let zero: _ = .zero // expected-error {{cannot infer contextual base in reference to member 'zero'}}
@@ -74,27 +75,35 @@ where T: ExpressibleByIntegerLiteral, U: ExpressibleByIntegerLiteral {
7475
}
7576

7677
extension Bar {
77-
func frobnicate2() -> Bar<_, _> { // expected-error {{type placeholder not allowed here}}
78-
return Bar(t: 42, u: 42)
79-
}
80-
func frobnicate3() -> Bar {
81-
return Bar<_, _>(t: 42, u: 42)
82-
}
83-
func frobnicate4() -> Bar<_, _> { // expected-error {{type placeholder not allowed here}}
84-
return Bar<_, _>(t: 42, u: 42)
85-
}
86-
func frobnicate5() -> Bar<_, U> { // expected-error {{type placeholder not allowed here}}
87-
return Bar(t: 42, u: 42)
88-
}
89-
func frobnicate6() -> Bar {
90-
return Bar<_, U>(t: 42, u: 42)
91-
}
92-
func frobnicate7() -> Bar<_, _> { // expected-error {{type placeholder not allowed here}}
93-
return Bar<_, U>(t: 42, u: 42)
94-
}
95-
func frobnicate8() -> Bar<_, U> { // expected-error {{type placeholder not allowed here}}
96-
return Bar<_, _>(t: 42, u: 42)
97-
}
78+
func frobnicate2() -> Bar<_, _> { // expected-error {{type placeholder may not appear in function return type}}
79+
// expected-note@-1 {{replace the placeholder with the correct type 'T'}}
80+
// expected-note@-2 {{replace the placeholder with the correct type 'U'}}
81+
return Bar(t: 42, u: 42)
82+
}
83+
func frobnicate3() -> Bar {
84+
return Bar<_, _>(t: 42, u: 42)
85+
}
86+
func frobnicate4() -> Bar<_, _> { // expected-error {{type placeholder may not appear in function return type}}
87+
// expected-note@-1 {{replace the placeholder with the correct type 'Int'}}
88+
// expected-note@-2 {{replace the placeholder with the correct type 'Int'}}
89+
return Bar<_, _>(t: 42, u: 42)
90+
}
91+
func frobnicate5() -> Bar<_, U> { // expected-error {{type placeholder may not appear in function return type}}
92+
// expected-note@-1 {{replace the placeholder with the correct type 'T'}}
93+
return Bar(t: 42, u: 42)
94+
}
95+
func frobnicate6() -> Bar {
96+
return Bar<_, U>(t: 42, u: 42)
97+
}
98+
func frobnicate7() -> Bar<_, _> { // expected-error {{type placeholder may not appear in function return type}}
99+
// expected-note@-1 {{replace the placeholder with the correct type 'Int'}}
100+
// expected-note@-2 {{replace the placeholder with the correct type 'U'}}
101+
return Bar<_, U>(t: 42, u: 42)
102+
}
103+
func frobnicate8() -> Bar<_, U> { // expected-error {{type placeholder may not appear in function return type}}
104+
// expected-note@-1 {{replace the placeholder with the correct type 'Int'}}
105+
return Bar<_, _>(t: 42, u: 42)
106+
}
98107
}
99108

100109
// FIXME: We should probably have better diagnostics for these situations--the user probably meant to use implicit member syntax
@@ -186,7 +195,7 @@ struct Just<Output>: Publisher {
186195
struct SetFailureType<Output, Failure>: Publisher {}
187196

188197
extension Publisher {
189-
func setFailureType<T>(to: T.Type) -> SetFailureType<Output, T> { // expected-note {{in call to function 'setFailureType(to:)'}}
198+
func setFailureType<T>(to: T.Type) -> SetFailureType<Output, T> { // expected-note 2 {{in call to function 'setFailureType(to:)'}}
190199
return .init()
191200
}
192201
}
@@ -197,10 +206,10 @@ let _: SetFailureType<Int, (String) -> Double> = Just<Int>().setFailureType(to:
197206
let _: SetFailureType<Int, (String, Double)> = Just<Int>().setFailureType(to: (_, _).self)
198207

199208
// TODO: Better error message here? Would be nice if we could point to the placeholder...
200-
let _: SetFailureType<Int, String> = Just<Int>().setFailureType(to: _.self).setFailureType(to: String.self) // expected-error {{placeholders are not allowed as top-level types}}
209+
let _: SetFailureType<Int, String> = Just<Int>().setFailureType(to: _.self).setFailureType(to: String.self) // expected-error {{generic parameter 'T' could not be inferred}}
201210

202-
let _: (_) = 0 as Int // expected-error {{placeholders are not allowed as top-level types}}
203-
let _: Int = 0 as (_) // expected-error {{placeholders are not allowed as top-level types}}
211+
let _: (_) = 0 as Int
212+
let _: Int = 0 as (_)
204213

205214
_ = (1...10)
206215
.map {
@@ -217,3 +226,31 @@ _ = (1...10)
217226
}
218227

219228
let _: SetFailureType<Int, String> = Just<Int>().setFailureType(to: _.self).setFailureType(to: String.self) // expected-error {{generic parameter 'T' could not be inferred}}
229+
230+
// N.B. The parallel structure of the annotation and inferred default
231+
// initializer types is all wrong. Plus, we do not trust
232+
// the contextual type with placeholders in it so the result is a generic
233+
// diagnostic.
234+
func mismatchedDefault<T>(_ x: [_] = [String: T]()) {} // expected-error {{type placeholder not allowed here}}
235+
236+
func mismatchedReturnTypes() -> _ { // expected-error {{type placeholder may not appear in function return type}}
237+
if true {
238+
return "" // expected-note@-2 {{replace the placeholder with the correct type 'String'}}
239+
} else {
240+
return 0.5 // expected-note@-4 {{replace the placeholder with the correct type 'Double'}}
241+
}
242+
}
243+
244+
// FIXME: Opaque result types ought to be treated better than this. But it's
245+
// tricky to know which type is intended in a lot of cases.
246+
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
247+
func opaque() -> some _ { // expected-error {{type placeholder not allowed here}}
248+
return Just<Int>().setFailureType(to: _.self)
249+
}
250+
251+
enum EnumWithPlaceholders {
252+
case topLevelPlaceholder(x: _) // expected-error {{type placeholder not allowed here}}
253+
case placeholderWithDefault(x: _ = 5) // expected-error {{type placeholder may not appear in top-level parameter}}
254+
// expected-note@-1 {{replace the placeholder with the correct type 'Int'}}
255+
}
256+

0 commit comments

Comments
 (0)