diff --git a/include/swift/Sema/CSFix.h b/include/swift/Sema/CSFix.h index f23921858e9be..dd5be38d5cdfe 100644 --- a/include/swift/Sema/CSFix.h +++ b/include/swift/Sema/CSFix.h @@ -587,21 +587,38 @@ class RelabelArguments final } }; +class RequirementFix : public ConstraintFix { +protected: + Type LHS; + Type RHS; + + RequirementFix(ConstraintSystem &cs, FixKind kind, Type lhs, Type rhs, + ConstraintLocator *locator) + : ConstraintFix(cs, kind, locator), LHS(lhs), RHS(rhs) {} + +public: + std::string getName() const override = 0; + + Type lhsType() const { return LHS; } + Type rhsType() const { return RHS; } + + bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override; + + bool diagnose(const Solution &solution, + bool asNote = false) const override = 0; +}; + /// Add a new conformance to the type to satisfy a requirement. -class MissingConformance final : public ConstraintFix { +class MissingConformance final : public RequirementFix { // Determines whether given protocol type comes from the context e.g. // assignment destination or argument comparison. bool IsContextual; - Type NonConformingType; - // This could either be a protocol or protocol composition. - Type ProtocolType; - MissingConformance(ConstraintSystem &cs, bool isContextual, Type type, Type protocolType, ConstraintLocator *locator) - : ConstraintFix(cs, FixKind::AddConformance, locator), - IsContextual(isContextual), NonConformingType(type), - ProtocolType(protocolType) {} + : RequirementFix(cs, FixKind::AddConformance, type, protocolType, + locator), + IsContextual(isContextual) {} public: std::string getName() const override { @@ -610,8 +627,6 @@ class MissingConformance final : public ConstraintFix { bool diagnose(const Solution &solution, bool asNote = false) const override; - bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override; - static MissingConformance *forRequirement(ConstraintSystem &cs, Type type, Type protocolType, ConstraintLocator *locator); @@ -620,9 +635,9 @@ class MissingConformance final : public ConstraintFix { Type protocolType, ConstraintLocator *locator); - Type getNonConformingType() { return NonConformingType; } + Type getNonConformingType() const { return LHS; } - Type getProtocolType() { return ProtocolType; } + Type getProtocolType() const { return RHS; } bool isEqual(const ConstraintFix *other) const; @@ -633,13 +648,11 @@ class MissingConformance final : public ConstraintFix { /// Skip same-type generic requirement constraint, /// and assume that types are equal. -class SkipSameTypeRequirement final : public ConstraintFix { - Type LHS, RHS; - +class SkipSameTypeRequirement final : public RequirementFix { SkipSameTypeRequirement(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) - : ConstraintFix(cs, FixKind::SkipSameTypeRequirement, locator), LHS(lhs), - RHS(rhs) {} + : RequirementFix(cs, FixKind::SkipSameTypeRequirement, lhs, rhs, + locator) {} public: std::string getName() const override { @@ -648,9 +661,6 @@ class SkipSameTypeRequirement final : public ConstraintFix { bool diagnose(const Solution &solution, bool asNote = false) const override; - Type lhsType() { return LHS; } - Type rhsType() { return RHS; } - static SkipSameTypeRequirement *create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator); @@ -661,13 +671,11 @@ class SkipSameTypeRequirement final : public ConstraintFix { /// Skip same-shape generic requirement constraint, /// and assume that types are equal. -class SkipSameShapeRequirement final : public ConstraintFix { - Type LHS, RHS; - +class SkipSameShapeRequirement final : public RequirementFix { SkipSameShapeRequirement(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) - : ConstraintFix(cs, FixKind::SkipSameShapeRequirement, locator), LHS(lhs), - RHS(rhs) {} + : RequirementFix(cs, FixKind::SkipSameShapeRequirement, lhs, rhs, + locator) {} public: std::string getName() const override { @@ -676,9 +684,6 @@ class SkipSameShapeRequirement final : public ConstraintFix { bool diagnose(const Solution &solution, bool asNote = false) const override; - Type lhsType() { return LHS; } - Type rhsType() { return RHS; } - static SkipSameShapeRequirement *create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator); @@ -689,13 +694,11 @@ class SkipSameShapeRequirement final : public ConstraintFix { /// Skip 'superclass' generic requirement constraint, /// and assume that types are equal. -class SkipSuperclassRequirement final : public ConstraintFix { - Type LHS, RHS; - +class SkipSuperclassRequirement final : public RequirementFix { SkipSuperclassRequirement(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator) - : ConstraintFix(cs, FixKind::SkipSuperclassRequirement, locator), - LHS(lhs), RHS(rhs) {} + : RequirementFix(cs, FixKind::SkipSuperclassRequirement, lhs, rhs, + locator) {} public: std::string getName() const override { @@ -1839,6 +1842,10 @@ class AllowInaccessibleMember final : public AllowInvalidMemberRef { bool diagnose(const Solution &solution, bool asNote = false) const override; + bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override { + return diagnose(*commonFixes.front().first); + } + static AllowInaccessibleMember *create(ConstraintSystem &cs, Type baseType, ValueDecl *member, DeclNameRef name, ConstraintLocator *locator); diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 406d051909ef7..a71d6e807fe3c 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -2480,8 +2480,10 @@ ConstraintSystem::matchResultBuilder(AnyFunctionRef fn, Type builderType, } // Record the first unhandled construct as a fix. - if (recordFix(SkipUnhandledConstructInResultBuilder::create( - *this, unsupported, builder, getConstraintLocator(locator)))) { + if (recordFix( + SkipUnhandledConstructInResultBuilder::create( + *this, unsupported, builder, getConstraintLocator(locator)), + /*impact=*/100)) { return getTypeMatchFailure(locator); } diff --git a/lib/Sema/CSFix.cpp b/lib/Sema/CSFix.cpp index 2df5716eb5c98..5dde9f9f2b83f 100644 --- a/lib/Sema/CSFix.cpp +++ b/lib/Sema/CSFix.cpp @@ -400,25 +400,26 @@ bool MissingConformance::diagnose(const Solution &solution, bool asNote) const { auto &cs = solution.getConstraintSystem(); auto context = cs.getContextualTypePurpose(locator->getAnchor()); MissingContextualConformanceFailure failure( - solution, context, NonConformingType, ProtocolType, locator); + solution, context, getNonConformingType(), getProtocolType(), locator); return failure.diagnose(asNote); } MissingConformanceFailure failure( - solution, locator, std::make_pair(NonConformingType, ProtocolType)); + solution, locator, + std::make_pair(getNonConformingType(), getProtocolType())); return failure.diagnose(asNote); } -bool MissingConformance::diagnoseForAmbiguity( +bool RequirementFix::diagnoseForAmbiguity( CommonFixesArray commonFixes) const { - auto *primaryFix = commonFixes.front().second->getAs(); + auto *primaryFix = commonFixes.front().second; assert(primaryFix); if (llvm::all_of( commonFixes, [&primaryFix]( const std::pair &entry) { - return primaryFix->isEqual(entry.second); + return primaryFix->getLocator() == entry.second->getLocator(); })) return diagnose(*commonFixes.front().first); @@ -433,8 +434,9 @@ bool MissingConformance::isEqual(const ConstraintFix *other) const { return false; return IsContextual == conformanceFix->IsContextual && - NonConformingType->isEqual(conformanceFix->NonConformingType) && - ProtocolType->isEqual(conformanceFix->ProtocolType); + getNonConformingType()->isEqual( + conformanceFix->getNonConformingType()) && + getProtocolType()->isEqual(conformanceFix->getProtocolType()); } MissingConformance * diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 728ab5761946c..89b0c3c51bef2 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -4542,14 +4542,30 @@ repairViaOptionalUnwrap(ConstraintSystem &cs, Type fromType, Type toType, if (!anchor) return false; - bool possibleContextualMismatch = false; // If this is a conversion to a non-optional contextual type e.g. // `let _: Bool = try? foo()` and `foo()` produces `Int` // we should diagnose it as type mismatch instead of missing unwrap. - if (auto last = locator.last()) { - possibleContextualMismatch = last->is() && - !toType->getOptionalObjectType(); - } + bool possibleContextualMismatch = [&]() { + auto last = locator.last(); + if (!(last && last->is())) + return false; + + // If the contextual type is optional as well, it's definitely a + // missing unwrap. + if (toType->getOptionalObjectType()) + return false; + + // If this is a leading-dot syntax member chain with `?.` + // notation, it wouldn't be possible to infer the base type + // without the contextual type, so we have to treat it as + // a missing unwrap. + if (auto *OEE = getAsExpr(anchor)) { + if (isExpr(OEE->getSubExpr())) + return false; + } + + return true; + }(); // `OptionalEvaluationExpr` doesn't add a new level of // optionality but it could be hiding concrete types @@ -6014,6 +6030,12 @@ bool ConstraintSystem::repairFailures( } case ConstraintLocator::TupleElement: { + if (lhs->isPlaceholder() || rhs->isPlaceholder()) { + recordAnyTypeVarAsPotentialHole(lhs); + recordAnyTypeVarAsPotentialHole(rhs); + return true; + } + if (isExpr(anchor) || isExpr(anchor)) { // If we could record a generic arguments mismatch instead of this fix, // don't record a ContextualMismatch here. @@ -6152,6 +6174,17 @@ bool ConstraintSystem::repairFailures( if (rhs->isPlaceholder()) return true; + // The base is a placeholder, let's report an unknown base issue. + if (lhs->isPlaceholder()) { + auto *baseExpr = + castToExpr(anchor)->getChainBase(); + + auto *fix = SpecifyBaseTypeForContextualMember::create( + *this, baseExpr->getName(), getConstraintLocator(locator)); + conversionsOrFixes.push_back(fix); + break; + } + if (repairViaOptionalUnwrap(*this, lhs, rhs, matchKind, conversionsOrFixes, locator)) break; diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 592553f4e3b31..021dbdb77d457 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4373,6 +4373,67 @@ static bool diagnoseAmbiguityWithContextualType( return true; } +/// Diagnose problems with generic requirement fixes that are anchored on +/// one callee location. The list could contain different kinds of fixes +/// i.e. missing protocol conformances at different positions, +/// same-type requirement mismatches, etc. +static bool diagnoseAmbiguityWithGenericRequirements( + ConstraintSystem &cs, + ArrayRef> aggregate) { + // If all of the fixes point to the same overload choice, + // we can diagnose this an a single error. + bool hasNonDeclOverloads = false; + + llvm::SmallSet overloadChoices; + for (const auto &entry : aggregate) { + const auto &solution = *entry.first; + auto *calleeLocator = solution.getCalleeLocator(entry.second->getLocator()); + + if (auto overload = solution.getOverloadChoiceIfAvailable(calleeLocator)) { + if (auto *D = overload->choice.getDeclOrNull()) { + overloadChoices.insert(D); + } else { + hasNonDeclOverloads = true; + } + } + } + + auto &primaryFix = aggregate.front(); + { + if (overloadChoices.size() > 0) { + // Some of the choices are non-declaration, + // let's delegate that to ambiguity diagnostics. + if (hasNonDeclOverloads) + return false; + + if (overloadChoices.size() == 1) + return primaryFix.second->diagnose(*primaryFix.first); + + // fall through to the tailored ambiguity diagnostic. + } else { + // If there are no overload choices it means that + // the issue is with types, delegate that to the primary fix. + return primaryFix.second->diagnoseForAmbiguity(aggregate); + } + } + + // Produce "no exact matches" diagnostic. + auto &ctx = cs.getASTContext(); + auto *choice = *overloadChoices.begin(); + auto name = choice->getName(); + + ctx.Diags.diagnose(getLoc(primaryFix.second->getLocator()->getAnchor()), + diag::no_overloads_match_exactly_in_call, + /*isApplication=*/false, choice->getDescriptiveKind(), + name.isSpecial(), name.getBaseName()); + + for (const auto &entry : aggregate) { + entry.second->diagnose(*entry.first, /*asNote=*/true); + } + + return true; +} + static bool diagnoseAmbiguity( ConstraintSystem &cs, const SolutionDiff::OverloadDiff &ambiguity, ArrayRef> aggregateFix, @@ -4796,14 +4857,80 @@ bool ConstraintSystem::diagnoseAmbiguityWithFixes( // overload choices. fixes.set_subtract(consideredFixes); + // Aggregate all requirement fixes that belong to the same callee + // and attempt to diagnose possible ambiguities. + { + auto isResultBuilderMethodRef = [&](ASTNode node) { + auto *UDE = getAsExpr(node); + if (!(UDE && UDE->isImplicit())) + return false; + + auto &ctx = getASTContext(); + SmallVector builderMethods( + {ctx.Id_buildBlock, ctx.Id_buildExpression, ctx.Id_buildPartialBlock, + ctx.Id_buildFinalResult}); + + return llvm::any_of(builderMethods, [&](const Identifier &methodId) { + return UDE->getName().compare(DeclNameRef(methodId)) == 0; + }); + }; + + // Aggregates fixes fixes attached to `buildExpression` and `buildBlock` + // methods at the particular source location. + llvm::MapVector> + builderMethodRequirementFixes; + + llvm::MapVector> + perCalleeRequirementFixes; + + for (const auto &entry : fixes) { + auto *fix = entry.second; + if (!fix->getLocator()->isLastElement()) + continue; + + auto *calleeLoc = entry.first->getCalleeLocator(fix->getLocator()); + + if (isResultBuilderMethodRef(calleeLoc->getAnchor())) { + auto *anchor = castToExpr(calleeLoc->getAnchor()); + builderMethodRequirementFixes[anchor->getLoc()].push_back(entry); + } else { + perCalleeRequirementFixes[calleeLoc].push_back(entry); + } + } + + SmallVector, 4> viableGroups; + { + auto takeAggregateIfViable = + [&](SmallVector &aggregate) { + // Ambiguity only if all of the solutions have a requirement + // fix at the given location. + if (aggregate.size() == solutions.size()) + viableGroups.push_back(std::move(aggregate)); + }; + + for (auto &entry : builderMethodRequirementFixes) + takeAggregateIfViable(entry.second); + + for (auto &entry : perCalleeRequirementFixes) + takeAggregateIfViable(entry.second); + } + + for (auto &aggregate : viableGroups) { + if (diagnoseAmbiguityWithGenericRequirements(*this, aggregate)) { + // Remove diagnosed fixes. + fixes.set_subtract(aggregate); + diagnosed = true; + } + } + } + llvm::MapVector, SmallVector> fixesByKind; for (const auto &entry : fixes) { const auto *fix = entry.second; - fixesByKind[{fix->getKind(), fix->getLocator()}].push_back( - {entry.first, fix}); + fixesByKind[{fix->getKind(), fix->getLocator()}].push_back(entry); } // If leftover fix is contained in all of the solutions let's diff --git a/test/Constraints/generics.swift b/test/Constraints/generics.swift index 65ac50ab0ad2f..8cfa0bdcba833 100644 --- a/test/Constraints/generics.swift +++ b/test/Constraints/generics.swift @@ -996,3 +996,38 @@ do { // expected-error@-3 {{local function 'foo' requires the types 'Set.Type.Element' and 'Array.Type.Element' be equivalent}} // expected-note@-4 2 {{only concrete types such as structs, enums and classes can conform to protocols}} } + +// https://github.com/apple/swift/issues/56173 +protocol P_56173 { + associatedtype Element +} +protocol Q_56173 { + associatedtype Element +} + +func test_requirement_failures_in_ambiguous_context() { + struct A : P_56173 { + typealias Element = String + } + struct B : Q_56173 { + typealias Element = Int + } + + func f1(_: T, _: T) {} // expected-note {{where 'T' = 'A'}} + + f1(A(), B()) // expected-error {{local function 'f1' requires that 'A' conform to 'Equatable'}} + + func f2(_: T, _: U) {} + // expected-note@-1 {{candidate requires that 'B' conform to 'P_56173' (requirement specified as 'U' : 'P_56173')}} + func f2(_: T, _: U) {} + // expected-note@-1 {{candidate requires that 'A' conform to 'Q_56173' (requirement specified as 'T' : 'Q_56173')}} + + f2(A(), B()) // expected-error {{no exact matches in call to local function 'f2'}} + + func f3(_: T) where T.Element == Int {} + // expected-note@-1 {{candidate requires that the types 'A.Element' (aka 'String') and 'Int' be equivalent (requirement specified as 'T.Element' == 'Int')}} + func f3(_: U) where U.Element == String {} + // expected-note@-1 {{candidate requires that 'A' conform to 'Q_56173' (requirement specified as 'U' : 'Q_56173')}} + + f3(A()) // expected-error {{no exact matches in call to local function 'f3'}} +} diff --git a/test/Constraints/members.swift b/test/Constraints/members.swift index e6d88c45f27e8..d64649c02fbd6 100644 --- a/test/Constraints/members.swift +++ b/test/Constraints/members.swift @@ -783,3 +783,22 @@ func rdar92358570(_ x: RDAR92358570, _ y: RDAR92358570.doSomethingStatically() // expected-error {{referencing static method 'doSomethingStatically()' on 'RDAR92358570' requires that 'any SomeClassBound & ClassBoundProtocol' inherit from 'AnyObject'}} } + +func test_diagnose_inaccessible_member_in_ambiguous_context() { + struct S { + private var x: Int // expected-note {{'x' declared here}} + } + + func test(_: KeyPath, y: Int = 42) {} + func test(_: KeyPath, x: Int = 42) {} + + test(\.x) // expected-error {{'x' is inaccessible due to 'private' protection level}} +} + +// rdar://104302974 +func test_leading_dot_syntax_unknown_base_ambiguity() { + func fn(_: S, value: T?) {} + func fn(_: String, value: T?) {} + + fn("", value: .member) // expected-error {{cannot infer contextual base in reference to member 'member'}} +} diff --git a/test/Constraints/result_builder_diags.swift b/test/Constraints/result_builder_diags.swift index 894d65747d449..4a16619fdc36e 100644 --- a/test/Constraints/result_builder_diags.swift +++ b/test/Constraints/result_builder_diags.swift @@ -907,3 +907,96 @@ func test_associated_values_dont_block_solver_when_unresolved() { } } } + +func test_dependent_member_with_unresolved_base_type() { + struct Wrapper : P { + } + + @resultBuilder + struct Builder { + static func buildBlock(_ value: some P) -> some P { + value + } + } + + func test(data: KeyPath, // expected-note {{in call to function 'test(data:_:)'}} + @Builder _: () -> U) -> Wrapper { fatalError() } + + struct Value : P { + typealias T = (Int, String) + } + + struct Test : P { + struct T { + var values: [(Int, Value.T)] + } + + var v: some P { + test(data: \T.values) { // expected-error {{generic parameter 'U' could not be inferred}} + Value() + } + } + } +} + +// rdar://89880662 - incorrect error about unsupported control flow statement +func test_impact_of_control_flow_fix() { + @resultBuilder + struct BuilderA { + static func buildOptional(_ value: T?) -> T? { return value } + static func buildBlock(_ v1: T) -> T { v1 } + static func buildBlock(_ v1: T, _ v2: U) -> (T, U) { (v1, v2) } + } + + @resultBuilder + struct BuilderB { + static func buildBlock(_ v1: T) -> T { v1 } + static func buildBlock(_ v1: T, _ v2: U) -> (T, U) { (v1, v2) } + } + + func fn(@BuilderA _: () -> T) {} + func fn(@BuilderB _: () -> (Int?, Void)) {} + + func test(_: Int) {} + + fn { + if true { + 0 + } + + test("") // expected-error {{cannot convert value of type 'String' to expected argument type 'Int'}} + } +} + +protocol Q {} + +func test_requirement_failure_in_buildBlock() { + struct A : P { typealias T = Int } + struct B : Q { typealias T = String } + + struct Result : P { typealias T = Int } + + @resultBuilder + struct BuilderA { + static func buildBlock(_: T0, _: T1) -> Result<(T0, T1)> { fatalError() } + // expected-note@-1 {{candidate requires that 'B' conform to 'P' (requirement specified as 'T1' : 'P')}} + } + + @resultBuilder + struct BuilderB { + static func buildBlock(_ v: U0, _: U1) -> some Q { v } + // expected-note@-1 {{candidate requires that 'A' conform to 'Q' (requirement specified as 'U0' : 'Q')}} + } + + struct Test { + func fn(@BuilderA _: () -> T) {} + func fn(@BuilderB _: () -> T) {} + + func test() { + fn { // expected-error {{no exact matches in reference to static method 'buildBlock'}} + A() + B() + } + } + } +} diff --git a/test/expr/delayed-ident/member_chains.swift b/test/expr/delayed-ident/member_chains.swift index fafc1e00df67a..ad66be1bec895 100644 --- a/test/expr/delayed-ident/member_chains.swift +++ b/test/expr/delayed-ident/member_chains.swift @@ -148,10 +148,12 @@ let _: ImplicitMembers = .implicit.getAnotherOptional() // expected-error {{valu let _: ImplicitMembers = .implicit[optional: ()] // expected-error {{value of optional type 'ImplicitMembers?' must be unwrapped to a value of type 'ImplicitMembers'}} expected-note {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{49-49= ?? <#default value#>}} expected-note {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{49-49=!}} let _: ImplicitMembers = .implicit[funcOptional: ()]() // expected-error {{value of optional type 'ImplicitMembers?' must be unwrapped to a value of type 'ImplicitMembers'}} expected-note {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{55-55= ?? <#default value#>}} expected-note {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{55-55=!}} -// FIXME: Improve these diagnostics (should probably offer unwrapping, as above) -let _: ImplicitMembers = .implicit.anotherOptional?.another // expected-error{{cannot convert value of type 'Optional<_>' to specified type 'ImplicitMembers'}} -let _: ImplicitMembers = .implicit[optionalFunc: ()]?() // expected-error{{cannot convert value of type 'Optional<_>' to specified type 'ImplicitMembers'}} - +let _: ImplicitMembers = .implicit.anotherOptional?.another // expected-error {{value of optional type 'ImplicitMembers?' must be unwrapped to a value of type 'ImplicitMembers'}} +// expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{60-60= ?? <#default value#>}} +// expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{26-26=(}} {{60-60=)!}} +let _: ImplicitMembers = .implicit[optionalFunc: ()]?() // expected-error{{value of optional type 'ImplicitMembers?' must be unwrapped to a value of type 'ImplicitMembers'}} +// expected-note@-1 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} {{56-56= ?? <#default value#>}} +// expected-note@-2 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} {{26-26=(}} {{56-56=)!}} let _: ImplicitMembers = .other.implicit let _: ImplicitMembers = .implicit.anotherOther.implicit diff --git a/test/expr/expressions.swift b/test/expr/expressions.swift index 8761c900707ad..b755498b8ef89 100644 --- a/test/expr/expressions.swift +++ b/test/expr/expressions.swift @@ -748,8 +748,9 @@ func invalidDictionaryLiteral() { [4].joined(separator: [1]) -// expected-error@-1 {{cannot convert value of type 'Int' to expected element type 'String'}} -// expected-error@-2 {{cannot convert value of type '[Int]' to expected argument type 'String'}} +// expected-error@-1 {{no exact matches in call to instance method 'joined'}} +// expected-note@-2 {{found candidate with type '(String) -> String'}} +// There is one more note here - candidate requires that 'Int' conform to 'Sequence' (requirement specified as 'Self.Element' : 'Sequence') pointing to Sequence extension [4].joined(separator: [[[1]]]) // expected-error@-1 {{cannot convert value of type 'Int' to expected element type 'String'}}