From cdd24b4ee9d8956513a4b6d5cf31107a4bba5dbf Mon Sep 17 00:00:00 2001 From: fischertony Date: Fri, 7 Sep 2018 13:04:30 +0300 Subject: [PATCH 1/5] [Parse] Support for? syntax --- include/swift/AST/Stmt.h | 15 ++++++++++++--- lib/Parse/ParseStmt.cpp | 12 ++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/include/swift/AST/Stmt.h b/include/swift/AST/Stmt.h index f879ccb0d8648..0e4b0f57a660c 100644 --- a/include/swift/AST/Stmt.h +++ b/include/swift/AST/Stmt.h @@ -798,6 +798,7 @@ class ForEachStmt : public LabeledStmt { Expr *Sequence; Expr *WhereExpr = nullptr; BraceStmt *Body; + bool isOptional; /// The iterator variable along with its initializer. PatternBindingDecl *Iterator = nullptr; @@ -808,11 +809,11 @@ class ForEachStmt : public LabeledStmt { public: ForEachStmt(LabeledStmtInfo LabelInfo, SourceLoc ForLoc, Pattern *Pat, SourceLoc InLoc, Expr *Sequence, Expr *WhereExpr, BraceStmt *Body, - Optional implicit = None) + bool isOptional, Optional implicit = None) : LabeledStmt(StmtKind::ForEach, getDefaultImplicitFlag(implicit, ForLoc), LabelInfo), ForLoc(ForLoc), Pat(nullptr), InLoc(InLoc), Sequence(Sequence), - WhereExpr(WhereExpr), Body(Body) { + WhereExpr(WhereExpr), Body(Body), isOptional(isOptional) { setPattern(Pat); } @@ -821,7 +822,15 @@ class ForEachStmt : public LabeledStmt { /// getInLoc - Retrieve the location of the 'in' keyword. SourceLoc getInLoc() const { return InLoc; } - + + /// Retrieve the location of '?' in 'for?', present when the iteration + /// is optional. + SourceLoc getQuestionLoc() const { + return ForLoc.isInvalid() ? ForLoc : ForLoc.getAdvancedLoc(3); + } + + bool getIsOptional() const { return isOptional; } + /// getPattern - Retrieve the pattern describing the iteration variables. /// These variables will only be visible within the body of the loop. Pattern *getPattern() const { return Pat; } diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index a17b717fb8522..ac2587a9e562d 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -2003,7 +2003,7 @@ static bool isStmtForCStyle(Parser &P) { /// /// stmt-for-each: -/// (identifier ':')? 'for' pattern 'in' expr-basic \ +/// (identifier ':')? 'for'(?)? pattern 'in' expr-basic \ /// ('where' expr-basic)? stmt-brace ParserResult Parser::parseStmtForEach(LabeledStmtInfo LabelInfo) { SyntaxContext->setCreateSyntax(SyntaxKind::ForInStmt); @@ -2011,6 +2011,7 @@ ParserResult Parser::parseStmtForEach(LabeledStmtInfo LabelInfo) { ParserStatus Status; ParserResult pattern; ParserResult Container; + bool isOptional = false; // The C-style for loop which was supported in Swift2 and foreach-style-for // loop are conflated together into a single keyword, so we have to do some @@ -2018,6 +2019,9 @@ ParserResult Parser::parseStmtForEach(LabeledStmtInfo LabelInfo) { bool IsCStyleFor = isStmtForCStyle(*this); auto StartOfControl = Tok.getLoc(); + if (consumeIf(tok::question_postfix)) + isOptional = true; + // Parse the pattern. This is either 'case ' or just a // normal pattern. if (consumeIf(tok::kw_case)) { @@ -2122,9 +2126,9 @@ ParserResult Parser::parseStmtForEach(LabeledStmtInfo LabelInfo) { return makeParserResult( Status, - new (Context) ForEachStmt(LabelInfo, ForLoc, pattern.get(), InLoc, - Container.get(), Where.getPtrOrNull(), - Body.get())); + new (Context) ForEachStmt(LabelInfo, ForLoc, pattern.get(), + InLoc, Container.get(), Where.getPtrOrNull(), + Body.get(), isOptional)); } /// From dd14b26dd36ca88d654ecc70c346b9bd4025ec8e Mon Sep 17 00:00:00 2001 From: fischertony Date: Fri, 7 Sep 2018 13:14:30 +0300 Subject: [PATCH 2/5] [TypeChecker] Teach the typechecker to expect optional sequences in a for-in loop. Adapt 'callWitness' to build optional evaluation expressions when needed. --- lib/Sema/CSApply.cpp | 22 ++++++++++++++++- lib/Sema/TypeCheckConstraints.cpp | 17 ++++++++++--- lib/Sema/TypeCheckStmt.cpp | 40 +++++++++++++++++++++++-------- 3 files changed, 65 insertions(+), 14 deletions(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index d20bcbcab0e1f..6cd871f181ec3 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -7868,7 +7868,7 @@ Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc, if (auto metaType = type->getAs()) type = metaType->getInstanceType(); - + auto witness = findNamedWitnessImpl( *this, dc, type->getRValueType(), protocol, name, brokenProtocolDiag); @@ -7972,6 +7972,26 @@ Expr *TypeChecker::callWitness(Expr *base, DeclContext *dc, if (!result) return nullptr; + if (dyn_cast(base)) { + Expr *OEE = nullptr; + if (cs.getType(call)->getOptionalObjectType()) { + OEE = new (Context) OptionalEvaluationExpr(call); + OEE->setType(cs.getType(call)); + } else { + Expr *IIO = new (Context) InjectIntoOptionalExpr( + call, OptionalType::get(cs.getType(call))); + OEE = new (Context) OptionalEvaluationExpr(IIO); + OEE->setType(IIO->getType()); + } + OEE->print(llvm::outs());llvm::outs()<<"\n\n&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&\n\n"; +// auto result = rewriter.visitOptionalEvaluationExpr(oee); + +// if (!OEE) +// return nullptr; +// rewriter.finalize(oee); + return OEE; + } + rewriter.finalize(result); return result; } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index cd8801909a922..b731a498aaa93 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2544,11 +2544,22 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { } SequenceType = cs.createTypeVariable(Locator); - cs.addConstraint(ConstraintKind::Conversion, cs.getType(expr), + auto exprTy = cs.getType(expr); + + cs.addConstraint(ConstraintKind::Conversion, exprTy, SequenceType, Locator); - cs.addConstraint(ConstraintKind::ConformsTo, SequenceType, - sequenceProto->getDeclaredType(), Locator); + if (exprTy->getOptionalObjectType()) { + auto T2 = cs.createTypeVariable(Locator); + + cs.addConstraint(ConstraintKind::OptionalObject, SequenceType, + T2, Locator); + cs.addConstraint(ConstraintKind::ConformsTo, T2, + sequenceProto->getDeclaredType(), Locator); + } else { + cs.addConstraint(ConstraintKind::ConformsTo, SequenceType, + sequenceProto->getDeclaredType(), Locator); + } auto iteratorLocator = cs.getConstraintLocator(Locator, ConstraintLocator::SequenceIteratorProtocol); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index dc195599f0faa..8cb8874da600d 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -673,11 +673,19 @@ class StmtChecker : public StmtVisitor { } Expr *sequence = S->getSequence(); + auto seqOptObjTy = sequence->getType()->getOptionalObjectType(); + if (seqOptObjTy) { + sequence = new (TC.Context) BindOptionalExpr(sequence, + sequence->getEndLoc(), 0); + sequence->setType(seqOptObjTy); + } // Invoke iterator() to get an iterator from the sequence. Type generatorTy; VarDecl *generator; { + // If the sequence is optional, check the object type for conformance + // instead. Type sequenceType = sequence->getType(); auto conformance = TC.conformsToProtocol(sequenceType, sequenceProto, DC, @@ -693,23 +701,29 @@ class StmtChecker : public StmtVisitor { if (!generatorTy) return nullptr; - + Expr *getIterator = TC.callWitness(sequence, DC, sequenceProto, *conformance, TC.Context.Id_makeIterator, {}, diag::sequence_protocol_broken); if (!getIterator) return nullptr; - +llvm::outs()<<"=============================================1-5\n"; // Create a local variable to capture the generator. std::string name; if (auto np = dyn_cast_or_null(S->getPattern())) name = "$"+np->getBoundName().str().str(); name += "$generator"; + + // If the sequence is optional, the actual generator type will be + // optional as well. + auto actualGeneratorTy = seqOptObjTy + ? TC.getOptionalType(sequence->getStartLoc(), generatorTy) + : generatorTy; generator = new (TC.Context) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Var, /*IsCaptureList*/false, S->getInLoc(), TC.Context.getIdentifier(name), DC); - generator->setType(generatorTy); - generator->setInterfaceType(generatorTy->mapTypeOutOfContext()); + generator->setType(actualGeneratorTy); + generator->setInterfaceType(actualGeneratorTy->mapTypeOutOfContext()); generator->setImplicit(); // Create a pattern binding to initialize the generator. @@ -741,15 +755,21 @@ class StmtChecker : public StmtVisitor { diag::iterator_protocol_broken); if (!elementTy) return nullptr; - + // Compute the expression that advances the generator. + auto generatorExpr = TC.buildCheckedRefExpr( + generator, DC, DeclNameLoc(S->getInLoc()), /*implicit*/true); + if (seqOptObjTy) { + generatorExpr = new (TC.Context) BindOptionalExpr( + generatorExpr, generatorExpr->getEndLoc(), 0); + generatorExpr->setType(LValueType::get(generatorTy)); + } Expr *iteratorNext - = TC.callWitness(TC.buildCheckedRefExpr(generator, DC, - DeclNameLoc(S->getInLoc()), - /*implicit*/true), - DC, generatorProto, *genConformance, + = TC.callWitness(generatorExpr, DC, generatorProto, *genConformance, TC.Context.Id_next, {}, diag::iterator_protocol_broken); + if (!iteratorNext) return nullptr; + llvm::outs()<<"=============================================1-7\n"; // Check that next() produces an Optional value. if (iteratorNext->getType()->getAnyNominal() != TC.Context.getOptionalDecl()) { @@ -771,7 +791,7 @@ class StmtChecker : public StmtVisitor { BraceStmt *Body = S->getBody(); typeCheckStmt(Body); S->setBody(Body); - + return S; } From 4396571d068c1d7782388707eb10444c7966b9b0 Mon Sep 17 00:00:00 2001 From: fischertony Date: Fri, 7 Sep 2018 13:58:45 +0300 Subject: [PATCH 3/5] [Sema][Diag] Provide diagnostics for invalid 'for?' use Amend the 'must be unwrapped' error when a optional sequence is used in a for-in loop. The additional and preferred fixit becomes 'use for?'. This is a problem since we don't have a way to propagate the stmt to CSDiagnostics.cpp, where this diagnostic is emitted. But we don't really need to: in the case of for-in loops, this error can be diagnosed outside of the constraint system. Currently, it is diagnosed in 'performStmtDiagnostics' in MiscDiagnostics.cpp. However, I had to ultimalety #include ConstraintSystem.h to be able to call the fixit methods and avoid duplicating logic. --- include/swift/AST/DiagnosticsSema.def | 7 +++ lib/Sema/CSDiag.cpp | 1 + lib/Sema/CSDiagnostics.cpp | 64 ++----------------- lib/Sema/MiscDiagnostics.cpp | 91 ++++++++++++++++++++++++++- lib/Sema/MiscDiagnostics.h | 11 +++- lib/Sema/TypeCheckStmt.cpp | 2 +- 6 files changed, 114 insertions(+), 62 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 8e1ce4877436e..d69d7b3bb8438 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -3142,6 +3142,13 @@ ERROR(continue_outside_loop,none, ERROR(continue_not_in_this_stmt,none, "'continue' cannot be used with %0 statements", (StringRef)) +ERROR(foreach_sequence_not_optional,none, + "optional for-in loop must not be used on non-optional sequence of type %0", + (Type)) +NOTE(to_optional_foreach_fixit,none, + "use 'for?' to ignore the loop when the optional value contains 'nil'", ()) +NOTE(to_nonoptional_foreach_fixit,none, "", ()) + // Switch Stmt ERROR(no_match_operator,none, "no binary '~=' operator available for 'switch' statement", ()) diff --git a/lib/Sema/CSDiag.cpp b/lib/Sema/CSDiag.cpp index 44ac2bdebf9f4..1fcc1a1faa2c3 100644 --- a/lib/Sema/CSDiag.cpp +++ b/lib/Sema/CSDiag.cpp @@ -8110,6 +8110,7 @@ bool FailureDiagnosis::diagnoseExprFailure() { // be type checked on its own (even to an incomplete type) then that is where // we focus our attention. If we do find a type, we use it to check for // contextual type mismatches. + expr->print(llvm::outs());llvm::outs()<<" ±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±\n"; return visit(expr); } diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 12b7384256427..617bc40aa891a 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -407,62 +407,6 @@ bool MemberAccessOnOptionalBaseFailure::diagnoseAsError() { resultIsOptional, SourceRange()); } -// Suggest a default value via ?? -static void offerDefaultValueUnwrapFixit(TypeChecker &TC, DeclContext *DC, Expr *expr) { - auto diag = - TC.diagnose(expr->getLoc(), diag::unwrap_with_default_value); - - // Figure out what we need to parenthesize. - bool needsParensInside = - exprNeedsParensBeforeAddingNilCoalescing(TC, DC, expr); - bool needsParensOutside = - exprNeedsParensAfterAddingNilCoalescing(TC, DC, expr, expr); - - llvm::SmallString<2> insertBefore; - llvm::SmallString<32> insertAfter; - if (needsParensOutside) { - insertBefore += "("; - } - if (needsParensInside) { - insertBefore += "("; - insertAfter += ")"; - } - insertAfter += " ?? <" "#default value#" ">"; - if (needsParensOutside) - insertAfter += ")"; - - if (!insertBefore.empty()) { - diag.fixItInsert(expr->getStartLoc(), insertBefore); - } - diag.fixItInsertAfter(expr->getEndLoc(), insertAfter); -} - -// Suggest a force-unwrap. -static void offerForceUnwrapFixit(ConstraintSystem &CS, Expr *expr) { - auto diag = CS.TC.diagnose(expr->getLoc(), diag::unwrap_with_force_value); - - // If expr is optional as the result of an optional chain and this last - // dot isn't a member returning optional, then offer to force the last - // link in the chain, rather than an ugly parenthesized postfix force. - if (auto optionalChain = dyn_cast(expr)) { - if (auto dotExpr = - dyn_cast(optionalChain->getSubExpr())) { - auto bind = dyn_cast(dotExpr->getBase()); - if (bind && !CS.getType(dotExpr)->getOptionalObjectType()) { - diag.fixItReplace(SourceRange(bind->getLoc()), "!"); - return; - } - } - } - - if (expr->canAppendPostfixExpression(true)) { - diag.fixItInsertAfter(expr->getEndLoc(), "!"); - } else { - diag.fixItInsert(expr->getStartLoc(), "(") - .fixItInsertAfter(expr->getEndLoc(), ")!"); - } -} - class VarDeclMultipleReferencesChecker : public ASTWalker { VarDecl *varDecl; int count; @@ -488,6 +432,10 @@ static bool diagnoseUnwrap(ConstraintSystem &CS, Expr *expr, Type type) { CS.TC.diagnose(expr->getLoc(), diag::optional_not_unwrapped, type, unwrappedType); + auto getType = [&](const Expr *E) -> Type { + return CS.getType(E); + }; + // If the expression we're unwrapping is the only reference to a // local variable whose type isn't explicit in the source, then // offer unwrapping fixits on the initializer as well. @@ -530,13 +478,13 @@ static bool diagnoseUnwrap(ConstraintSystem &CS, Expr *expr, Type type) { offerDefaultValueUnwrapFixit(CS.TC, varDecl->getDeclContext(), initializer); - offerForceUnwrapFixit(CS, initializer); + offerForceUnwrapFixit(initializer, CS.TC, getType); } } } offerDefaultValueUnwrapFixit(CS.TC, CS.DC, expr); - offerForceUnwrapFixit(CS, expr); + offerForceUnwrapFixit(expr, CS.TC, getType); return true; } diff --git a/lib/Sema/MiscDiagnostics.cpp b/lib/Sema/MiscDiagnostics.cpp index 93bc1ec402432..22c40518e4d4b 100644 --- a/lib/Sema/MiscDiagnostics.cpp +++ b/lib/Sema/MiscDiagnostics.cpp @@ -16,6 +16,7 @@ #include "MiscDiagnostics.h" #include "TypeChecker.h" +#include "ConstraintSystem.h" #include "TypeCheckAvailability.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/NameLookup.h" @@ -2979,6 +2980,33 @@ static void checkSwitch(TypeChecker &TC, const SwitchStmt *stmt) { } } +static void checkForEach(TypeChecker &TC, DeclContext *DC, + const ForEachStmt *stmt) { + const auto seq = stmt->getSequence(); + const auto seqTy = seq->getType(); + + auto unwrappedTy = seqTy->getOptionalObjectType(); + + if (unwrappedTy) { + if (!stmt->getIsOptional()) { + TC.diagnose(seq->getLoc(), diag::optional_not_unwrapped, seqTy, + unwrappedTy); + + TC.diagnose(seq->getLoc(), diag::to_optional_foreach_fixit) + .fixItInsert(stmt->getQuestionLoc(), + getTokenText(tok::question_postfix)); + + offerDefaultValueUnwrapFixit(TC, DC, seq); + offerForceUnwrapFixit(seq, TC); + } + } else if (stmt->getIsOptional()) { + TC.diagnose(stmt->getForLoc(), diag::foreach_sequence_not_optional, + seqTy) + .highlight(seq->getSourceRange()) + .fixItRemove(stmt->getQuestionLoc()); + } +} + void swift::fixItEncloseTrailingClosure(TypeChecker &TC, InFlightDiagnostic &diag, const CallExpr *call, @@ -3882,6 +3910,62 @@ static void diagnoseDeprecatedWritableKeyPath(TypeChecker &TC, const Expr *E, const_cast(E)->walk(Walker); } +void swift::offerDefaultValueUnwrapFixit(TypeChecker &TC, DeclContext *DC, + Expr *expr) { + auto diag = + TC.diagnose(expr->getLoc(), diag::unwrap_with_default_value); + + // Figure out what we need to parenthesize. + bool needsParensInside = + exprNeedsParensBeforeAddingNilCoalescing(TC, DC, expr); + bool needsParensOutside = + exprNeedsParensAfterAddingNilCoalescing(TC, DC, expr, expr); + + llvm::SmallString<2> insertBefore; + llvm::SmallString<32> insertAfter; + if (needsParensOutside) { + insertBefore += "("; + } + if (needsParensInside) { + insertBefore += "("; + insertAfter += ")"; + } + insertAfter += " ?? <" "#default value#" ">"; + if (needsParensOutside) + insertAfter += ")"; + + if (!insertBefore.empty()) { + diag.fixItInsert(expr->getStartLoc(), insertBefore); + } + diag.fixItInsertAfter(expr->getEndLoc(), insertAfter); +} + +void swift::offerForceUnwrapFixit(Expr *expr, TypeChecker &TC, + llvm::function_ref getType) { + auto diag = TC.diagnose(expr->getLoc(), diag::unwrap_with_force_value); + + // If expr is optional as the result of an optional chain and this last + // dot isn't a member returning optional, then offer to force the last + // link in the chain, rather than an ugly parenthesized postfix force. + if (auto optionalChain = dyn_cast(expr)) { + if (auto dotExpr = + dyn_cast(optionalChain->getSubExpr())) { + auto bind = dyn_cast(dotExpr->getBase()); + if (bind && !getType(dotExpr)->getOptionalObjectType()) { + diag.fixItReplace(SourceRange(bind->getLoc()), "!"); + return; + } + } + } + + if (expr->canAppendPostfixExpression(true)) { + diag.fixItInsertAfter(expr->getEndLoc(), "!"); + } else { + diag.fixItInsert(expr->getStartLoc(), "(") + .fixItInsertAfter(expr->getEndLoc(), ")!"); + } +} + //===----------------------------------------------------------------------===// // High-level entry points. //===----------------------------------------------------------------------===// @@ -3903,11 +3987,14 @@ void swift::performSyntacticExprDiagnostics(TypeChecker &TC, const Expr *E, diagDeprecatedObjCSelectors(TC, DC, E); } -void swift::performStmtDiagnostics(TypeChecker &TC, const Stmt *S) { +void swift::performStmtDiagnostics(TypeChecker &TC, DeclContext *DC, const Stmt *S) { TC.checkUnsupportedProtocolType(const_cast(S)); - if (auto switchStmt = dyn_cast(S)) + if (auto switchStmt = dyn_cast(S)) { checkSwitch(TC, switchStmt); + } else if (auto forEachStmt = dyn_cast(S)) { + checkForEach(TC, DC, forEachStmt); + } checkStmtConditionTrailingClosure(TC, S); diff --git a/lib/Sema/MiscDiagnostics.h b/lib/Sema/MiscDiagnostics.h index b6a57d4daebe4..fc2f80c198f76 100644 --- a/lib/Sema/MiscDiagnostics.h +++ b/lib/Sema/MiscDiagnostics.h @@ -40,7 +40,7 @@ void performSyntacticExprDiagnostics(TypeChecker &TC, const Expr *E, bool isExprStmt); /// \brief Emit diagnostics for a given statement. -void performStmtDiagnostics(TypeChecker &TC, const Stmt *S); +void performStmtDiagnostics(TypeChecker &TC, DeclContext *DC, const Stmt *S); void performAbstractFuncDeclDiagnostics(TypeChecker &TC, AbstractFunctionDecl *AFD); @@ -97,6 +97,15 @@ void fixItEncloseTrailingClosure(TypeChecker &TC, const CallExpr *call, Identifier closureLabel); +// Suggest a default value via ?? +void offerDefaultValueUnwrapFixit(TypeChecker &TC, DeclContext *DC, Expr *expr); + +// Suggest a force-unwrap. +void offerForceUnwrapFixit(Expr *expr, TypeChecker &TC, + llvm::function_ref getType = + [](const Expr *E) -> Type { + return E->getType(); + }); } // namespace swift #endif // SWIFT_SEMA_MISC_DIAGNOSTICS_H diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 8cb8874da600d..8eae5b50a1bb7 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -391,7 +391,7 @@ class StmtChecker : public StmtVisitor { if (S2 == nullptr) return true; S = S2; - performStmtDiagnostics(TC, S); + performStmtDiagnostics(TC, DC, S); return false; } From 499523f0ece93ab4acb243d34139f7acc0e65349 Mon Sep 17 00:00:00 2001 From: fischertony Date: Fri, 7 Sep 2018 14:03:42 +0300 Subject: [PATCH 4/5] [Test] --- test/stmt/foreach.swift | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/test/stmt/foreach.swift b/test/stmt/foreach.swift index 2095a6f970f47..99d3d929fe540 100644 --- a/test/stmt/foreach.swift +++ b/test/stmt/foreach.swift @@ -167,11 +167,23 @@ func testMatchingPatterns() { // QoI: diagnostic for for-each over an optional sequence isn't great func testOptionalSequence() { - let array : [Int]? - for x in array { // expected-error {{value of optional type '[Int]?' must be unwrapped}} - // expected-note@-1{{coalesce}} - // expected-note@-2{{force-unwrap}} - } + let array : [Int]? = nil + for x in array { } // expected-warning {{immutable value}} + // expected-error@-1 {{value of optional type '[Int]?' must be unwrapped}} + // expected-note@-2 {{use 'for?'}} {{6-6=?}} + // expected-note@-3 {{coalesce}} + // expected-note@-4 {{force-unwrap}} + + let array1 : [Int] = [] + let array2 : [Int?]? = nil + + for? x in array1 { } // expected-warning {{immutable value}} + // expected-error@-1{{optional for-in loop must not be used on non-optional sequence of type '[Int]'}} {{6-7=}} + + for? _ in array { } // Ok + for? x in array where x > 0 { } // Ok + for? case .some(let x) in array2 { } // expected-warning {{immutable value}} + for? x in Optional.some(0...10) { } // expected-warning {{immutable value}} } // Crash with (invalid) for each over an existential From a8db4df40296eb49616c7aadc2d5b7d19f4ebdfa Mon Sep 17 00:00:00 2001 From: fischertony Date: Fri, 14 Sep 2018 00:23:59 +0300 Subject: [PATCH 5/5] [Gardening] Typos --- lib/Sema/TypeCheckConstraints.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index b731a498aaa93..83eba5a07f8db 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -2535,7 +2535,7 @@ bool TypeChecker::typeCheckForEachBinding(DeclContext *dc, ForEachStmt *stmt) { // Save the locator we're using for the expression. Locator = cs.getConstraintLocator(expr); - // The expression type must conform to the Sequence. + // The expression type must conform to Sequence. auto &tc = cs.getTypeChecker(); ProtocolDecl *sequenceProto = tc.getProtocol(Stmt->getForLoc(), KnownProtocolKind::Sequence);