From f608802cfe498b8684e4ea63603fe99073102ce6 Mon Sep 17 00:00:00 2001 From: Sima Nerush <2002ssn@gmail.com> Date: Sat, 30 Jul 2022 20:08:53 -0600 Subject: [PATCH] Sema: Emit diagnostics when walking into collection literals with defaulted types Resolves https://github.com/apple/swift/issues/60011 Gardening: clean up `checkTypeDefaultedCollectionExpr` function --- lib/Sema/MiscDiagnostics.cpp | 73 ++++++++++++++--------- test/Constraints/array_literal.swift | 39 +++++++++++- test/Constraints/casts.swift | 1 + test/Constraints/casts_swift6.swift | 1 + test/Constraints/construction.swift | 1 + test/Constraints/dictionary_literal.swift | 3 +- test/Constraints/subscript.swift | 1 + test/IDE/print_usrs_opaque_types.swift | 10 ++++ test/expr/cast/objc_coerce_array.swift | 4 ++ 9 files changed, 104 insertions(+), 29 deletions(-) diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 12a8e8a6d48e7..de43feb009e25 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -109,13 +109,15 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, SmallPtrSet AlreadyDiagnosedBitCasts; bool IsExprStmt; + bool HasReachedSemanticsProvidingExpr; - public: ASTContext &Ctx; const DeclContext *DC; + public: DiagnoseWalker(const DeclContext *DC, bool isExprStmt) - : IsExprStmt(isExprStmt), Ctx(DC->getASTContext()), DC(DC) {} + : IsExprStmt(isExprStmt), HasReachedSemanticsProvidingExpr(false), + Ctx(DC->getASTContext()), DC(DC) {} std::pair walkToPatternPre(Pattern *P) override { return { false, P }; @@ -128,6 +130,17 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, bool shouldWalkIntoTapExpression() override { return false; } std::pair walkToExprPre(Expr *E) override { + + if (auto collection = dyn_cast(E)) { + if (collection->isTypeDefaulted()) { + // Diagnose type defaulted collection literals in subexpressions as + // warnings to preserve source compatibility. + diagnoseTypeDefaultedCollectionExpr( + collection, Ctx, + /*downgradeToWarning=*/HasReachedSemanticsProvidingExpr); + } + } + // See through implicit conversions of the expression. We want to be able // to associate the parent of this expression with the ultimate callee. auto Base = E; @@ -328,6 +341,11 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, checkMoveExpr(moveExpr); } + if (!HasReachedSemanticsProvidingExpr && + E == E->getSemanticsProvidingExpr()) { + HasReachedSemanticsProvidingExpr = true; + } + return { true, E }; } @@ -444,23 +462,34 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, return false; } - /// We have a collection literal with a defaulted type, e.g. of [Any]. Emit - /// an error if it was inferred to this type in an invalid context, which is - /// one in which the parent expression is not itself a collection literal. - void checkTypeDefaultedCollectionExpr(CollectionExpr *c) { - // If the parent is a non-expression, or is not itself a literal, then - // produce an error with a fixit to add the type as an explicit - // annotation. - if (c->getNumElements() == 0) - Ctx.Diags.diagnose(c->getLoc(), diag::collection_literal_empty) - .highlight(c->getSourceRange()); - else { + /// Diagnose a collection literal with a defaulted type such as \c [Any]. + static void diagnoseTypeDefaultedCollectionExpr(CollectionExpr *c, + ASTContext &ctx, + bool downgradeToWarning) { + // Produce a diagnostic with a fixit to add the defaulted type as an + // explicit annotation. + auto &diags = ctx.Diags; + + if (c->getNumElements() == 0) { + InFlightDiagnostic inFlight = + diags.diagnose(c->getLoc(), diag::collection_literal_empty); + inFlight.highlight(c->getSourceRange()); + + if (downgradeToWarning) { + inFlight.limitBehavior(DiagnosticBehavior::Warning); + } + } else { assert(c->getType()->hasTypeRepr() && "a defaulted type should always be printable"); - Ctx.Diags.diagnose(c->getLoc(), diag::collection_literal_heterogeneous, - c->getType()) - .highlight(c->getSourceRange()) - .fixItInsertAfter(c->getEndLoc(), " as " + c->getType()->getString()); + InFlightDiagnostic inFlight = diags.diagnose( + c->getLoc(), diag::collection_literal_heterogeneous, c->getType()); + inFlight.highlight(c->getSourceRange()); + inFlight.fixItInsertAfter(c->getEndLoc(), + " as " + c->getType()->getString()); + + if (downgradeToWarning) { + inFlight.limitBehavior(DiagnosticBehavior::Warning); + } } } @@ -1334,16 +1363,6 @@ static void diagSyntacticUseRestrictions(const Expr *E, const DeclContext *DC, DiagnoseWalker Walker(DC, isExprStmt); const_cast(E)->walk(Walker); - - // Diagnose uses of collection literals with defaulted types at the top - // level. - if (auto collection - = dyn_cast(E->getSemanticsProvidingExpr())) { - if (collection->isTypeDefaulted()) { - Walker.checkTypeDefaultedCollectionExpr( - const_cast(collection)); - } - } } diff --git a/test/Constraints/array_literal.swift b/test/Constraints/array_literal.swift index 8276bd5632e66..f8e87090f98b1 100644 --- a/test/Constraints/array_literal.swift +++ b/test/Constraints/array_literal.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -disable-availability-checking struct IntList : ExpressibleByArrayLiteral { typealias Element = Int @@ -142,19 +142,56 @@ func defaultToAny(i: Int, s: String) { // expected-error@-1{{empty collection literal requires an explicit type}} let _: Int = a5 // expected-error{{value of type '[Any]'}} + let _: [Any] = [] let _: [Any] = [1, "a", 3.5] let _: [Any] = [1, "a", [3.5, 3.7, 3.9]] let _: [Any] = [1, "a", [3.5, "b", 3]] + // expected-warning@-1{{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}} let _: [Any] = [1, [2, [3]]] + // expected-warning@-1{{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}} + + func f1() -> [Any] { + [] + } let _: [Any?] = [1, "a", nil, 3.5] let _: [Any?] = [1, "a", nil, [3.5, 3.7, 3.9]] let _: [Any?] = [1, "a", nil, [3.5, "b", nil]] + // expected-warning@-1{{heterogeneous collection literal could only be inferred to '[Any?]'; add explicit type annotation if this is intentional}} let _: [Any?] = [1, [2, [3]]] + // expected-warning@-1{{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}} let _: [Any?] = [1, nil, [2, nil, [3]]] + // expected-warning@-1{{heterogeneous collection literal could only be inferred to '[Any?]'; add explicit type annotation if this is intentional}} let a6 = [B(), C()] let _: Int = a6 // expected-error{{value of type '[A]'}} + + let a7: some Collection = [1, "Swift"] + // expected-warning@-1{{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}} {{41-41= as [Any]}} + let _: (any Sequence)? = [1, "Swift"] + // expected-warning@-1{{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}} + let _: any Sequence = [1, nil, "Swift"] + // expected-warning@-1{{heterogeneous collection literal could only be inferred to '[Any?]'; add explicit type annotation if this is intentional}} + let _ = [1, true, ([], 1)] + // expected-error@-1 {{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}} + // expected-warning@-2 {{empty collection literal requires an explicit type}} + let _ = true ? [] : [] + // expected-warning@-1{{empty collection literal requires an explicit type}} + let _ = (true, ([1, "Swift"])) + //expected-warning@-1{{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}} + let _ = ([1, true]) + //expected-error@-1{{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}} + + func f2(_: [T]) {} + + func f3() -> [T]? {} + + f2([]) + // expected-warning@-1{{empty collection literal requires an explicit type}} + f2([1, nil, ""]) + // expected-warning@-1{{heterogeneous collection literal could only be inferred to '[Any?]'; add explicit type annotation if this is intentional}} + _ = f3() ?? [] + // expected-warning@-1{{empty collection literal requires an explicit type}} } func noInferAny(iob: inout B, ioc: inout C) { diff --git a/test/Constraints/casts.swift b/test/Constraints/casts.swift index 4038c2cc92b8a..65c9f9a2f5e4c 100644 --- a/test/Constraints/casts.swift +++ b/test/Constraints/casts.swift @@ -337,6 +337,7 @@ func test_compatibility_coercions(_ arr: [Int], _ optArr: [Int]?, _ dict: [Strin // The array can also be inferred to be [Any]. _ = ([] ?? []) as Array // expected-warning {{left side of nil coalescing operator '??' has non-optional type '[Any]', so the right side is never used}} + // expected-warning@-1{{empty collection literal requires an explicit type}} // rdar://88334481 – Don't apply the compatibility logic for collection literals. typealias Magic = T diff --git a/test/Constraints/casts_swift6.swift b/test/Constraints/casts_swift6.swift index 496eeb90b7059..000484e984943 100644 --- a/test/Constraints/casts_swift6.swift +++ b/test/Constraints/casts_swift6.swift @@ -57,6 +57,7 @@ func test_compatibility_coercions(_ arr: [Int], _ optArr: [Int]?, _ dict: [Strin // The array can also be inferred to be [Any]. _ = ([] ?? []) as Array // expected-warning {{left side of nil coalescing operator '??' has non-optional type '[Any]', so the right side is never used}} + // expected-warning@-1 {{empty collection literal requires an explicit type}} // Cases from rdar://88334481 typealias Magic = T diff --git a/test/Constraints/construction.swift b/test/Constraints/construction.swift index d4857ad08d633..e738589758672 100644 --- a/test/Constraints/construction.swift +++ b/test/Constraints/construction.swift @@ -172,6 +172,7 @@ SR_5245(s: SR_5245.S(f: [.e1, .e2])) // rdar://problem/34670592 - Compiler crash on heterogeneous collection literal _ = Array([1, "hello"]) // Ok +// expected-warning@-1 {{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}} func init_via_non_const_metatype(_ s1: S1.Type) { _ = s1(i: 42) // expected-error {{initializing from a metatype value must reference 'init' explicitly}} {{9-9=.init}} diff --git a/test/Constraints/dictionary_literal.swift b/test/Constraints/dictionary_literal.swift index d73270f8cf30b..217cceec6e9d4 100644 --- a/test/Constraints/dictionary_literal.swift +++ b/test/Constraints/dictionary_literal.swift @@ -139,6 +139,7 @@ func testDefaultExistentials() { "b": ["a", 2, 3.14159], "c": ["a": 2, "b": 3.5]] // expected-error@-3{{heterogeneous collection literal could only be inferred to '[String : Any]'; add explicit type annotation if this is intentional}} + // expected-warning@-3{{heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional}} let d3 = ["b" : B(), "c" : C()] let _: Int = d3 // expected-error{{value of type '[String : A]'}} @@ -193,4 +194,4 @@ f59215(["", ""]) //expected-error{{dictionary of type '[String : String]' cannot f59215(["", "", "", ""]) //expected-error{{dictionary of type '[String : String]' cannot be used with array literal}} // expected-note@-1{{did you mean to use a dictionary literal instead?}} {{11-12=:}} {{19-20=:}} f59215(["", "", "", ""]) //expected-error{{dictionary of type '[String : String]' cannot be used with array literal}} -// expected-note@-1{{did you mean to use a dictionary literal instead?}} {{11-12=:}} {{19-20=:}} \ No newline at end of file +// expected-note@-1{{did you mean to use a dictionary literal instead?}} {{11-12=:}} {{19-20=:}} diff --git a/test/Constraints/subscript.swift b/test/Constraints/subscript.swift index 00db94819bc27..34bdef40c7f8e 100644 --- a/test/Constraints/subscript.swift +++ b/test/Constraints/subscript.swift @@ -104,6 +104,7 @@ extension Int { let _ = 1["1"] // expected-error {{ambiguous use of 'subscript(_:)'}} let squares = [ 1, 2, 3 ].reduce([:]) { (dict, n) in + // expected-warning@-1 {{empty collection literal requires an explicit type}} var dict = dict dict[n] = n * n return dict diff --git a/test/IDE/print_usrs_opaque_types.swift b/test/IDE/print_usrs_opaque_types.swift index 5723746352632..f6bc21c72759b 100644 --- a/test/IDE/print_usrs_opaque_types.swift +++ b/test/IDE/print_usrs_opaque_types.swift @@ -9,17 +9,20 @@ func testUnifyingGenericParams(x: T) -> some Collection where T == U { // expected-warning@-1 {{same-type requirement makes generic parameters 'U' and 'T' equivalent}} return [] + // expected-warning@-1 {{empty collection literal requires an explicit type}} } // CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test0C22UnifyingGenericParams21xQrx_tSlRz7ElementQzRs_r0_lF func testUnifyingGenericParams2(x: T) -> some Collection where T: Collection, U == T.Element { return [] + // expected-warning@-1 {{empty collection literal requires an explicit type}} } // CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test0C24ConcretizingGenericParam1xQrSi_tSiRszlF func testConcretizingGenericParam(x: T) -> some Collection where T == Int { // expected-warning@-1 {{same-type requirement makes generic parameter 'T' non-generic}} return [] + // expected-warning@-1 {{empty collection literal requires an explicit type}} } struct GenericContext { @@ -27,11 +30,13 @@ struct GenericContext { func testUnifyingGenericParams(x: T) -> some Collection where T == U { // expected-warning@-1 {{same-type requirement makes generic parameters 'U' and 'T' equivalent}} return [] + // expected-warning@-1 {{empty collection literal requires an explicit type}} } // CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test14GenericContextV0c8UnifyingD7Params21xQrx_tSlRz7ElementQzRsd__lF func testUnifyingGenericParams2(x: T) -> some Collection where T: Collection, U == T.Element { return [] + // expected-warning@-1 {{empty collection literal requires an explicit type}} } // CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test14GenericContextVyQrxcqd__Rszluip @@ -40,6 +45,7 @@ struct GenericContext { // CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test14GenericContextVyQrxcqd__Rszluig get { return [] + // expected-warning@-1 {{empty collection literal requires an explicit type}} } } } @@ -48,6 +54,7 @@ extension GenericContext where T == Int { // CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test14GenericContextVAASiRszlE0c12ConcretizingD5Param1xQrSi_tF func testConcretizingGenericParam(x: T) -> some Collection { return [] + // expected-warning@-1 {{empty collection literal requires an explicit type}} } } @@ -58,6 +65,7 @@ extension TooGenericTooContext where T == U { // CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test010TooGenericD7ContextVAAq_RszrlE0c8UnifyingE6Params1xQrx_tF func testUnifyingGenericParams(x: T) -> some Collection { return [] + // expected-warning@-1 {{empty collection literal requires an explicit type}} } } @@ -65,12 +73,14 @@ extension TooGenericTooContext where T: Collection, U == T.Element { // CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test010TooGenericD7ContextVAASlRz7ElementQzRs_rlE0c8UnifyingE7Params21xQrx_tF func testUnifyingGenericParams2(x: T) -> some Collection { return [] + // expected-warning@-1 {{empty collection literal requires an explicit type}} } } extension TooGenericTooContext where T == Int { // CHECK: [[@LINE+1]]:{{[0-9]+}} s:14swift_ide_test010TooGenericD7ContextVAASiRszrlE0c12ConcretizingE5Param1xQrSi_tF func testConcretizingGenericParam(x: T) -> some Collection { return [] + // expected-warning@-1 {{empty collection literal requires an explicit type}} } } diff --git a/test/expr/cast/objc_coerce_array.swift b/test/expr/cast/objc_coerce_array.swift index 22f3c5eb0aadb..b34d6ca08e742 100644 --- a/test/expr/cast/objc_coerce_array.swift +++ b/test/expr/cast/objc_coerce_array.swift @@ -7,9 +7,13 @@ var x = 1 _ = [x] as [NSNumber] _ = ["x":["y":"z","a":1]] as [String : [String : AnyObject]] +// expected-warning@-1{{heterogeneous collection literal could only be inferred to '[String : AnyObject]'; add explicit type annotation if this is intentiona}} _ = ["x":["z",1]] as [String : [AnyObject]] +// expected-warning@-1{{heterogeneous collection literal could only be inferred to '[AnyObject]'; add explicit type annotation if this is intentional}} _ = [["y":"z","a":1]] as [[String : AnyObject]] +// expected-warning@-1{{heterogeneous collection literal could only be inferred to '[String : AnyObject]'; add explicit type annotation if this is intentional}} _ = [["z",1]] as [[AnyObject]] +// expected-warning@-1{{heterogeneous collection literal could only be inferred to '[AnyObject]'; add explicit type annotation if this is intentional}} var y: Any = 1