| //===--- TypeCheckStmt.cpp - Type Checking for Statements -----------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements semantic analysis for statements. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "TypeChecker.h" |
| #include "TypeCheckAvailability.h" |
| #include "TypeCheckConcurrency.h" |
| #include "TypeCheckType.h" |
| #include "MiscDiagnostics.h" |
| #include "swift/Subsystems.h" |
| #include "swift/AST/ASTPrinter.h" |
| #include "swift/AST/ASTScope.h" |
| #include "swift/AST/ASTWalker.h" |
| #include "swift/AST/ASTVisitor.h" |
| #include "swift/AST/DiagnosticsSema.h" |
| #include "swift/AST/DiagnosticSuppression.h" |
| #include "swift/AST/Identifier.h" |
| #include "swift/AST/Initializer.h" |
| #include "swift/AST/NameLookup.h" |
| #include "swift/AST/ParameterList.h" |
| #include "swift/AST/PrettyStackTrace.h" |
| #include "swift/AST/SourceFile.h" |
| #include "swift/AST/TypeCheckRequests.h" |
| #include "swift/Basic/Range.h" |
| #include "swift/Basic/STLExtras.h" |
| #include "swift/Basic/SourceManager.h" |
| #include "swift/Basic/Statistic.h" |
| #include "swift/Basic/TopCollection.h" |
| #include "swift/Parse/Lexer.h" |
| #include "swift/Parse/LocalContext.h" |
| #include "swift/Sema/IDETypeChecking.h" |
| #include "swift/Syntax/TokenKinds.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/PointerUnion.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/TinyPtrVector.h" |
| #include "llvm/ADT/Twine.h" |
| #include "llvm/Support/Compiler.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/Format.h" |
| #include "llvm/Support/Timer.h" |
| #include <iterator> |
| |
| using namespace swift; |
| |
| #define DEBUG_TYPE "TypeCheckStmt" |
| |
| #ifndef NDEBUG |
| /// Determine whether the given context is for the backing property of a |
| /// property wrapper. |
| static bool isPropertyWrapperBackingInitContext(DeclContext *dc) { |
| auto initContext = dyn_cast<Initializer>(dc); |
| if (!initContext) return false; |
| |
| auto patternInitContext = dyn_cast<PatternBindingInitializer>(initContext); |
| if (!patternInitContext) return false; |
| |
| auto binding = patternInitContext->getBinding(); |
| if (!binding) return false; |
| |
| auto singleVar = binding->getSingleVar(); |
| if (!singleVar) return false; |
| |
| return singleVar->getOriginalWrappedProperty() != nullptr; |
| } |
| #endif |
| |
| namespace { |
| class ContextualizeClosures : public ASTWalker { |
| DeclContext *ParentDC; |
| public: |
| unsigned NextDiscriminator = 0; |
| |
| ContextualizeClosures(DeclContext *parent, |
| unsigned nextDiscriminator = 0) |
| : ParentDC(parent), NextDiscriminator(nextDiscriminator) {} |
| |
| std::pair<bool, Expr *> walkToExprPre(Expr *E) override { |
| // Autoclosures need to be numbered and potentially reparented. |
| // Reparenting is required with: |
| // - nested autoclosures, because the inner autoclosure will be |
| // parented to the outer context, not the outer autoclosure |
| // - non-local initializers |
| if (auto CE = dyn_cast<AutoClosureExpr>(E)) { |
| // FIXME: Work around an apparent reentrancy problem with the REPL. |
| // I don't understand what's going on here well enough to fix the |
| // underlying issue. -Joe |
| if (CE->getParent() == ParentDC |
| && CE->getDiscriminator() != AutoClosureExpr::InvalidDiscriminator) |
| return { false, E }; |
| |
| assert(CE->getDiscriminator() == AutoClosureExpr::InvalidDiscriminator); |
| CE->setDiscriminator(NextDiscriminator++); |
| CE->setParent(ParentDC); |
| |
| // Recurse into the autoclosure body using the same sequence, |
| // but parenting to the autoclosure instead of the outer closure. |
| auto oldParentDC = ParentDC; |
| ParentDC = CE; |
| CE->getBody()->walk(*this); |
| ParentDC = oldParentDC; |
| |
| TypeChecker::computeCaptures(CE); |
| return { false, E }; |
| } |
| |
| // Capture lists need to be reparented to enclosing autoclosures. |
| if (auto CapE = dyn_cast<CaptureListExpr>(E)) { |
| if (isa<AutoClosureExpr>(ParentDC)) { |
| for (auto &Cap : CapE->getCaptureList()) { |
| Cap.Init->setDeclContext(ParentDC); |
| Cap.Var->setDeclContext(ParentDC); |
| } |
| } |
| } |
| |
| // Explicit closures start their own sequence. |
| if (auto CE = dyn_cast<ClosureExpr>(E)) { |
| // In the repl, the parent top-level context may have been re-written. |
| if (CE->getParent() != ParentDC) { |
| if ((CE->getParent()->getContextKind() != |
| ParentDC->getContextKind()) || |
| ParentDC->getContextKind() != DeclContextKind::TopLevelCodeDecl) { |
| // If a closure is nested within an auto closure, we'll need to update |
| // its parent to the auto closure parent. |
| assert((ParentDC->getContextKind() == |
| DeclContextKind::AbstractClosureExpr || |
| isPropertyWrapperBackingInitContext(ParentDC)) && |
| "Incorrect parent decl context for closure"); |
| CE->setParent(ParentDC); |
| } |
| } |
| |
| // If the closure was type checked within its enclosing context, |
| // we need to walk into it with a new sequence. |
| // Otherwise, it'll have been separately type-checked. |
| if (!CE->isSeparatelyTypeChecked()) |
| CE->getBody()->walk(ContextualizeClosures(CE)); |
| |
| TypeChecker::computeCaptures(CE); |
| return { false, E }; |
| } |
| |
| // Caller-side default arguments need their @autoclosures checked. |
| if (auto *DAE = dyn_cast<DefaultArgumentExpr>(E)) |
| if (DAE->isCallerSide() && DAE->getParamDecl()->isAutoClosure()) |
| DAE->getCallerSideDefaultExpr()->walk(*this); |
| |
| return { true, E }; |
| } |
| |
| /// We don't want to recurse into most local declarations. |
| bool walkToDeclPre(Decl *D) override { |
| // But we do want to walk into the initializers of local |
| // variables. |
| return isa<PatternBindingDecl>(D); |
| } |
| }; |
| |
| static DeclName getDescriptiveName(AbstractFunctionDecl *AFD) { |
| DeclName name = AFD->getName(); |
| if (!name) { |
| if (auto *accessor = dyn_cast<AccessorDecl>(AFD)) { |
| name = accessor->getStorage()->getName(); |
| } |
| } |
| return name; |
| } |
| |
| /// Used for debugging which parts of the code are taking a long time to |
| /// compile. |
| class FunctionBodyTimer { |
| AnyFunctionRef Function; |
| llvm::TimeRecord StartTime = llvm::TimeRecord::getCurrentTime(); |
| |
| public: |
| FunctionBodyTimer(AnyFunctionRef Fn) : Function(Fn) {} |
| |
| ~FunctionBodyTimer() { |
| llvm::TimeRecord endTime = llvm::TimeRecord::getCurrentTime(false); |
| |
| auto elapsed = endTime.getProcessTime() - StartTime.getProcessTime(); |
| unsigned elapsedMS = static_cast<unsigned>(elapsed * 1000); |
| |
| ASTContext &ctx = Function.getAsDeclContext()->getASTContext(); |
| auto *AFD = Function.getAbstractFunctionDecl(); |
| |
| if (ctx.TypeCheckerOpts.DebugTimeFunctionBodies) { |
| // Round up to the nearest 100th of a millisecond. |
| llvm::errs() << llvm::format("%0.2f", ceil(elapsed * 100000) / 100) << "ms\t"; |
| Function.getLoc().print(llvm::errs(), ctx.SourceMgr); |
| |
| if (AFD) { |
| llvm::errs() |
| << "\t" << Decl::getDescriptiveKindName(AFD->getDescriptiveKind()) |
| << " " << getDescriptiveName(AFD); |
| } else { |
| llvm::errs() << "\t(closure)"; |
| } |
| llvm::errs() << "\n"; |
| } |
| |
| const auto WarnLimit = ctx.TypeCheckerOpts.WarnLongFunctionBodies; |
| if (WarnLimit != 0 && elapsedMS >= WarnLimit) { |
| if (AFD) { |
| ctx.Diags.diagnose(AFD, diag::debug_long_function_body, |
| AFD->getDescriptiveKind(), getDescriptiveName(AFD), |
| elapsedMS, WarnLimit); |
| } else { |
| ctx.Diags.diagnose(Function.getLoc(), diag::debug_long_closure_body, |
| elapsedMS, WarnLimit); |
| } |
| } |
| } |
| }; |
| } // end anonymous namespace |
| |
| void TypeChecker::contextualizeInitializer(Initializer *DC, Expr *E) { |
| ContextualizeClosures CC(DC); |
| E->walk(CC); |
| } |
| |
| void TypeChecker::contextualizeTopLevelCode(TopLevelCodeDecl *TLCD) { |
| auto &Context = TLCD->DeclContext::getASTContext(); |
| unsigned nextDiscriminator = Context.NextAutoClosureDiscriminator; |
| ContextualizeClosures CC(TLCD, nextDiscriminator); |
| TLCD->getBody()->walk(CC); |
| assert(nextDiscriminator == Context.NextAutoClosureDiscriminator && |
| "reentrant/concurrent invocation of contextualizeTopLevelCode?"); |
| Context.NextAutoClosureDiscriminator = CC.NextDiscriminator; |
| } |
| |
| /// Emits an error with a fixit for the case of unnecessary cast over a |
| /// OptionSet value. The primary motivation is to help with SDK changes. |
| /// Example: |
| /// \code |
| /// func supported() -> MyMask { |
| /// return Int(MyMask.Bingo.rawValue) |
| /// } |
| /// \endcode |
| static void tryDiagnoseUnnecessaryCastOverOptionSet(ASTContext &Ctx, |
| Expr *E, |
| Type ResultType, |
| ModuleDecl *module) { |
| auto *NTD = ResultType->getAnyNominal(); |
| if (!NTD) |
| return; |
| auto optionSetType = dyn_cast_or_null<ProtocolDecl>(Ctx.getOptionSetDecl()); |
| if (!optionSetType) |
| return; |
| SmallVector<ProtocolConformance *, 4> conformances; |
| if (!(optionSetType && |
| NTD->lookupConformance(module, optionSetType, conformances))) |
| return; |
| |
| auto *CE = dyn_cast<CallExpr>(E); |
| if (!CE) |
| return; |
| if (!isa<ConstructorRefCallExpr>(CE->getFn())) |
| return; |
| auto *ParenE = dyn_cast<ParenExpr>(CE->getArg()); |
| if (!ParenE) |
| return; |
| auto *ME = dyn_cast<MemberRefExpr>(ParenE->getSubExpr()); |
| if (!ME) |
| return; |
| ValueDecl *VD = ME->getMember().getDecl(); |
| if (!VD || VD->getBaseName() != Ctx.Id_rawValue) |
| return; |
| auto *BME = dyn_cast<MemberRefExpr>(ME->getBase()); |
| if (!BME) |
| return; |
| if (!BME->getType()->isEqual(ResultType)) |
| return; |
| |
| Ctx.Diags.diagnose(E->getLoc(), diag::unnecessary_cast_over_optionset, |
| ResultType) |
| .highlight(E->getSourceRange()) |
| .fixItRemoveChars(E->getLoc(), ME->getStartLoc()) |
| .fixItRemove(SourceRange(ME->getDotLoc(), E->getEndLoc())); |
| } |
| |
| /// Whether the given enclosing context is a "defer" body. |
| static bool isDefer(DeclContext *dc) { |
| if (auto *func = dyn_cast<FuncDecl>(dc)) |
| return func->isDeferBody(); |
| |
| return false; |
| } |
| |
| /// Check that a labeled statement doesn't shadow another statement with the |
| /// same label. |
| static void checkLabeledStmtShadowing( |
| ASTContext &ctx, SourceFile *sourceFile, LabeledStmt *ls) { |
| auto name = ls->getLabelInfo().Name; |
| if (name.empty() || !sourceFile || ls->getStartLoc().isInvalid()) |
| return; |
| |
| auto activeLabeledStmtsVec = ASTScope::lookupLabeledStmts( |
| sourceFile, ls->getStartLoc()); |
| auto activeLabeledStmts = llvm::makeArrayRef(activeLabeledStmtsVec); |
| for (auto prevLS : activeLabeledStmts.slice(1)) { |
| if (prevLS->getLabelInfo().Name == name) { |
| ctx.Diags.diagnose( |
| ls->getLabelInfo().Loc, diag::label_shadowed, name); |
| ctx.Diags.diagnose( |
| prevLS->getLabelInfo().Loc, diag::invalid_redecl_prev, name); |
| } |
| } |
| } |
| |
| static void |
| emitUnresolvedLabelDiagnostics(DiagnosticEngine &DE, |
| SourceLoc targetLoc, Identifier targetName, |
| TopCollection<unsigned, LabeledStmt *> corrections) { |
| // If an unresolved label was used, but we have a single correction, |
| // produce the specific diagnostic and fixit. |
| if (corrections.size() == 1) { |
| DE.diagnose(targetLoc, diag::unresolved_label_corrected, |
| targetName, corrections.begin()->Value->getLabelInfo().Name) |
| .highlight(SourceRange(targetLoc)) |
| .fixItReplace(SourceRange(targetLoc), |
| corrections.begin()->Value->getLabelInfo().Name.str()); |
| DE.diagnose(corrections.begin()->Value->getLabelInfo().Loc, |
| diag::decl_declared_here, |
| corrections.begin()->Value->getLabelInfo().Name); |
| } else { |
| // If we have multiple corrections or none, produce a generic diagnostic |
| // and all corrections available. |
| DE.diagnose(targetLoc, diag::unresolved_label, targetName) |
| .highlight(SourceRange(targetLoc)); |
| for (auto &entry : corrections) |
| DE.diagnose(entry.Value->getLabelInfo().Loc, diag::note_typo_candidate, |
| entry.Value->getLabelInfo().Name.str()) |
| .fixItReplace(SourceRange(targetLoc), |
| entry.Value->getLabelInfo().Name.str()); |
| } |
| } |
| |
| /// Find the target of a break or continue statement without a label. |
| /// |
| /// \returns the target, if one was found, or \c nullptr if no such target |
| /// exists. |
| static LabeledStmt *findUnlabeledBreakOrContinueStmtTarget( |
| ASTContext &ctx, SourceFile *sourceFile, SourceLoc loc, |
| bool isContinue, DeclContext *dc, |
| ArrayRef<LabeledStmt *> activeLabeledStmts) { |
| for (auto labeledStmt : activeLabeledStmts) { |
| // 'break' with no label looks through non-loop structures |
| // except 'switch'. |
| // 'continue' ignores non-loop structures. |
| if (!labeledStmt->requiresLabelOnJump() && |
| (!isContinue || labeledStmt->isPossibleContinueTarget())) { |
| return labeledStmt; |
| } |
| } |
| |
| // If we're in a defer, produce a tailored diagnostic. |
| if (isDefer(dc)) { |
| ctx.Diags.diagnose( |
| loc, diag::jump_out_of_defer, isContinue ? "continue": "break"); |
| return nullptr; |
| } |
| |
| // If we're dealing with an unlabeled break inside of an 'if' or 'do' |
| // statement, produce a more specific error. |
| if (!isContinue && |
| llvm::any_of(activeLabeledStmts, |
| [&](Stmt *S) -> bool { |
| return isa<IfStmt>(S) || isa<DoStmt>(S); |
| })) { |
| ctx.Diags.diagnose( |
| loc, diag::unlabeled_break_outside_loop); |
| return nullptr; |
| } |
| |
| // Otherwise produce a generic error. |
| ctx.Diags.diagnose( |
| loc, |
| isContinue ? diag::continue_outside_loop : diag::break_outside_loop); |
| return nullptr; |
| } |
| |
| /// Find the target of a break or continue statement. |
| /// |
| /// \returns the target, if one was found, or \c nullptr if no such target |
| /// exists. |
| static LabeledStmt *findBreakOrContinueStmtTarget( |
| ASTContext &ctx, SourceFile *sourceFile, |
| SourceLoc loc, Identifier targetName, SourceLoc targetLoc, |
| bool isContinue, DeclContext *dc) { |
| |
| // Retrieve the active set of labeled statements. |
| SmallVector<LabeledStmt *, 4> activeLabeledStmts; |
| activeLabeledStmts = ASTScope::lookupLabeledStmts(sourceFile, loc); |
| |
| // Handle an unlabeled break separately; that's the easy case. |
| if (targetName.empty()) { |
| return findUnlabeledBreakOrContinueStmtTarget( |
| ctx, sourceFile, loc, isContinue, dc, activeLabeledStmts); |
| } |
| |
| // Scan inside out until we find something with the right label. |
| TopCollection<unsigned, LabeledStmt *> labelCorrections(3); |
| for (auto labeledStmt : activeLabeledStmts) { |
| if (targetName == labeledStmt->getLabelInfo().Name) { |
| // Continue cannot be used to repeat switches, use fallthrough instead. |
| if (isContinue && !labeledStmt->isPossibleContinueTarget()) { |
| ctx.Diags.diagnose( |
| loc, diag::continue_not_in_this_stmt, |
| isa<SwitchStmt>(labeledStmt) ? "switch" : "if"); |
| return nullptr; |
| } |
| |
| return labeledStmt; |
| } |
| |
| unsigned distance = |
| TypeChecker::getCallEditDistance( |
| DeclNameRef(targetName), |
| labeledStmt->getLabelInfo().Name, |
| TypeChecker::UnreasonableCallEditDistance); |
| if (distance < TypeChecker::UnreasonableCallEditDistance) |
| labelCorrections.insert(distance, std::move(labeledStmt)); |
| } |
| labelCorrections.filterMaxScoreRange( |
| TypeChecker::MaxCallEditDistanceFromBestCandidate); |
| |
| // If we're in a defer, produce a tailored diagnostic. |
| if (isDefer(dc)) { |
| ctx.Diags.diagnose( |
| loc, diag::jump_out_of_defer, isContinue ? "continue": "break"); |
| return nullptr; |
| } |
| |
| // Provide potential corrections for an incorrect label. |
| emitUnresolvedLabelDiagnostics( |
| ctx.Diags, targetLoc, targetName, labelCorrections); |
| return nullptr; |
| } |
| |
| /// Type check the given 'if', 'while', or 'guard' statement condition. |
| /// |
| /// \param stmt The conditional statement to type-check, which will be modified |
| /// in place. |
| /// |
| /// \returns true if an error occurred, false otherwise. |
| static bool typeCheckConditionForStatement(LabeledConditionalStmt *stmt, |
| DeclContext *dc) { |
| auto &Context = dc->getASTContext(); |
| bool hadError = false; |
| bool hadAnyFalsable = false; |
| auto cond = stmt->getCond(); |
| for (auto &elt : cond) { |
| if (elt.getKind() == StmtConditionElement::CK_Availability) { |
| hadAnyFalsable = true; |
| |
| // Reject inlinable code using availability macros. |
| PoundAvailableInfo *info = elt.getAvailability(); |
| if (auto *decl = dc->getAsDecl()) { |
| if (decl->getAttrs().hasAttribute<InlinableAttr>() || |
| decl->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>()) |
| for (auto queries : info->getQueries()) |
| if (auto availSpec = |
| dyn_cast<PlatformVersionConstraintAvailabilitySpec>(queries)) |
| if (availSpec->getMacroLoc().isValid()) { |
| Context.Diags.diagnose( |
| availSpec->getMacroLoc(), |
| swift::diag::availability_macro_in_inlinable, |
| decl->getDescriptiveKind()); |
| break; |
| } |
| } |
| |
| continue; |
| } |
| |
| if (auto E = elt.getBooleanOrNull()) { |
| assert(!E->getType() && "the bool condition is already type checked"); |
| hadError |= TypeChecker::typeCheckCondition(E, dc); |
| elt.setBoolean(E); |
| hadAnyFalsable = true; |
| continue; |
| } |
| assert(elt.getKind() != StmtConditionElement::CK_Boolean); |
| |
| // This is cleanup goop run on the various paths where type checking of the |
| // pattern binding fails. |
| auto typeCheckPatternFailed = [&] { |
| hadError = true; |
| elt.getPattern()->setType(ErrorType::get(Context)); |
| elt.getInitializer()->setType(ErrorType::get(Context)); |
| |
| elt.getPattern()->forEachVariable([&](VarDecl *var) { |
| // Don't change the type of a variable that we've been able to |
| // compute a type for. |
| if (var->hasInterfaceType() && !var->isInvalid()) |
| return; |
| var->setInvalid(); |
| }); |
| }; |
| |
| // Resolve the pattern. |
| assert(!elt.getPattern()->hasType() && |
| "the pattern binding condition is already type checked"); |
| auto *pattern = TypeChecker::resolvePattern(elt.getPattern(), dc, |
| /*isStmtCondition*/ true); |
| if (!pattern) { |
| typeCheckPatternFailed(); |
| continue; |
| } |
| elt.setPattern(pattern); |
| |
| TypeChecker::diagnoseDuplicateBoundVars(pattern); |
| |
| // Check the pattern, it allows unspecified types because the pattern can |
| // provide type information. |
| auto contextualPattern = ContextualPattern::forRawPattern(pattern, dc); |
| Type patternType = TypeChecker::typeCheckPattern(contextualPattern); |
| if (patternType->hasError()) { |
| typeCheckPatternFailed(); |
| continue; |
| } |
| |
| // If the pattern didn't get a type, it's because we ran into some |
| // unknown types along the way. We'll need to check the initializer. |
| auto init = elt.getInitializer(); |
| hadError |= TypeChecker::typeCheckBinding(pattern, init, dc, patternType); |
| elt.setPattern(pattern); |
| elt.setInitializer(init); |
| hadAnyFalsable |= pattern->isRefutablePattern(); |
| } |
| |
| // If the binding is not refutable, and there *is* an else, reject it as |
| // unreachable. |
| if (!hadAnyFalsable && !hadError) { |
| auto &diags = dc->getASTContext().Diags; |
| Diag<> msg = diag::invalid_diagnostic; |
| switch (stmt->getKind()) { |
| case StmtKind::If: |
| msg = diag::if_always_true; |
| break; |
| case StmtKind::While: |
| msg = diag::while_always_true; |
| break; |
| case StmtKind::Guard: |
| msg = diag::guard_always_succeeds; |
| break; |
| default: |
| llvm_unreachable("unknown LabeledConditionalStmt kind"); |
| } |
| diags.diagnose(cond[0].getStartLoc(), msg); |
| } |
| |
| stmt->setCond(cond); |
| return false; |
| } |
| |
| /// Verify that the pattern bindings for the cases that we're falling through |
| /// from and to are equivalent. |
| static void checkFallthroughPatternBindingsAndTypes( |
| ASTContext &ctx, |
| CaseStmt *caseBlock, CaseStmt *previousBlock, |
| FallthroughStmt *fallthrough) { |
| auto firstPattern = caseBlock->getCaseLabelItems()[0].getPattern(); |
| SmallVector<VarDecl *, 4> vars; |
| firstPattern->collectVariables(vars); |
| |
| // We know that the typechecker has already guaranteed that all of |
| // the case label items in the fallthrough have the same var |
| // decls. So if we match against the case body var decls, |
| // transitively we will match all of the other case label items in |
| // the fallthrough destination as well. |
| auto previousVars = previousBlock->getCaseBodyVariablesOrEmptyArray(); |
| for (auto *expected : vars) { |
| bool matched = false; |
| if (!expected->hasName()) |
| continue; |
| |
| for (auto *previous : previousVars) { |
| if (!previous->hasName() || |
| expected->getName() != previous->getName()) { |
| continue; |
| } |
| |
| if (!previous->getType()->isEqual(expected->getType())) { |
| ctx.Diags.diagnose( |
| previous->getLoc(), diag::type_mismatch_fallthrough_pattern_list, |
| previous->getType(), expected->getType()); |
| previous->setInvalid(); |
| expected->setInvalid(); |
| } |
| |
| // Ok, we found our match. Make the previous fallthrough statement var |
| // decl our parent var decl. |
| expected->setParentVarDecl(previous); |
| matched = true; |
| break; |
| } |
| |
| if (!matched) { |
| ctx.Diags.diagnose( |
| fallthrough->getLoc(), diag::fallthrough_into_case_with_var_binding, |
| expected->getName()); |
| } |
| } |
| } |
| |
| /// Check the correctness of a 'fallthrough' statement. |
| /// |
| /// \returns true if an error occurred. |
| static bool checkFallthroughStmt(DeclContext *dc, FallthroughStmt *stmt) { |
| CaseStmt *fallthroughSource; |
| CaseStmt *fallthroughDest; |
| ASTContext &ctx = dc->getASTContext(); |
| auto sourceFile = dc->getParentSourceFile(); |
| std::tie(fallthroughSource, fallthroughDest) = |
| ASTScope::lookupFallthroughSourceAndDest(sourceFile, stmt->getLoc()); |
| |
| if (!fallthroughSource) { |
| ctx.Diags.diagnose(stmt->getLoc(), diag::fallthrough_outside_switch); |
| return true; |
| } |
| if (!fallthroughDest) { |
| ctx.Diags.diagnose(stmt->getLoc(), diag::fallthrough_from_last_case); |
| return true; |
| } |
| stmt->setFallthroughSource(fallthroughSource); |
| stmt->setFallthroughDest(fallthroughDest); |
| |
| checkFallthroughPatternBindingsAndTypes( |
| ctx, fallthroughDest, fallthroughSource, stmt); |
| return false; |
| } |
| |
| namespace { |
| class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> { |
| public: |
| ASTContext &Ctx; |
| |
| /// DC - This is the current DeclContext. |
| DeclContext *DC; |
| |
| /// Skip type checking any elements inside 'BraceStmt', also this is |
| /// propagated to ConstraintSystem. |
| bool LeaveBraceStmtBodyUnchecked = false; |
| |
| ASTContext &getASTContext() const { return Ctx; }; |
| |
| StmtChecker(DeclContext *DC) : Ctx(DC->getASTContext()), DC(DC) { } |
| |
| //===--------------------------------------------------------------------===// |
| // Helper Functions. |
| //===--------------------------------------------------------------------===// |
| |
| bool isInDefer() const { |
| return isDefer(DC); |
| } |
| |
| template<typename StmtTy> |
| bool typeCheckStmt(StmtTy *&S) { |
| FrontendStatsTracer StatsTracer(getASTContext().Stats, |
| "typecheck-stmt", S); |
| PrettyStackTraceStmt trace(getASTContext(), "type-checking", S); |
| StmtTy *S2 = cast_or_null<StmtTy>(visit(S)); |
| if (S2 == nullptr) |
| return true; |
| S = S2; |
| performStmtDiagnostics(S, DC); |
| return false; |
| } |
| |
| /// Type-check an entire function body. |
| bool typeCheckBody(BraceStmt *&S) { |
| bool HadError = typeCheckStmt(S); |
| S->walk(ContextualizeClosures(DC)); |
| return HadError; |
| } |
| |
| void typeCheckASTNode(ASTNode &node); |
| |
| //===--------------------------------------------------------------------===// |
| // Visit Methods. |
| //===--------------------------------------------------------------------===// |
| |
| Stmt *visitBraceStmt(BraceStmt *BS); |
| |
| Stmt *visitReturnStmt(ReturnStmt *RS) { |
| auto TheFunc = AnyFunctionRef::fromDeclContext(DC); |
| |
| if (!TheFunc.hasValue()) { |
| getASTContext().Diags.diagnose(RS->getReturnLoc(), |
| diag::return_invalid_outside_func); |
| return nullptr; |
| } |
| |
| // If the return is in a defer, then it isn't valid either. |
| if (isInDefer()) { |
| getASTContext().Diags.diagnose(RS->getReturnLoc(), |
| diag::jump_out_of_defer, "return"); |
| return nullptr; |
| } |
| |
| Type ResultTy = TheFunc->getBodyResultType(); |
| if (!ResultTy || ResultTy->hasError()) |
| return nullptr; |
| |
| if (!RS->hasResult()) { |
| if (!ResultTy->isVoid()) |
| getASTContext().Diags.diagnose(RS->getReturnLoc(), |
| diag::return_expr_missing); |
| return RS; |
| } |
| |
| // If the body consisted of a single return without a result |
| // |
| // func foo() -> Int { |
| // return |
| // } |
| // |
| // in parseAbstractFunctionBody the return is given an empty, implicit tuple |
| // as its result |
| // |
| // func foo() -> Int { |
| // return () |
| // } |
| // |
| // Look for that case and diagnose it as missing return expression. |
| if (!ResultTy->isVoid() && TheFunc->hasSingleExpressionBody()) { |
| auto expr = TheFunc->getSingleExpressionBody(); |
| if (expr->isImplicit() && isa<TupleExpr>(expr) && |
| cast<TupleExpr>(expr)->getNumElements() == 0) { |
| getASTContext().Diags.diagnose(RS->getReturnLoc(), |
| diag::return_expr_missing); |
| return RS; |
| } |
| } |
| |
| Expr *E = RS->getResult(); |
| |
| // In an initializer, the only expression allowed is "nil", which indicates |
| // failure from a failable initializer. |
| if (auto ctor = dyn_cast_or_null<ConstructorDecl>( |
| TheFunc->getAbstractFunctionDecl())) { |
| // The only valid return expression in an initializer is the literal |
| // 'nil'. |
| auto nilExpr = dyn_cast<NilLiteralExpr>(E->getSemanticsProvidingExpr()); |
| if (!nilExpr) { |
| getASTContext().Diags.diagnose(RS->getReturnLoc(), |
| diag::return_init_non_nil) |
| .highlight(E->getSourceRange()); |
| RS->setResult(nullptr); |
| return RS; |
| } |
| |
| // "return nil" is only permitted in a failable initializer. |
| if (!ctor->isFailable()) { |
| getASTContext().Diags.diagnose(RS->getReturnLoc(), |
| diag::return_non_failable_init) |
| .highlight(E->getSourceRange()); |
| getASTContext().Diags.diagnose(ctor->getLoc(), diag::make_init_failable, |
| ctor->getName()) |
| .fixItInsertAfter(ctor->getLoc(), "?"); |
| RS->setResult(nullptr); |
| return RS; |
| } |
| |
| // Replace the "return nil" with a new 'fail' statement. |
| return new (getASTContext()) FailStmt(RS->getReturnLoc(), |
| nilExpr->getLoc(), |
| RS->isImplicit()); |
| } |
| |
| TypeCheckExprOptions options = {}; |
| |
| if (LeaveBraceStmtBodyUnchecked) { |
| assert(DiagnosticSuppression::isEnabled(getASTContext().Diags) && |
| "Diagnosing and AllowUnresolvedTypeVariables don't seem to mix"); |
| options |= TypeCheckExprFlags::LeaveClosureBodyUnchecked; |
| options |= TypeCheckExprFlags::AllowUnresolvedTypeVariables; |
| } |
| |
| ContextualTypePurpose ctp = CTP_ReturnStmt; |
| if (auto func = |
| dyn_cast_or_null<FuncDecl>(TheFunc->getAbstractFunctionDecl())) { |
| if (func->hasSingleExpressionBody()) { |
| ctp = CTP_ReturnSingleExpr; |
| } |
| } |
| |
| auto exprTy = |
| TypeChecker::typeCheckExpression(E, DC, {ResultTy, ctp}, options); |
| RS->setResult(E); |
| |
| if (!exprTy) { |
| tryDiagnoseUnnecessaryCastOverOptionSet(getASTContext(), E, ResultTy, |
| DC->getParentModule()); |
| } |
| |
| return RS; |
| } |
| |
| Stmt *visitYieldStmt(YieldStmt *YS) { |
| // If the yield is in a defer, then it isn't valid. |
| if (isInDefer()) { |
| getASTContext().Diags.diagnose(YS->getYieldLoc(), |
| diag::jump_out_of_defer, "yield"); |
| return YS; |
| } |
| |
| SmallVector<AnyFunctionType::Yield, 4> buffer; |
| auto TheFunc = AnyFunctionRef::fromDeclContext(DC); |
| auto yieldResults = TheFunc->getBodyYieldResults(buffer); |
| |
| auto yieldExprs = YS->getMutableYields(); |
| if (yieldExprs.size() != yieldResults.size()) { |
| getASTContext().Diags.diagnose(YS->getYieldLoc(), diag::bad_yield_count, |
| yieldResults.size()); |
| return YS; |
| } |
| |
| for (auto i : indices(yieldExprs)) { |
| Type yieldType = yieldResults[i].getType(); |
| auto exprToCheck = yieldExprs[i]; |
| |
| InOutExpr *inout = nullptr; |
| |
| // Classify whether we're yielding by reference or by value. |
| ContextualTypePurpose contextTypePurpose; |
| Type contextType = yieldType; |
| if (yieldResults[i].isInOut()) { |
| contextTypePurpose = CTP_YieldByReference; |
| contextType = LValueType::get(contextType); |
| |
| // Check that the yielded expression is a &. |
| if ((inout = dyn_cast<InOutExpr>(exprToCheck))) { |
| // Strip the & off so that the constraint system doesn't complain |
| // about the unparented &. |
| exprToCheck = inout->getSubExpr(); |
| } else { |
| getASTContext().Diags.diagnose(exprToCheck->getLoc(), |
| diag::missing_address_of_yield, yieldType) |
| .highlight(exprToCheck->getSourceRange()); |
| inout = new (getASTContext()) InOutExpr(exprToCheck->getStartLoc(), |
| exprToCheck, |
| Type(), /*implicit*/ true); |
| } |
| } else { |
| contextTypePurpose = CTP_YieldByValue; |
| } |
| |
| TypeChecker::typeCheckExpression(exprToCheck, DC, |
| {contextType, contextTypePurpose}); |
| |
| // Propagate the change into the inout expression we stripped before. |
| if (inout) { |
| inout->setSubExpr(exprToCheck); |
| inout->setType(InOutType::get(yieldType)); |
| exprToCheck = inout; |
| } |
| |
| // Note that this modifies the statement's expression list in-place. |
| yieldExprs[i] = exprToCheck; |
| } |
| return YS; |
| } |
| |
| Stmt *visitThrowStmt(ThrowStmt *TS) { |
| // Coerce the operand to the exception type. |
| auto E = TS->getSubExpr(); |
| |
| Type exnType = getASTContext().getErrorDecl()->getDeclaredInterfaceType(); |
| if (!exnType) return TS; |
| |
| TypeChecker::typeCheckExpression(E, DC, {exnType, CTP_ThrowStmt}); |
| TS->setSubExpr(E); |
| |
| return TS; |
| } |
| |
| Stmt *visitPoundAssertStmt(PoundAssertStmt *PA) { |
| Expr *C = PA->getCondition(); |
| TypeChecker::typeCheckCondition(C, DC); |
| PA->setCondition(C); |
| return PA; |
| } |
| |
| Stmt *visitDeferStmt(DeferStmt *DS) { |
| TypeChecker::typeCheckDecl(DS->getTempDecl()); |
| |
| Expr *theCall = DS->getCallExpr(); |
| TypeChecker::typeCheckExpression(theCall, DC); |
| DS->setCallExpr(theCall); |
| |
| return DS; |
| } |
| |
| Stmt *visitIfStmt(IfStmt *IS) { |
| typeCheckConditionForStatement(IS, DC); |
| |
| auto sourceFile = DC->getParentSourceFile(); |
| checkLabeledStmtShadowing(getASTContext(), sourceFile, IS); |
| |
| Stmt *S = IS->getThenStmt(); |
| typeCheckStmt(S); |
| IS->setThenStmt(S); |
| |
| if ((S = IS->getElseStmt())) { |
| typeCheckStmt(S); |
| IS->setElseStmt(S); |
| } |
| |
| return IS; |
| } |
| |
| Stmt *visitGuardStmt(GuardStmt *GS) { |
| typeCheckConditionForStatement(GS, DC); |
| |
| BraceStmt *S = GS->getBody(); |
| typeCheckStmt(S); |
| GS->setBody(S); |
| return GS; |
| } |
| |
| Stmt *visitDoStmt(DoStmt *DS) { |
| auto sourceFile = DC->getParentSourceFile(); |
| checkLabeledStmtShadowing(getASTContext(), sourceFile, DS); |
| |
| BraceStmt *S = DS->getBody(); |
| typeCheckStmt(S); |
| DS->setBody(S); |
| return DS; |
| } |
| |
| Stmt *visitWhileStmt(WhileStmt *WS) { |
| typeCheckConditionForStatement(WS, DC); |
| |
| auto sourceFile = DC->getParentSourceFile(); |
| checkLabeledStmtShadowing(getASTContext(), sourceFile, WS); |
| |
| Stmt *S = WS->getBody(); |
| typeCheckStmt(S); |
| WS->setBody(S); |
| |
| return WS; |
| } |
| Stmt *visitRepeatWhileStmt(RepeatWhileStmt *RWS) { |
| auto sourceFile = DC->getParentSourceFile(); |
| checkLabeledStmtShadowing(getASTContext(), sourceFile, RWS); |
| |
| Stmt *S = RWS->getBody(); |
| typeCheckStmt(S); |
| RWS->setBody(S); |
| |
| Expr *E = RWS->getCond(); |
| TypeChecker::typeCheckCondition(E, DC); |
| RWS->setCond(E); |
| return RWS; |
| } |
| |
| Stmt *visitForEachStmt(ForEachStmt *S) { |
| if (TypeChecker::typeCheckForEachBinding(DC, S)) |
| return nullptr; |
| |
| TypeChecker::diagnoseDuplicateBoundVars(S->getPattern()); |
| |
| // Type-check the body of the loop. |
| auto sourceFile = DC->getParentSourceFile(); |
| checkLabeledStmtShadowing(getASTContext(), sourceFile, S); |
| |
| BraceStmt *Body = S->getBody(); |
| typeCheckStmt(Body); |
| S->setBody(Body); |
| |
| return S; |
| } |
| |
| Stmt *visitBreakStmt(BreakStmt *S) { |
| if (auto target = findBreakOrContinueStmtTarget( |
| getASTContext(), DC->getParentSourceFile(), S->getLoc(), |
| S->getTargetName(), S->getTargetLoc(), /*isContinue=*/false, |
| DC)) { |
| S->setTarget(target); |
| } |
| |
| return S; |
| } |
| |
| Stmt *visitContinueStmt(ContinueStmt *S) { |
| if (auto target = findBreakOrContinueStmtTarget( |
| getASTContext(), DC->getParentSourceFile(), S->getLoc(), |
| S->getTargetName(), S->getTargetLoc(), /*isContinue=*/true, |
| DC)) { |
| S->setTarget(target); |
| } |
| |
| return S; |
| } |
| |
| Stmt *visitFallthroughStmt(FallthroughStmt *S) { |
| if (checkFallthroughStmt(DC, S)) |
| return nullptr; |
| |
| return S; |
| } |
| |
| void checkCaseLabelItemPattern(CaseStmt *caseBlock, CaseLabelItem &labelItem, |
| bool &limitExhaustivityChecks, |
| Type subjectType) { |
| Pattern *pattern = labelItem.getPattern(); |
| if (!labelItem.isPatternResolved()) { |
| pattern = TypeChecker::resolvePattern( |
| pattern, DC, /*isStmtCondition*/ false); |
| if (!pattern) { |
| return; |
| } |
| } |
| |
| // Coerce the pattern to the subject's type. |
| bool coercionError = false; |
| if (subjectType) { |
| auto contextualPattern = ContextualPattern::forRawPattern(pattern, DC); |
| TypeResolutionOptions patternOptions(TypeResolverContext::InExpression); |
| auto coercedPattern = TypeChecker::coercePatternToType( |
| contextualPattern, subjectType, patternOptions); |
| if (coercedPattern) |
| pattern = coercedPattern; |
| else |
| coercionError = true; |
| } |
| |
| if (!subjectType || coercionError) { |
| limitExhaustivityChecks = true; |
| |
| // If that failed, mark any variables binding pieces of the pattern |
| // as invalid to silence follow-on errors. |
| pattern->forEachVariable([&](VarDecl *VD) { |
| VD->setInvalid(); |
| }); |
| } |
| labelItem.setPattern(pattern, /*resolved=*/true); |
| |
| TypeChecker::diagnoseDuplicateBoundVars(pattern); |
| |
| // Otherwise for each variable in the pattern, make sure its type is |
| // identical to the initial case decl and stash the previous case decl as |
| // the parent of the decl. |
| pattern->forEachVariable([&](VarDecl *vd) { |
| if (!vd->hasName()) |
| return; |
| |
| // We know that prev var decls matches the initial var decl. So if we can |
| // match prevVarDecls, we can also match initial var decl... So for each |
| // decl in prevVarDecls... |
| auto expected = vd->getParentVarDecl(); |
| if (!expected) |
| return; |
| |
| // Then we check for errors. |
| // |
| // NOTE: We emit the diagnostics against the initial case label item var |
| // decl associated with expected to ensure that we always emit |
| // diagnostics against a single reference var decl. If we used expected |
| // instead, we would emit confusing diagnostics since a correct var decl |
| // after an incorrect var decl will be marked as incorrect. For instance |
| // given the following case statement. |
| // |
| // case .a(let x), .b(var x), .c(let x): |
| // |
| // if we use expected, we will emit errors saying that .b(var x) needs |
| // to be a let and .c(let x) needs to be a var. Thus if one |
| // automatically applied both fix-its, one would still get an error |
| // producing program: |
| // |
| // case .a(let x), .b(let x), .c(var x): |
| // |
| // More complex case label item lists could cause even longer fixup |
| // sequences. Thus, we emit errors against the VarDecl associated with |
| // expected in the initial case label item list. |
| // |
| // Luckily, it is easy for us to compute this since we only change the |
| // parent field of the initial case label item's VarDecls /after/ we |
| // finish updating the parent pointers of the VarDecls associated with |
| // all other CaseLabelItems. So that initial group of VarDecls are |
| // guaranteed to still have a parent pointer pointing at our |
| // CaseStmt. Since we have setup the parent pointer VarDecl linked list |
| // for all other CaseLabelItem var decls that we have already processed, |
| // we can use our VarDecl linked list to find that initial case label |
| // item VarDecl. |
| auto *initialCaseVarDecl = expected; |
| while (auto *prev = initialCaseVarDecl->getParentVarDecl()) { |
| initialCaseVarDecl = prev; |
| } |
| assert(isa<CaseStmt>(initialCaseVarDecl->getParentPatternStmt())); |
| |
| if (!initialCaseVarDecl->isInvalid() && |
| !vd->getType()->isEqual(initialCaseVarDecl->getType())) { |
| getASTContext().Diags.diagnose( |
| vd->getLoc(), diag::type_mismatch_multiple_pattern_list, |
| vd->getType(), initialCaseVarDecl->getType()); |
| vd->setInvalid(); |
| initialCaseVarDecl->setInvalid(); |
| } |
| }); |
| } |
| |
| template <typename Iterator> |
| void checkSiblingCaseStmts(Iterator casesBegin, Iterator casesEnd, |
| CaseParentKind parentKind, |
| bool &limitExhaustivityChecks, Type subjectType) { |
| static_assert( |
| std::is_same<typename std::iterator_traits<Iterator>::value_type, |
| CaseStmt *>::value, |
| "Expected an iterator over CaseStmt *"); |
| |
| // First pass: check all of the bindings. |
| for (auto *caseBlock : make_range(casesBegin, casesEnd)) { |
| // Bind all of the pattern variables together so we can follow the |
| // "parent" pointers later on. |
| bindSwitchCasePatternVars(DC, caseBlock); |
| |
| auto caseLabelItemArray = caseBlock->getMutableCaseLabelItems(); |
| for (auto &labelItem : caseLabelItemArray) { |
| // Resolve the pattern in our case label if it has not been resolved and |
| // check that our var decls follow invariants. |
| checkCaseLabelItemPattern(caseBlock, labelItem, limitExhaustivityChecks, |
| subjectType); |
| |
| // Check the guard expression, if present. |
| if (auto *guard = labelItem.getGuardExpr()) { |
| limitExhaustivityChecks |= TypeChecker::typeCheckCondition(guard, DC); |
| labelItem.setGuardExpr(guard); |
| } |
| } |
| |
| // Setup the types of our case body var decls. |
| for (auto *expected : caseBlock->getCaseBodyVariablesOrEmptyArray()) { |
| assert(expected->hasName()); |
| auto prev = expected->getParentVarDecl(); |
| if (prev->hasInterfaceType()) |
| expected->setInterfaceType(prev->getInterfaceType()); |
| } |
| } |
| |
| // Second pass: type-check the body statements. |
| for (auto i = casesBegin; i != casesEnd; ++i) { |
| auto *caseBlock = *i; |
| |
| // Check restrictions on '@unknown'. |
| if (caseBlock->hasUnknownAttr()) { |
| assert(parentKind == CaseParentKind::Switch && |
| "'@unknown' can only appear on switch cases"); |
| checkUnknownAttrRestrictions( |
| getASTContext(), caseBlock, limitExhaustivityChecks); |
| } |
| |
| BraceStmt *body = caseBlock->getBody(); |
| limitExhaustivityChecks |= typeCheckStmt(body); |
| caseBlock->setBody(body); |
| } |
| } |
| |
| Stmt *visitSwitchStmt(SwitchStmt *switchStmt) { |
| // Type-check the subject expression. |
| Expr *subjectExpr = switchStmt->getSubjectExpr(); |
| auto resultTy = TypeChecker::typeCheckExpression(subjectExpr, DC); |
| auto limitExhaustivityChecks = !resultTy; |
| if (Expr *newSubjectExpr = |
| TypeChecker::coerceToRValue(getASTContext(), subjectExpr)) |
| subjectExpr = newSubjectExpr; |
| switchStmt->setSubjectExpr(subjectExpr); |
| Type subjectType = switchStmt->getSubjectExpr()->getType(); |
| |
| // Type-check the case blocks. |
| auto sourceFile = DC->getParentSourceFile(); |
| checkLabeledStmtShadowing(getASTContext(), sourceFile, switchStmt); |
| |
| // Pre-emptively visit all Decls (#if/#warning/#error) that still exist in |
| // the list of raw cases. |
| for (auto &node : switchStmt->getRawCases()) { |
| if (!node.is<Decl *>()) |
| continue; |
| TypeChecker::typeCheckDecl(node.get<Decl *>()); |
| } |
| |
| auto cases = switchStmt->getCases(); |
| checkSiblingCaseStmts(cases.begin(), cases.end(), CaseParentKind::Switch, |
| limitExhaustivityChecks, subjectType); |
| |
| if (!switchStmt->isImplicit()) { |
| TypeChecker::checkSwitchExhaustiveness(switchStmt, DC, |
| limitExhaustivityChecks); |
| } |
| |
| return switchStmt; |
| } |
| |
| Stmt *visitCaseStmt(CaseStmt *S) { |
| // Cases are handled in visitSwitchStmt. |
| llvm_unreachable("case stmt outside of switch?!"); |
| } |
| |
| Stmt *visitDoCatchStmt(DoCatchStmt *S) { |
| // The labels are in scope for both the 'do' and all of the catch |
| // clauses. This allows the user to break out of (or restart) the |
| // entire construct. |
| auto sourceFile = DC->getParentSourceFile(); |
| checkLabeledStmtShadowing(getASTContext(), sourceFile, S); |
| |
| // Type-check the 'do' body. Type failures in here will generally |
| // not cause type failures in the 'catch' clauses. |
| Stmt *newBody = S->getBody(); |
| typeCheckStmt(newBody); |
| S->setBody(newBody); |
| |
| // Do-catch statements always limit exhaustivity checks. |
| bool limitExhaustivityChecks = true; |
| |
| auto catches = S->getCatches(); |
| checkSiblingCaseStmts(catches.begin(), catches.end(), |
| CaseParentKind::DoCatch, limitExhaustivityChecks, |
| getASTContext().getExceptionType()); |
| |
| return S; |
| } |
| |
| Stmt *visitFailStmt(FailStmt *S) { |
| // These are created as part of type-checking "return" in an initializer. |
| // There is nothing more to do. |
| return S; |
| } |
| |
| }; |
| } // end anonymous namespace |
| |
| static bool isDiscardableType(Type type) { |
| return (type->hasError() || |
| type->isUninhabited() || |
| type->lookThroughAllOptionalTypes()->isVoid()); |
| } |
| |
| static void diagnoseIgnoredLiteral(ASTContext &Ctx, LiteralExpr *LE) { |
| const auto getLiteralDescription = [](LiteralExpr *LE) -> StringRef { |
| switch (LE->getKind()) { |
| case ExprKind::IntegerLiteral: return "integer"; |
| case ExprKind::FloatLiteral: return "floating-point"; |
| case ExprKind::BooleanLiteral: return "boolean"; |
| case ExprKind::StringLiteral: return "string"; |
| case ExprKind::InterpolatedStringLiteral: return "string"; |
| case ExprKind::MagicIdentifierLiteral: |
| return MagicIdentifierLiteralExpr::getKindString( |
| cast<MagicIdentifierLiteralExpr>(LE)->getKind()); |
| case ExprKind::NilLiteral: return "nil"; |
| case ExprKind::ObjectLiteral: return "object"; |
| |
| // Define an unreachable case for all non-literal expressions. |
| // This way, if a new literal is added in the future, the compiler |
| // will warn that a case is missing from this switch. |
| #define LITERAL_EXPR(Id, Parent) |
| #define EXPR(Id, Parent) case ExprKind::Id: |
| #include "swift/AST/ExprNodes.def" |
| llvm_unreachable("Not a literal expression"); |
| } |
| |
| llvm_unreachable("Unhandled ExprKind in switch."); |
| }; |
| |
| Ctx.Diags.diagnose(LE->getLoc(), diag::expression_unused_literal, |
| getLiteralDescription(LE)) |
| .highlight(LE->getSourceRange()); |
| } |
| |
| void TypeChecker::checkIgnoredExpr(Expr *E) { |
| // For parity with C, several places in the grammar accept multiple |
| // comma-separated expressions and then bind them together as an implicit |
| // tuple. Break these apart and check them separately. |
| if (E->isImplicit() && isa<TupleExpr>(E)) { |
| for (auto Elt : cast<TupleExpr>(E)->getElements()) { |
| checkIgnoredExpr(Elt); |
| } |
| return; |
| } |
| |
| // Skip checking if there is no type, which presumably means there was a |
| // type error. |
| if (!E->getType()) { |
| return; |
| } |
| |
| // Complain about l-values that are neither loaded nor stored. |
| auto &Context = E->getType()->getASTContext(); |
| auto &DE = Context.Diags; |
| if (E->getType()->hasLValueType()) { |
| // This must stay in sync with diag::expression_unused_lvalue. |
| enum { |
| SK_Variable = 0, |
| SK_Property, |
| SK_Subscript |
| } storageKind = SK_Variable; |
| if (auto declRef = E->getReferencedDecl()) { |
| auto decl = declRef.getDecl(); |
| if (isa<SubscriptDecl>(decl)) |
| storageKind = SK_Subscript; |
| else if (decl->getDeclContext()->isTypeContext()) |
| storageKind = SK_Property; |
| } |
| DE.diagnose(E->getLoc(), diag::expression_unused_lvalue, storageKind) |
| .highlight(E->getSourceRange()); |
| return; |
| } |
| |
| // Drill through noop expressions we don't care about. |
| auto valueE = E; |
| while (1) { |
| valueE = valueE->getValueProvidingExpr(); |
| |
| if (auto *OEE = dyn_cast<OpenExistentialExpr>(valueE)) |
| valueE = OEE->getSubExpr(); |
| else if (auto *CRCE = dyn_cast<CovariantReturnConversionExpr>(valueE)) |
| valueE = CRCE->getSubExpr(); |
| else if (auto *EE = dyn_cast<ErasureExpr>(valueE)) |
| valueE = EE->getSubExpr(); |
| else |
| break; |
| } |
| |
| // Complain about functions that aren't called. |
| // TODO: What about tuples which contain functions by-value that are |
| // dead? |
| if (E->getType()->is<AnyFunctionType>()) { |
| bool isDiscardable = false; |
| |
| // The called function could be wrapped inside a `dot_syntax_call_expr` |
| // node, for example: |
| // |
| // class Bar { |
| // @discardableResult |
| // func foo() -> Int { return 0 } |
| // |
| // func baz() { |
| // self.foo |
| // foo |
| // } |
| // } |
| // |
| // So look through the DSCE and get the function being called. |
| auto expr = |
| isa<DotSyntaxCallExpr>(E) ? cast<DotSyntaxCallExpr>(E)->getFn() : E; |
| |
| if (auto *Fn = dyn_cast<ApplyExpr>(expr)) { |
| if (auto *calledValue = Fn->getCalledValue()) { |
| if (auto *FD = dyn_cast<AbstractFunctionDecl>(calledValue)) { |
| if (FD->getAttrs().hasAttribute<DiscardableResultAttr>()) { |
| isDiscardable = true; |
| } |
| } |
| } |
| } |
| |
| if (!isDiscardable) { |
| DE.diagnose(E->getLoc(), diag::expression_unused_function) |
| .highlight(E->getSourceRange()); |
| return; |
| } |
| } |
| |
| // If the result of this expression is of type "Never" or "()" |
| // (the latter potentially wrapped in optionals) then it is |
| // safe to ignore. |
| if (isDiscardableType(valueE->getType())) |
| return; |
| |
| // Complain about '#selector'. |
| if (auto *ObjCSE = dyn_cast<ObjCSelectorExpr>(valueE)) { |
| DE.diagnose(ObjCSE->getLoc(), diag::expression_unused_selector_result) |
| .highlight(E->getSourceRange()); |
| return; |
| } |
| |
| // Complain about '#keyPath'. |
| if (isa<KeyPathExpr>(valueE)) { |
| DE.diagnose(valueE->getLoc(), diag::expression_unused_keypath_result) |
| .highlight(E->getSourceRange()); |
| return; |
| } |
| |
| // Always complain about 'try?'. |
| if (auto *OTE = dyn_cast<OptionalTryExpr>(valueE)) { |
| DE.diagnose(OTE->getTryLoc(), diag::expression_unused_optional_try) |
| .highlight(E->getSourceRange()); |
| return; |
| } |
| |
| // If we have an OptionalEvaluationExpr at the top level, then someone is |
| // "optional chaining" and ignoring the result. Produce a diagnostic if it |
| // doesn't make sense to ignore it. |
| if (auto *OEE = dyn_cast<OptionalEvaluationExpr>(valueE)) { |
| if (auto *IIO = dyn_cast<InjectIntoOptionalExpr>(OEE->getSubExpr())) |
| return checkIgnoredExpr(IIO->getSubExpr()); |
| if (auto *C = dyn_cast<CallExpr>(OEE->getSubExpr())) |
| return checkIgnoredExpr(C); |
| if (auto *OE = dyn_cast<OpenExistentialExpr>(OEE->getSubExpr())) |
| return checkIgnoredExpr(OE); |
| } |
| |
| if (auto *LE = dyn_cast<LiteralExpr>(valueE)) { |
| diagnoseIgnoredLiteral(Context, LE); |
| return; |
| } |
| |
| // Check if we have a call to a function not marked with |
| // '@discardableResult'. |
| if (auto call = dyn_cast<ApplyExpr>(valueE)) { |
| // Dig through all levels of calls. |
| Expr *fn = call->getFn(); |
| while (true) { |
| fn = fn->getSemanticsProvidingExpr(); |
| if (auto applyFn = dyn_cast<ApplyExpr>(fn)) { |
| fn = applyFn->getFn(); |
| } else if (auto FVE = dyn_cast<ForceValueExpr>(fn)) { |
| fn = FVE->getSubExpr(); |
| } else if (auto dotSyntaxRef = dyn_cast<DotSyntaxBaseIgnoredExpr>(fn)) { |
| fn = dotSyntaxRef->getRHS(); |
| } else { |
| break; |
| } |
| } |
| |
| // Find the callee. |
| AbstractFunctionDecl *callee = nullptr; |
| if (auto declRef = dyn_cast<DeclRefExpr>(fn)) |
| callee = dyn_cast<AbstractFunctionDecl>(declRef->getDecl()); |
| else if (auto ctorRef = dyn_cast<OtherConstructorDeclRefExpr>(fn)) |
| callee = ctorRef->getDecl(); |
| else if (auto memberRef = dyn_cast<MemberRefExpr>(fn)) |
| callee = dyn_cast<AbstractFunctionDecl>(memberRef->getMember().getDecl()); |
| else if (auto dynMemberRef = dyn_cast<DynamicMemberRefExpr>(fn)) |
| callee = dyn_cast<AbstractFunctionDecl>( |
| dynMemberRef->getMember().getDecl()); |
| |
| // If the callee explicitly allows its result to be ignored, then don't |
| // complain. |
| if (callee && callee->getAttrs().getAttribute<DiscardableResultAttr>()) |
| return; |
| |
| // Otherwise, complain. Start with more specific diagnostics. |
| |
| // Diagnose unused literals that were translated to implicit |
| // constructor calls during CSApply / ExprRewriter::convertLiteral. |
| if (call->isImplicit()) { |
| Expr *arg = call->getArg(); |
| if (auto *TE = dyn_cast<TupleExpr>(arg)) |
| if (TE->getNumElements() == 1) |
| arg = TE->getElement(0); |
| |
| if (auto *LE = dyn_cast<LiteralExpr>(arg)) { |
| diagnoseIgnoredLiteral(Context, LE); |
| return; |
| } |
| } |
| |
| // Other unused constructor calls. |
| if (callee && isa<ConstructorDecl>(callee) && !call->isImplicit()) { |
| DE.diagnose(fn->getLoc(), diag::expression_unused_init_result, |
| callee->getDeclContext()->getDeclaredInterfaceType()) |
| .highlight(call->getArg()->getSourceRange()); |
| return; |
| } |
| |
| SourceRange SR1 = call->getArg()->getSourceRange(), SR2; |
| if (auto *BO = dyn_cast<BinaryExpr>(call)) { |
| SR1 = BO->getArg()->getElement(0)->getSourceRange(); |
| SR2 = BO->getArg()->getElement(1)->getSourceRange(); |
| } |
| |
| // Otherwise, produce a generic diagnostic. |
| if (callee) { |
| auto &ctx = callee->getASTContext(); |
| if (callee->isImplicit()) { |
| // Translate calls to implicit functions to their user-facing names |
| if (callee->getBaseName() == ctx.Id_derived_enum_equals || |
| callee->getBaseName() == ctx.Id_derived_struct_equals) { |
| DE.diagnose(fn->getLoc(), diag::expression_unused_result_operator, |
| ctx.Id_EqualsOperator) |
| .highlight(SR1).highlight(SR2); |
| return; |
| } |
| } |
| |
| auto diagID = diag::expression_unused_result_call; |
| if (callee->getName().isOperator()) |
| diagID = diag::expression_unused_result_operator; |
| |
| DE.diagnose(fn->getLoc(), diagID, callee->getName()) |
| .highlight(SR1).highlight(SR2); |
| } else |
| DE.diagnose(fn->getLoc(), diag::expression_unused_result_unknown, |
| isa<ClosureExpr>(fn), valueE->getType()) |
| .highlight(SR1).highlight(SR2); |
| |
| return; |
| } |
| |
| // Produce a generic diagnostic. |
| DE.diagnose(valueE->getLoc(), diag::expression_unused_result, |
| valueE->getType()) |
| .highlight(valueE->getSourceRange()); |
| } |
| |
| void StmtChecker::typeCheckASTNode(ASTNode &node) { |
| // Type check the expression |
| if (auto *E = node.dyn_cast<Expr *>()) { |
| auto &ctx = DC->getASTContext(); |
| |
| TypeCheckExprOptions options = TypeCheckExprFlags::IsExprStmt; |
| bool isDiscarded = |
| (!ctx.LangOpts.Playground && !ctx.LangOpts.DebuggerSupport); |
| if (isDiscarded) |
| options |= TypeCheckExprFlags::IsDiscarded; |
| if (LeaveBraceStmtBodyUnchecked) { |
| options |= TypeCheckExprFlags::LeaveClosureBodyUnchecked; |
| options |= TypeCheckExprFlags::AllowUnresolvedTypeVariables; |
| } |
| |
| auto resultTy = |
| TypeChecker::typeCheckExpression(E, DC, /*contextualInfo=*/{}, options); |
| |
| // If a closure expression is unused, the user might have intended to write |
| // "do { ... }". |
| auto *CE = dyn_cast<ClosureExpr>(E); |
| if (CE || isa<CaptureListExpr>(E)) { |
| ctx.Diags.diagnose(E->getLoc(), diag::expression_unused_closure); |
| |
| if (CE && CE->hasAnonymousClosureVars() && |
| CE->getParameters()->size() == 0) { |
| ctx.Diags.diagnose(CE->getStartLoc(), diag::brace_stmt_suggest_do) |
| .fixItInsert(CE->getStartLoc(), "do "); |
| } |
| } else if (isDiscarded && resultTy) { |
| TypeChecker::checkIgnoredExpr(E); |
| } |
| |
| node = E; |
| return; |
| } |
| |
| // Type check the statement. |
| if (auto *S = node.dyn_cast<Stmt *>()) { |
| typeCheckStmt(S); |
| node = S; |
| return; |
| } |
| |
| // Type check the declaration. |
| if (auto *D = node.dyn_cast<Decl *>()) { |
| TypeChecker::typeCheckDecl(D); |
| return; |
| } |
| |
| llvm_unreachable("Type checking null ASTNode"); |
| } |
| |
| Stmt *StmtChecker::visitBraceStmt(BraceStmt *BS) { |
| if (LeaveBraceStmtBodyUnchecked) |
| return BS; |
| |
| // Diagnose defer statement being last one in block (only if |
| // BraceStmt does not start a TopLevelDecl). |
| if (!BS->empty()) { |
| if (auto stmt = |
| BS->getLastElement().dyn_cast<Stmt *>()) { |
| if (auto deferStmt = dyn_cast<DeferStmt>(stmt)) { |
| if (!isa<TopLevelCodeDecl>(DC) || |
| cast<TopLevelCodeDecl>(DC)->getBody() != BS) { |
| getASTContext().Diags.diagnose(deferStmt->getStartLoc(), |
| diag::defer_stmt_at_block_end) |
| .fixItReplace(deferStmt->getStartLoc(), "do"); |
| } |
| } |
| } |
| } |
| |
| for (auto &elem : BS->getElements()) |
| typeCheckASTNode(elem); |
| |
| return BS; |
| } |
| |
| void TypeChecker::typeCheckASTNode(ASTNode &node, DeclContext *DC, |
| bool LeaveBodyUnchecked) { |
| StmtChecker stmtChecker(DC); |
| // FIXME: 'ActiveLabeledStmts', etc. in StmtChecker are not |
| // populated. Since they don't affect "type checking", it's doesn't cause |
| // any issue for now. But it should be populated nonetheless. |
| stmtChecker.LeaveBraceStmtBodyUnchecked = LeaveBodyUnchecked; |
| stmtChecker.typeCheckASTNode(node); |
| } |
| |
| static Type getResultBuilderType(FuncDecl *FD) { |
| Type builderType = FD->getResultBuilderType(); |
| |
| // For getters, fall back on looking on the attribute on the storage. |
| if (!builderType) { |
| auto accessor = dyn_cast<AccessorDecl>(FD); |
| if (accessor && accessor->getAccessorKind() == AccessorKind::Get) { |
| builderType = accessor->getStorage()->getResultBuilderType(); |
| } |
| } |
| |
| return builderType; |
| } |
| |
| static Expr* constructCallToSuperInit(ConstructorDecl *ctor, |
| ClassDecl *ClDecl) { |
| ASTContext &Context = ctor->getASTContext(); |
| Expr *superRef = new (Context) SuperRefExpr(ctor->getImplicitSelfDecl(), |
| SourceLoc(), /*Implicit=*/true); |
| Expr *r = UnresolvedDotExpr::createImplicit( |
| Context, superRef, DeclBaseName::createConstructor()); |
| r = CallExpr::createImplicit(Context, r, { }, { }); |
| |
| if (ctor->hasThrows()) |
| r = new (Context) TryExpr(SourceLoc(), r, Type(), /*implicit=*/true); |
| |
| DiagnosticSuppression suppression(ctor->getASTContext().Diags); |
| auto resultTy = TypeChecker::typeCheckExpression( |
| r, ctor, /*contextualInfo=*/{}, TypeCheckExprFlags::IsDiscarded); |
| if (!resultTy) |
| return nullptr; |
| |
| return r; |
| } |
| |
| /// Check a super.init call. |
| /// |
| /// \returns true if an error occurred. |
| static bool checkSuperInit(ConstructorDecl *fromCtor, |
| ApplyExpr *apply, bool implicitlyGenerated) { |
| // Make sure we are referring to a designated initializer. |
| auto otherCtorRef = dyn_cast<OtherConstructorDeclRefExpr>( |
| apply->getSemanticFn()); |
| if (!otherCtorRef) |
| return false; |
| |
| auto ctor = otherCtorRef->getDecl(); |
| if (!ctor->isDesignatedInit()) { |
| if (!implicitlyGenerated) { |
| auto selfTy = fromCtor->getDeclContext()->getSelfInterfaceType(); |
| if (auto classTy = selfTy->getClassOrBoundGenericClass()) { |
| assert(classTy->getSuperclass()); |
| auto &Diags = fromCtor->getASTContext().Diags; |
| Diags.diagnose(apply->getArg()->getLoc(), diag::chain_convenience_init, |
| classTy->getSuperclass()); |
| ctor->diagnose(diag::convenience_init_here); |
| } |
| } |
| return true; |
| } |
| |
| // For an implicitly generated super.init() call, make sure there's |
| // only one designated initializer. |
| if (implicitlyGenerated) { |
| auto *dc = ctor->getDeclContext(); |
| auto *superclassDecl = dc->getSelfClassDecl(); |
| |
| superclassDecl->synthesizeSemanticMembersIfNeeded( |
| DeclBaseName::createConstructor()); |
| |
| NLOptions subOptions = NL_QualifiedDefault; |
| |
| SmallVector<ValueDecl *, 4> lookupResults; |
| fromCtor->lookupQualified(superclassDecl, |
| DeclNameRef::createConstructor(), |
| subOptions, lookupResults); |
| |
| for (auto decl : lookupResults) { |
| auto superclassCtor = dyn_cast<ConstructorDecl>(decl); |
| if (!superclassCtor || !superclassCtor->isDesignatedInit() || |
| superclassCtor == ctor) |
| continue; |
| |
| // Found another designated initializer in the superclass. Don't add the |
| // super.init() call. |
| return true; |
| } |
| |
| // Make sure we can reference the designated initializer correctly. |
| auto loc = fromCtor->getLoc(); |
| diagnoseDeclAvailability( |
| ctor, loc, nullptr, |
| ExportContext::forFunctionBody(fromCtor, loc)); |
| } |
| |
| |
| return false; |
| } |
| |
| static bool isKnownEndOfConstructor(ASTNode N) { |
| auto *S = N.dyn_cast<Stmt*>(); |
| if (!S) return false; |
| |
| return isa<ReturnStmt>(S) || isa<FailStmt>(S); |
| } |
| |
| /// Check for problems specific to the body of a constructor within a |
| /// class, involving (e.g.) implicit calls to the superclass initializer and |
| /// issues related to designated/convenience initializers. |
| static void checkClassConstructorBody(ClassDecl *classDecl, |
| ConstructorDecl *ctor, |
| BraceStmt *body) { |
| ASTContext &ctx = classDecl->getASTContext(); |
| bool wantSuperInitCall = false; |
| bool isDelegating = false; |
| auto initKindAndExpr = ctor->getDelegatingOrChainedInitKind(); |
| switch (initKindAndExpr.initKind) { |
| case BodyInitKind::Delegating: |
| isDelegating = true; |
| wantSuperInitCall = false; |
| break; |
| |
| case BodyInitKind::Chained: |
| checkSuperInit(ctor, initKindAndExpr.initExpr, false); |
| |
| /// A convenience initializer cannot chain to a superclass constructor. |
| if (ctor->isConvenienceInit()) { |
| ctx.Diags.diagnose(initKindAndExpr.initExpr->getLoc(), |
| diag::delegating_convenience_super_init, |
| ctor->getDeclContext()->getDeclaredInterfaceType()); |
| } |
| |
| LLVM_FALLTHROUGH; |
| |
| case BodyInitKind::None: |
| wantSuperInitCall = false; |
| break; |
| |
| case BodyInitKind::ImplicitChained: |
| wantSuperInitCall = true; |
| break; |
| } |
| |
| // A class designated initializer must never be delegating. |
| if (ctor->isDesignatedInit() && isDelegating) { |
| if (classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType) { |
| ctor->diagnose(diag::delegating_designated_init_in_extension, |
| ctor->getDeclContext()->getDeclaredInterfaceType()); |
| } else { |
| ctor->diagnose(diag::delegating_designated_init, |
| ctor->getDeclContext()->getDeclaredInterfaceType()) |
| .fixItInsert(ctor->getLoc(), "convenience "); |
| } |
| |
| ctx.Diags.diagnose(initKindAndExpr.initExpr->getLoc(), diag::delegation_here); |
| } |
| |
| // An inlinable constructor in a class must always be delegating, |
| // unless the class is '@_fixed_layout'. |
| // Note: This is specifically not using isFormallyResilient. We relax this |
| // rule for classes in non-resilient modules so that they can have inlinable |
| // constructors, as long as those constructors don't reference private |
| // declarations. |
| if (!isDelegating && classDecl->isResilient()) { |
| auto kind = ctor->getFragileFunctionKind(); |
| if (kind.kind != FragileFunctionKind::None) { |
| ctor->diagnose(diag::class_designated_init_inlinable_resilient, |
| classDecl->getDeclaredInterfaceType(), |
| static_cast<unsigned>(kind.kind)); |
| } |
| } |
| |
| // If we don't want a super.init call, we're done. |
| if (!wantSuperInitCall) |
| return; |
| |
| // Find a default initializer in the superclass. |
| Expr *SuperInitCall = constructCallToSuperInit(ctor, classDecl); |
| if (!SuperInitCall) |
| return; |
| |
| // If the initializer we found is a designated initializer, we're okay. |
| class FindOtherConstructorRef : public ASTWalker { |
| public: |
| ApplyExpr *Found = nullptr; |
| |
| std::pair<bool, Expr *> walkToExprPre(Expr *E) override { |
| if (auto apply = dyn_cast<ApplyExpr>(E)) { |
| if (isa<OtherConstructorDeclRefExpr>(apply->getSemanticFn())) { |
| Found = apply; |
| return { false, E }; |
| } |
| } |
| |
| return { Found == nullptr, E }; |
| } |
| }; |
| |
| FindOtherConstructorRef finder; |
| SuperInitCall->walk(finder); |
| if (!checkSuperInit(ctor, finder.Found, true)) { |
| // Store the super.init expression within the constructor declaration |
| // to be emitted during SILGen. |
| ctor->setSuperInitCall(SuperInitCall); |
| } |
| } |
| |
| bool TypeCheckASTNodeAtLocRequest::evaluate(Evaluator &evaluator, |
| DeclContext *DC, |
| SourceLoc Loc) const { |
| auto &ctx = DC->getASTContext(); |
| assert(DiagnosticSuppression::isEnabled(ctx.Diags) && |
| "Diagnosing and Single ASTNode type checknig don't mix"); |
| |
| // Find innermost ASTNode at Loc from DC. Results the reference to the found |
| // ASTNode and the decl context of it. |
| class ASTNodeFinder : public ASTWalker { |
| SourceManager &SM; |
| SourceLoc Loc; |
| ASTNode *FoundNode = nullptr; |
| DeclContext *DC = nullptr; |
| |
| public: |
| ASTNodeFinder(SourceManager &SM, SourceLoc Loc) : SM(SM), Loc(Loc) {} |
| |
| bool isNull() const { return !FoundNode; } |
| ASTNode &getRef() const { |
| assert(FoundNode); |
| return *FoundNode; |
| } |
| DeclContext *getDeclContext() const { return DC; } |
| |
| std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override { |
| if (auto *brace = dyn_cast<BraceStmt>(S)) { |
| auto braceCharRange = Lexer::getCharSourceRangeFromSourceRange( |
| SM, brace->getSourceRange()); |
| // Unless this brace contains the loc, there's nothing to do. |
| if (!braceCharRange.contains(Loc)) |
| return {false, S}; |
| |
| // Reset the node found in a parent context. |
| if (!brace->isImplicit()) |
| FoundNode = nullptr; |
| |
| for (ASTNode &node : brace->getElements()) { |
| if (SM.isBeforeInBuffer(Loc, node.getStartLoc())) |
| break; |
| |
| // NOTE: We need to check the character loc here because the target |
| // loc can be inside the last token of the node. i.e. interpolated |
| // string. |
| SourceLoc endLoc = Lexer::getLocForEndOfToken(SM, node.getEndLoc()); |
| if (SM.isBeforeInBuffer(endLoc, Loc) || endLoc == Loc) |
| continue; |
| |
| // 'node' may be the target node, except 'CaseStmt' which cannot be |
| // type checked alone. |
| if (!node.isStmt(StmtKind::Case)) |
| FoundNode = &node; |
| |
| // Walk into the node to narrow down. |
| node.walk(*this); |
| |
| break; |
| } |
| // Already walked into. |
| return {false, nullptr}; |
| } |
| |
| return {true, S}; |
| } |
| |
| std::pair<bool, Expr *> walkToExprPre(Expr *E) override { |
| if (SM.isBeforeInBuffer(Loc, E->getStartLoc())) |
| return {false, E}; |
| |
| SourceLoc endLoc = Lexer::getLocForEndOfToken(SM, E->getEndLoc()); |
| if (SM.isBeforeInBuffer(endLoc, Loc)) |
| return {false, E}; |
| |
| // Don't walk into 'TapExpr'. They should be type checked with parent |
| // 'InterpolatedStringLiteralExpr'. |
| if (isa<TapExpr>(E)) |
| return {false, E}; |
| |
| if (auto closure = dyn_cast<ClosureExpr>(E)) { |
| // NOTE: When a client wants to type check a closure signature, it |
| // requests with closure's 'getLoc()' location. |
| if (Loc == closure->getLoc()) |
| return {false, E}; |
| |
| DC = closure; |
| } |
| return {true, E}; |
| } |
| |
| bool walkToDeclPre(Decl *D) override { |
| if (auto *newDC = dyn_cast<DeclContext>(D)) |
| DC = newDC; |
| return true; |
| } |
| |
| } finder(ctx.SourceMgr, Loc); |
| DC->walkContext(finder); |
| |
| // Nothing found at the location, or the decl context does not own the 'Loc'. |
| if (finder.isNull()) |
| return true; |
| |
| DC = finder.getDeclContext(); |
| |
| if (auto *AFD = dyn_cast<AbstractFunctionDecl>(DC)) { |
| if (AFD->isBodyTypeChecked()) |
| return false; |
| |
| ASTScope::expandFunctionBody(AFD); |
| } |
| |
| // Function builder function doesn't support partial type checking. |
| if (auto *func = dyn_cast<FuncDecl>(DC)) { |
| if (Type builderType = getResultBuilderType(func)) { |
| auto optBody = |
| TypeChecker::applyResultBuilderBodyTransform(func, builderType); |
| if (!optBody || !*optBody) |
| return true; |
| // Wire up the function body now. |
| func->setBody(*optBody, AbstractFunctionDecl::BodyKind::TypeChecked); |
| return false; |
| } else if (func->hasSingleExpressionBody() && |
| func->getResultInterfaceType()->isVoid()) { |
| // The function returns void. We don't need an explicit return, no matter |
| // what the type of the expression is. Take the inserted return back out. |
| func->getBody()->setLastElement(func->getSingleExpressionBody()); |
| } |
| } |
| |
| // The enclosing closure might be a single expression closure or a function |
| // builder closure. In such cases, the body elements are type checked with |
| // the closure itself. So we need to try type checking the enclosing closure |
| // signature first unless it has already been type checked. |
| if (auto CE = dyn_cast<ClosureExpr>(DC)) { |
| if (CE->getBodyState() == ClosureExpr::BodyState::Parsed) { |
| swift::typeCheckASTNodeAtLoc(CE->getParent(), CE->getLoc()); |
| if (CE->getBodyState() != ClosureExpr::BodyState::ReadyForTypeChecking) |
| return false; |
| } |
| } |
| |
| TypeChecker::typeCheckASTNode(finder.getRef(), DC, |
| /*LeaveBodyUnchecked=*/true); |
| return false; |
| } |
| |
| BraceStmt * |
| TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator, |
| AbstractFunctionDecl *AFD) const { |
| ASTContext &ctx = AFD->getASTContext(); |
| |
| Optional<FunctionBodyTimer> timer; |
| const auto &tyOpts = ctx.TypeCheckerOpts; |
| if (tyOpts.DebugTimeFunctionBodies || tyOpts.WarnLongFunctionBodies) |
| timer.emplace(AFD); |
| |
| BraceStmt *body = AFD->getBody(); |
| assert(body && "Expected body to type-check"); |
| |
| // It's possible we sythesized an already type-checked body, in which case |
| // we're done. |
| if (AFD->isBodyTypeChecked()) |
| return body; |
| |
| auto errorBody = [&]() { |
| // If we had an error, return an ErrorExpr body instead of returning the |
| // un-type-checked body. |
| // FIXME: This should be handled by typeCheckExpression. |
| auto range = AFD->getBodySourceRange(); |
| return BraceStmt::create(ctx, range.Start, |
| {new (ctx) ErrorExpr(range, ErrorType::get(ctx))}, |
| range.End); |
| }; |
| |
| bool alreadyTypeChecked = false; |
| if (auto *func = dyn_cast<FuncDecl>(AFD)) { |
| if (Type builderType = getResultBuilderType(func)) { |
| if (auto optBody = |
| TypeChecker::applyResultBuilderBodyTransform( |
| func, builderType)) { |
| if (!*optBody) |
| return errorBody(); |
| |
| body = *optBody; |
| alreadyTypeChecked = true; |
| |
| body->walk(ContextualizeClosures(AFD)); |
| } |
| } else if (func->hasSingleExpressionBody() && |
| func->getResultInterfaceType()->isVoid()) { |
| // The function returns void. We don't need an explicit return, no matter |
| // what the type of the expression is. Take the inserted return back out. |
| body->setLastElement(func->getSingleExpressionBody()); |
| } |
| } else if (isa<ConstructorDecl>(AFD) && |
| (body->empty() || |
| !isKnownEndOfConstructor(body->getLastElement()))) { |
| // For constructors, we make sure that the body ends with a "return" stmt, |
| // which we either implicitly synthesize, or the user can write. This |
| // simplifies SILGen. |
| SmallVector<ASTNode, 8> Elts(body->getElements().begin(), |
| body->getElements().end()); |
| Elts.push_back(new (ctx) ReturnStmt(body->getRBraceLoc(), |
| /*value*/nullptr, |
| /*implicit*/true)); |
| body = BraceStmt::create(ctx, body->getLBraceLoc(), Elts, |
| body->getRBraceLoc(), body->isImplicit()); |
| } |
| |
| // Typechecking, in particular ApplySolution is going to replace closures |
| // with OpaqueValueExprs and then try to do lookups into the closures. |
| // So, build out the body now. |
| ASTScope::expandFunctionBody(AFD); |
| |
| // Type check the function body if needed. |
| bool hadError = false; |
| if (!alreadyTypeChecked) { |
| StmtChecker SC(AFD); |
| hadError = SC.typeCheckBody(body); |
| } |
| |
| // If this was a function with a single expression body, let's see |
| // if implicit return statement came out to be `Never` which means |
| // that we have eagerly converted something like `{ fatalError() }` |
| // into `{ return fatalError() }` that has to be corrected here. |
| if (isa<FuncDecl>(AFD) && cast<FuncDecl>(AFD)->hasSingleExpressionBody()) { |
| if (auto *stmt = body->getLastElement().dyn_cast<Stmt *>()) { |
| if (auto *retStmt = dyn_cast<ReturnStmt>(stmt)) { |
| if (retStmt->isImplicit() && retStmt->hasResult()) { |
| auto returnType = retStmt->getResult()->getType(); |
| if (returnType && returnType->isUninhabited()) |
| body->setLastElement(retStmt->getResult()); |
| } |
| } |
| } |
| } |
| |
| // Class constructor checking. |
| if (auto *ctor = dyn_cast<ConstructorDecl>(AFD)) { |
| if (auto classDecl = ctor->getDeclContext()->getSelfClassDecl()) { |
| checkClassConstructorBody(classDecl, ctor, body); |
| } |
| } |
| |
| // Temporarily wire up the function body for some extra checks. |
| // FIXME: Eliminate this. |
| AFD->setBody(body, AbstractFunctionDecl::BodyKind::TypeChecked); |
| |
| // If nothing went wrong yet, perform extra checking. |
| if (!hadError) |
| performAbstractFuncDeclDiagnostics(AFD); |
| |
| // SWIFT_ENABLE_TENSORFLOW |
| // Check `@compilerEvaluable` function body correctness. |
| // Do this here, rather than in |
| // `AttributeChecker::visitCompilerEvaluableAttr()` because we need the |
| // function bodies to be type checked. |
| TypeChecker::checkFunctionBodyCompilerEvaluable(AFD); |
| // SWIFT_ENABLE_TENSORFLOW END |
| |
| TypeChecker::computeCaptures(AFD); |
| checkFunctionActorIsolation(AFD); |
| TypeChecker::checkFunctionEffects(AFD); |
| |
| return hadError ? errorBody() : body; |
| } |
| |
| bool TypeChecker::typeCheckClosureBody(ClosureExpr *closure) { |
| TypeChecker::checkParameterList(closure->getParameters(), closure); |
| |
| BraceStmt *body = closure->getBody(); |
| |
| Optional<FunctionBodyTimer> timer; |
| const auto &tyOpts = closure->getASTContext().TypeCheckerOpts; |
| if (tyOpts.DebugTimeFunctionBodies || tyOpts.WarnLongFunctionBodies) |
| timer.emplace(closure); |
| |
| bool HadError = StmtChecker(closure).typeCheckBody(body); |
| if (body) { |
| closure->setBody(body, closure->hasSingleExpressionBody()); |
| } |
| closure->setBodyState(ClosureExpr::BodyState::SeparatelyTypeChecked); |
| return HadError; |
| } |
| |
| bool TypeChecker::typeCheckTapBody(TapExpr *expr, DeclContext *DC) { |
| // We intentionally use typeCheckStmt instead of typeCheckBody here |
| // because we want to contextualize TapExprs with the body they're in. |
| BraceStmt *body = expr->getBody(); |
| bool HadError = StmtChecker(DC).typeCheckStmt(body); |
| if (body) { |
| expr->setBody(body); |
| } |
| return HadError; |
| } |
| |
| void TypeChecker::typeCheckTopLevelCodeDecl(TopLevelCodeDecl *TLCD) { |
| // We intentionally use typeCheckStmt instead of typeCheckBody here |
| // because we want to contextualize all the TopLevelCode |
| // declarations simultaneously. |
| BraceStmt *Body = TLCD->getBody(); |
| StmtChecker(TLCD).typeCheckStmt(Body); |
| TLCD->setBody(Body); |
| checkTopLevelActorIsolation(TLCD); |
| checkTopLevelEffects(TLCD); |
| performTopLevelDeclDiagnostics(TLCD); |
| } |
| |
| void swift::checkUnknownAttrRestrictions( |
| ASTContext &ctx, CaseStmt *caseBlock, |
| bool &limitExhaustivityChecks) { |
| CaseStmt *fallthroughDest = caseBlock->findNextCaseStmt(); |
| if (caseBlock->getCaseLabelItems().size() != 1) { |
| assert(!caseBlock->getCaseLabelItems().empty() && |
| "parser should not produce case blocks with no items"); |
| ctx.Diags.diagnose(caseBlock->getLoc(), |
| diag::unknown_case_multiple_patterns) |
| .highlight(caseBlock->getCaseLabelItems()[1].getSourceRange()); |
| limitExhaustivityChecks = true; |
| } |
| |
| if (fallthroughDest != nullptr) { |
| if (!caseBlock->isDefault()) |
| ctx.Diags.diagnose(caseBlock->getLoc(), |
| diag::unknown_case_must_be_last); |
| limitExhaustivityChecks = true; |
| } |
| |
| const auto &labelItem = caseBlock->getCaseLabelItems().front(); |
| if (labelItem.getGuardExpr() && !labelItem.isDefault()) { |
| ctx.Diags.diagnose(labelItem.getStartLoc(), |
| diag::unknown_case_where_clause) |
| .highlight(labelItem.getGuardExpr()->getSourceRange()); |
| } |
| |
| const Pattern *pattern = |
| labelItem.getPattern()->getSemanticsProvidingPattern(); |
| if (!isa<AnyPattern>(pattern)) { |
| ctx.Diags.diagnose(labelItem.getStartLoc(), |
| diag::unknown_case_must_be_catchall) |
| .highlight(pattern->getSourceRange()); |
| } |
| } |
| |
| void swift::bindSwitchCasePatternVars(DeclContext *dc, CaseStmt *caseStmt) { |
| llvm::SmallDenseMap<Identifier, std::pair<VarDecl *, bool>, 4> latestVars; |
| auto recordVar = [&](Pattern *pattern, VarDecl *var) { |
| if (!var->hasName()) |
| return; |
| |
| // If there is an existing variable with this name, set it as the |
| // parent of this new variable. |
| auto &entry = latestVars[var->getName()]; |
| if (entry.first) { |
| assert(!var->getParentVarDecl() || |
| var->getParentVarDecl() == entry.first); |
| var->setParentVarDecl(entry.first); |
| |
| // Check for a mutability mismatch. |
| if (pattern && entry.second != var->isLet()) { |
| // Find the original declaration. |
| auto initialCaseVarDecl = entry.first; |
| while (auto parentVar = initialCaseVarDecl->getParentVarDecl()) |
| initialCaseVarDecl = parentVar; |
| |
| auto diag = var->diagnose(diag::mutability_mismatch_multiple_pattern_list, |
| var->isLet(), initialCaseVarDecl->isLet()); |
| |
| BindingPattern *foundVP = nullptr; |
| pattern->forEachNode([&](Pattern *P) { |
| if (auto *VP = dyn_cast<BindingPattern>(P)) |
| if (VP->getSingleVar() == var) |
| foundVP = VP; |
| }); |
| if (foundVP) |
| diag.fixItReplace(foundVP->getLoc(), |
| initialCaseVarDecl->isLet() ? "let" : "var"); |
| |
| var->setInvalid(); |
| initialCaseVarDecl->setInvalid(); |
| } |
| } else { |
| entry.second = var->isLet(); |
| } |
| |
| // Record this variable as the latest with this name. |
| entry.first = var; |
| }; |
| |
| // Wire up the parent var decls for each variable that occurs within |
| // the patterns of each case item. in source order. |
| for (auto &caseItem : caseStmt->getMutableCaseLabelItems()) { |
| // Resolve the pattern. |
| auto *pattern = caseItem.getPattern(); |
| if (!caseItem.isPatternResolved()) { |
| pattern = TypeChecker::resolvePattern( |
| pattern, dc, /*isStmtCondition=*/false); |
| if (!pattern) |
| continue; |
| } |
| |
| caseItem.setPattern(pattern, /*resolved=*/true); |
| pattern->forEachVariable( [&](VarDecl *var) { |
| recordVar(pattern, var); |
| }); |
| } |
| |
| // Wire up the case body variables to the latest patterns. |
| for (auto bodyVar : caseStmt->getCaseBodyVariablesOrEmptyArray()) { |
| recordVar(nullptr, bodyVar); |
| } |
| } |