blob: ff6667cff5a3a040b5755937639e0c5857b30670 [file] [log] [blame]
//===--- TypeCheckStmt.cpp - Type Checking for Statements -----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements semantic analysis for statements.
//
//===----------------------------------------------------------------------===//
#include "TypeChecker.h"
#include "MiscDiagnostics.h"
#include "swift/Subsystems.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/Attr.h"
#include "swift/AST/ExprHandle.h"
#include "swift/AST/Identifier.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/Basic/Range.h"
#include "swift/Basic/STLExtras.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Parse/Lexer.h"
#include "swift/Parse/LocalContext.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/Debug.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Timer.h"
using namespace swift;
namespace {
class ContextualizeClosures : public ASTWalker {
DeclContext *ParentDC;
public:
unsigned NextDiscriminator = 0;
ContextualizeClosures(DeclContext *parent,
unsigned nextDiscriminator = 0)
: ParentDC(parent), NextDiscriminator(nextDiscriminator) {}
/// Change the context we're contextualizing to. This is
/// basically only reasonable when processing all the different
/// top-level code declarations.
void setContext(TopLevelCodeDecl *parent) {
ParentDC = parent;
}
bool hasAutoClosures() const {
return NextDiscriminator != 0;
}
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;
return { false, E };
}
// 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 &&
"Incorrect parent decl context for closure");
CE->setParent(ParentDC);
}
}
// If the closure has a single expression body, we need to walk into it
// with a new sequence. Otherwise, it'll have been separately
// type-checked.
if (CE->hasSingleExpressionBody())
CE->getBody()->walk(ContextualizeClosures(CE));
// In neither case do we need to continue the *current* walk.
return { false, E };
}
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);
}
};
class FunctionBodyTimer {
PointerUnion<const AbstractFunctionDecl *,
const AbstractClosureExpr *> Function;
llvm::TimeRecord StartTime = llvm::TimeRecord::getCurrentTime();
public:
FunctionBodyTimer(decltype(Function) Fn) : Function(Fn) {}
~FunctionBodyTimer() {
llvm::TimeRecord endTime = llvm::TimeRecord::getCurrentTime(false);
auto elapsed = endTime.getProcessTime() - StartTime.getProcessTime();
llvm::errs() << llvm::format("%0.1f", elapsed * 1000) << "ms\t";
if (auto *AFD = Function.dyn_cast<const AbstractFunctionDecl *>()) {
AFD->getLoc().print(llvm::errs(), AFD->getASTContext().SourceMgr);
llvm::errs() << "\t";
AFD->print(llvm::errs(), PrintOptions());
} else {
auto *ACE = Function.get<const AbstractClosureExpr *>();
ACE->getLoc().print(llvm::errs(), ACE->getASTContext().SourceMgr);
llvm::errs() << "\t(closure)";
}
llvm::errs() << "\n";
}
};
}
static void setAutoClosureDiscriminators(DeclContext *DC, Stmt *S) {
S->walk(ContextualizeClosures(DC));
}
bool TypeChecker::contextualizeInitializer(Initializer *DC, Expr *E) {
ContextualizeClosures CC(DC);
E->walk(CC);
return CC.hasAutoClosures();
}
void TypeChecker::contextualizeTopLevelCode(TopLevelContext &TLC,
ArrayRef<Decl*> topLevel) {
unsigned nextDiscriminator = TLC.NextAutoClosureDiscriminator;
ContextualizeClosures CC(nullptr, nextDiscriminator);
for (auto decl : topLevel) {
auto topLevelCode = dyn_cast<TopLevelCodeDecl>(decl);
if (!topLevelCode) continue;
CC.setContext(topLevelCode);
topLevelCode->getBody()->walk(CC);
}
assert(nextDiscriminator == TLC.NextAutoClosureDiscriminator &&
"reentrant/concurrent invocation of contextualizeTopLevelCode?");
TLC.NextAutoClosureDiscriminator = CC.NextDiscriminator;
}
/// Emits an error with a fixit for the case of unnecessary cast over a
/// OptionSetType 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,
Module *module) {
auto *NTD = ResultType->getAnyNominal();
if (!NTD)
return;
auto optionSetType = dyn_cast<ProtocolDecl>(Ctx.getOptionSetTypeDecl());
SmallVector<ProtocolConformance *, 4> conformances;
if (!(optionSetType &&
NTD->lookupConformance(module, optionSetType, conformances)))
return;
CallExpr *CE = dyn_cast<CallExpr>(E);
if (!CE)
return;
if (!isa<ConstructorRefCallExpr>(CE->getFn()))
return;
ParenExpr *ParenE = dyn_cast<ParenExpr>(CE->getArg());
if (!ParenE)
return;
MemberRefExpr *ME = dyn_cast<MemberRefExpr>(ParenE->getSubExpr());
if (!ME)
return;
ValueDecl *VD = ME->getMember().getDecl();
if (!VD || VD->getName() != Ctx.Id_rawValue)
return;
MemberRefExpr *BME = dyn_cast<MemberRefExpr>(ME->getBase());
if (!BME)
return;
if (BME->getType()->getCanonicalType() != ResultType->getCanonicalType())
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()));
}
namespace {
class StmtChecker : public StmtVisitor<StmtChecker, Stmt*> {
public:
TypeChecker &TC;
/// \brief This is the current function or closure being checked.
/// This is null for top level code.
Optional<AnyFunctionRef> TheFunc;
/// DC - This is the current DeclContext.
DeclContext *DC;
// Scope information for control flow statements
// (break, continue, fallthrough).
/// The level of loop nesting. 'break' and 'continue' are valid only in scopes
/// where this is greater than one.
SmallVector<LabeledStmt*, 2> ActiveLabeledStmts;
/// The level of 'switch' nesting. 'fallthrough' is valid only in scopes where
/// this is greater than one.
unsigned SwitchLevel = 0;
/// The destination block for a 'fallthrough' statement. Null if the switch
/// scope depth is zero or if we are checking the final 'case' of the current
/// switch.
CaseStmt /*nullable*/ *FallthroughDest = nullptr;
SourceLoc EndTypeCheckLoc;
/// Used to check for discarded expression values: in the REPL top-level
/// expressions are not discarded.
bool IsREPL;
struct AddLabeledStmt {
StmtChecker &SC;
AddLabeledStmt(StmtChecker &SC, LabeledStmt *LS) : SC(SC) {
// Verify that we don't have label shadowing.
if (!LS->getLabelInfo().Name.empty())
for (auto PrevLS : SC.ActiveLabeledStmts) {
if (PrevLS->getLabelInfo().Name == LS->getLabelInfo().Name) {
SC.TC.diagnose(LS->getLabelInfo().Loc,
diag::label_shadowed, LS->getLabelInfo().Name);
SC.TC.diagnose(PrevLS->getLabelInfo().Loc,
diag::invalid_redecl_prev,
PrevLS->getLabelInfo().Name);
}
}
// In any case, remember that we're in this labeled statement so that
// break and continue are aware of it.
SC.ActiveLabeledStmts.push_back(LS);
}
~AddLabeledStmt() {
SC.ActiveLabeledStmts.pop_back();
}
};
struct AddSwitchNest {
StmtChecker &SC;
CaseStmt *OuterFallthroughDest;
AddSwitchNest(StmtChecker &SC) : SC(SC),
OuterFallthroughDest(SC.FallthroughDest) {
++SC.SwitchLevel;
}
~AddSwitchNest() {
--SC.SwitchLevel;
SC.FallthroughDest = OuterFallthroughDest;
}
};
StmtChecker(TypeChecker &TC, AbstractFunctionDecl *AFD)
: TC(TC), TheFunc(AFD), DC(AFD), IsREPL(false) { }
StmtChecker(TypeChecker &TC, ClosureExpr *TheClosure)
: TC(TC), TheFunc(TheClosure), DC(TheClosure), IsREPL(false) { }
StmtChecker(TypeChecker &TC, DeclContext *DC)
: TC(TC), TheFunc(), DC(DC), IsREPL(false) {
if (const SourceFile *SF = DC->getParentSourceFile())
if (SF->Kind == SourceFileKind::REPL)
IsREPL = true;
}
//===--------------------------------------------------------------------===//
// Helper Functions.
//===--------------------------------------------------------------------===//
bool isInDefer() const {
if (!TheFunc.hasValue()) return false;
auto *FD = dyn_cast_or_null<FuncDecl>
(TheFunc.getValue().getAbstractFunctionDecl());
return FD && FD->isDeferBody();
}
template<typename StmtTy>
bool typeCheckStmt(StmtTy *&S) {
StmtTy *S2 = cast_or_null<StmtTy>(visit(S));
if (S2 == 0) return true;
S = S2;
performStmtDiagnostics(TC, S);
return false;
}
/// Type-check an entire function body.
bool typeCheckBody(BraceStmt *&S) {
if (typeCheckStmt(S)) return true;
setAutoClosureDiscriminators(DC, S);
return false;
}
//===--------------------------------------------------------------------===//
// Visit Methods.
//===--------------------------------------------------------------------===//
Stmt *visitBraceStmt(BraceStmt *BS);
Stmt *visitReturnStmt(ReturnStmt *RS) {
if (!TheFunc.hasValue()) {
TC.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()) {
TC.diagnose(RS->getReturnLoc(), diag::jump_out_of_defer, "return");
return nullptr;
}
Type ResultTy = TheFunc->getBodyResultType();
if (!ResultTy || ResultTy->is<ErrorType>())
return nullptr;
if (!RS->hasResult()) {
if (!ResultTy->isEqual(TupleType::getEmpty(TC.Context)))
TC.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) {
TC.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->getFailability() == OTK_None) {
TC.diagnose(RS->getReturnLoc(), diag::return_non_failable_init)
.highlight(E->getSourceRange());
TC.diagnose(ctor->getLoc(), diag::make_init_failable,
ctor->getFullName())
.fixItInsertAfter(ctor->getLoc(), "?");
RS->setResult(nullptr);
return RS;
}
// Replace the "return nil" with a new 'fail' statement.
return new (TC.Context) FailStmt(RS->getReturnLoc(), nilExpr->getLoc(),
RS->isImplicit());
}
auto hadTypeError = TC.typeCheckExpression(E, DC, ResultTy, CTP_ReturnStmt);
RS->setResult(E);
if (hadTypeError) {
tryDiagnoseUnnecessaryCastOverOptionSet(TC.Context, E, ResultTy,
DC->getParentModule());
return nullptr;
}
return RS;
}
Stmt *visitThrowStmt(ThrowStmt *TS) {
// Coerce the operand to the exception type.
auto E = TS->getSubExpr();
Type exnType = TC.getExceptionType(DC, TS->getThrowLoc());
if (!exnType) return TS;
auto hadTypeError = TC.typeCheckExpression(E, DC, exnType, CTP_ThrowStmt);
TS->setSubExpr(E);
return hadTypeError ? nullptr : TS;
}
Stmt *visitDeferStmt(DeferStmt *DS) {
TC.typeCheckDecl(DS->getTempDecl(), /*isFirstPass*/false);
Expr *theCall = DS->getCallExpr();
auto hadTypeError = TC.typeCheckExpression(theCall, DC);
DS->setCallExpr(theCall);
return hadTypeError ? nullptr : DS;
}
Stmt *visitIfStmt(IfStmt *IS) {
bool hadTypeError = false;
StmtCondition C = IS->getCond();
hadTypeError |= TC.typeCheckStmtCondition(C, DC, diag::if_always_true);
IS->setCond(C);
AddLabeledStmt ifNest(*this, IS);
Stmt *S = IS->getThenStmt();
hadTypeError |= typeCheckStmt(S);
IS->setThenStmt(S);
if ((S = IS->getElseStmt())) {
hadTypeError |= typeCheckStmt(S);
IS->setElseStmt(S);
}
return hadTypeError ? nullptr : IS;
}
Stmt *visitGuardStmt(GuardStmt *GS) {
bool hadTypeError = false;
StmtCondition C = GS->getCond();
hadTypeError |= TC.typeCheckStmtCondition(C, DC, diag::guard_always_succeeds);
GS->setCond(C);
AddLabeledStmt ifNest(*this, GS);
Stmt *S = GS->getBody();
hadTypeError |= typeCheckStmt(S);
GS->setBody(S);
return hadTypeError ? nullptr : GS;
}
Stmt *visitIfConfigStmt(IfConfigStmt *ICS) {
// Active members are attached to the enclosing declaration, so there's no
// need to walk anything within.
return ICS;
}
Stmt *visitDoStmt(DoStmt *DS) {
AddLabeledStmt loopNest(*this, DS);
Stmt *S = DS->getBody();
bool hadTypeError = typeCheckStmt(S);
DS->setBody(S);
return hadTypeError ? nullptr : DS;
}
Stmt *visitWhileStmt(WhileStmt *WS) {
bool hadTypeError = false;
StmtCondition C = WS->getCond();
hadTypeError |= TC.typeCheckStmtCondition(C, DC, diag::while_always_true);
WS->setCond(C);
AddLabeledStmt loopNest(*this, WS);
Stmt *S = WS->getBody();
hadTypeError |= typeCheckStmt(S);
WS->setBody(S);
return hadTypeError ? nullptr : WS;
}
Stmt *visitRepeatWhileStmt(RepeatWhileStmt *RWS) {
bool hadTypeError = false;
{
AddLabeledStmt loopNest(*this, RWS);
Stmt *S = RWS->getBody();
hadTypeError |= typeCheckStmt(S);
RWS->setBody(S);
}
Expr *E = RWS->getCond();
hadTypeError |= TC.typeCheckCondition(E, DC);
RWS->setCond(E);
return hadTypeError ? nullptr : RWS;
}
Stmt *visitForStmt(ForStmt *FS) {
bool hadTypeError = false;
// Type check any var decls in the initializer.
for (auto D : FS->getInitializerVarDecls())
TC.typeCheckDecl(D, /*isFirstPass*/false);
if (auto *Initializer = FS->getInitializer().getPtrOrNull()) {
hadTypeError |= TC.typeCheckExpression(Initializer, DC, Type(), CTP_Unused,
TypeCheckExprFlags::IsDiscarded);
FS->setInitializer(Initializer);
TC.checkIgnoredExpr(Initializer);
}
if (auto *Cond = FS->getCond().getPtrOrNull()) {
hadTypeError |= TC.typeCheckCondition(Cond, DC);
FS->setCond(Cond);
}
if (auto *Increment = FS->getIncrement().getPtrOrNull()) {
hadTypeError |= TC.typeCheckExpression(Increment, DC, Type(), CTP_Unused,
TypeCheckExprFlags::IsDiscarded);
FS->setIncrement(Increment);
TC.checkIgnoredExpr(Increment);
}
AddLabeledStmt loopNest(*this, FS);
Stmt *S = FS->getBody();
hadTypeError |= typeCheckStmt(S);
FS->setBody(S);
return hadTypeError ? nullptr : FS;
}
Stmt *visitForEachStmt(ForEachStmt *S) {
TypeResolutionOptions options;
options |= TR_AllowUnspecifiedTypes;
options |= TR_AllowUnboundGenerics;
options |= TR_InExpression;
if (auto *P = TC.resolvePattern(S->getPattern(), DC,
/*isStmtCondition*/false)) {
S->setPattern(P);
} else {
S->getPattern()->setType(ErrorType::get(TC.Context));
return nullptr;
}
if (TC.typeCheckPattern(S->getPattern(), DC, options)) {
// FIXME: Handle errors better.
S->getPattern()->setType(ErrorType::get(TC.Context));
return nullptr;
}
if (TC.typeCheckForEachBinding(DC, S))
return nullptr;
if (auto *Where = S->getWhere()) {
if (TC.typeCheckCondition(Where, DC))
return nullptr;
S->setWhere(Where);
}
// Retrieve the 'Sequence' protocol.
ProtocolDecl *sequenceProto
= TC.getProtocol(S->getForLoc(), KnownProtocolKind::SequenceType);
if (!sequenceProto) {
return nullptr;
}
// Retrieve the 'Generator' protocol.
ProtocolDecl *generatorProto
= TC.getProtocol(S->getForLoc(), KnownProtocolKind::GeneratorType);
if (!generatorProto) {
return nullptr;
}
// If the sequence is an implicitly unwrapped optional, force it.
Expr *sequence = S->getSequence();
if (auto objectTy
= sequence->getType()->getImplicitlyUnwrappedOptionalObjectType()) {
sequence = new (TC.Context) ForceValueExpr(sequence,
sequence->getEndLoc());
sequence->setType(objectTy);
sequence->setImplicit();
S->setSequence(sequence);
}
// Invoke generate() to get a generator from the sequence.
Type generatorTy;
VarDecl *generator;
{
Type sequenceType = sequence->getType();
ProtocolConformance *conformance = nullptr;
if (!TC.conformsToProtocol(sequenceType, sequenceProto, DC,
ConformanceCheckFlags::InExpression,
&conformance, sequence->getLoc()))
return nullptr;
if (conformance && conformance->isInvalid())
return nullptr;
generatorTy = TC.getWitnessType(sequenceType, sequenceProto,
conformance,
TC.Context.Id_Generator,
diag::sequence_protocol_broken);
Expr *getGenerator
= TC.callWitness(sequence, DC, sequenceProto, conformance,
TC.Context.Id_generate,
{}, diag::sequence_protocol_broken);
if (!getGenerator) return nullptr;
// Create a local variable to capture the generator.
std::string name;
if (auto np = dyn_cast_or_null<NamedPattern>(S->getPattern()))
name = "$"+np->getBoundName().str().str();
name += "$generator";
generator = new (TC.Context)
VarDecl(/*static*/ false, /*IsLet*/ false, S->getInLoc(),
TC.Context.getIdentifier(name), generatorTy, DC);
generator->setImplicit();
// Create a pattern binding to initialize the generator.
auto genPat = new (TC.Context) NamedPattern(generator);
genPat->setImplicit();
auto genBinding =
PatternBindingDecl::create(TC.Context, SourceLoc(),
StaticSpellingKind::None,
S->getForLoc(), genPat, getGenerator, DC);
genBinding->setImplicit();
S->setGenerator(genBinding);
}
// Working with generators requires Optional.
if (TC.requireOptionalIntrinsics(S->getForLoc()))
return nullptr;
// Gather the witnesses from the Generator protocol conformance, which
// we'll use to drive the loop.
// FIXME: Would like to customize the diagnostic emitted in
// conformsToProtocol().
ProtocolConformance *genConformance = nullptr;
if (!TC.conformsToProtocol(generatorTy, generatorProto, DC,
ConformanceCheckFlags::InExpression,
&genConformance, sequence->getLoc()))
return nullptr;
Type elementTy = TC.getWitnessType(generatorTy, generatorProto,
genConformance, TC.Context.Id_Element,
diag::generator_protocol_broken);
if (!elementTy)
return nullptr;
// Compute the expression that advances the generator.
Expr *genNext
= TC.callWitness(TC.buildCheckedRefExpr(generator, DC, S->getInLoc(),
/*implicit*/true),
DC, generatorProto, genConformance,
TC.Context.Id_next, {}, diag::generator_protocol_broken);
if (!genNext) return nullptr;
// Check that next() produces an Optional<T> value.
if (genNext->getType()->getCanonicalType()->getAnyNominal()
!= TC.Context.getOptionalDecl()) {
TC.diagnose(S->getForLoc(), diag::generator_protocol_broken);
return nullptr;
}
// Convert that Optional<T> value to Optional<Element>.
auto optPatternType = OptionalType::get(S->getPattern()->getType());
if (!optPatternType->isEqual(genNext->getType()) &&
TC.convertToType(genNext, optPatternType, DC)) {
return nullptr;
}
S->setGeneratorNext(genNext);
// Type-check the body of the loop.
AddLabeledStmt loopNest(*this, S);
BraceStmt *Body = S->getBody();
if (typeCheckStmt(Body)) return nullptr;
S->setBody(Body);
return S;
}
Stmt *visitBreakStmt(BreakStmt *S) {
LabeledStmt *Target = nullptr;
// Pick the nearest break target that matches the specified name.
if (S->getTargetName().empty()) {
for (auto I = ActiveLabeledStmts.rbegin(), E = ActiveLabeledStmts.rend();
I != E; ++I) {
// 'break' with no label looks through non-loop structures
// except 'switch'.
if (!(*I)->requiresLabelOnJump()) {
Target = *I;
break;
}
}
} else {
// Scan inside out until we find something with the right label.
for (auto I = ActiveLabeledStmts.rbegin(), E = ActiveLabeledStmts.rend();
I != E; ++I) {
if (S->getTargetName() == (*I)->getLabelInfo().Name) {
Target = *I;
break;
}
}
}
if (!Target) {
// If we're in a defer, produce a tailored diagnostic.
if (isInDefer()) {
TC.diagnose(S->getLoc(), diag::jump_out_of_defer, "break");
return nullptr;
}
auto diagid = diag::break_outside_loop;
// If someone is using an unlabeled break inside of an 'if' or 'do'
// statement, produce a more specific error.
if (S->getTargetName().empty() && !ActiveLabeledStmts.empty() &&
(isa<IfStmt>(ActiveLabeledStmts.back()) ||
isa<DoStmt>(ActiveLabeledStmts.back())))
diagid = diag::unlabeled_break_outside_loop;
TC.diagnose(S->getLoc(), diagid);
return nullptr;
}
S->setTarget(Target);
return S;
}
Stmt *visitContinueStmt(ContinueStmt *S) {
LabeledStmt *Target = nullptr;
// Scan to see if we are in any non-switch labeled statements (loops). Scan
// inside out.
if (S->getTargetName().empty()) {
for (auto I = ActiveLabeledStmts.rbegin(), E = ActiveLabeledStmts.rend();
I != E; ++I) {
// 'continue' with no label ignores non-loop structures.
if (!(*I)->requiresLabelOnJump() &&
(*I)->isPossibleContinueTarget()) {
Target = *I;
break;
}
}
} else {
// Scan inside out until we find something with the right label.
for (auto I = ActiveLabeledStmts.rbegin(), E = ActiveLabeledStmts.rend();
I != E; ++I) {
if (S->getTargetName() == (*I)->getLabelInfo().Name) {
Target = *I;
break;
}
}
}
if (!Target) {
// If we're in a defer, produce a tailored diagnostic.
if (isInDefer()) {
TC.diagnose(S->getLoc(), diag::jump_out_of_defer, "break");
return nullptr;
}
TC.diagnose(S->getLoc(), diag::continue_outside_loop);
return nullptr;
}
// Continue cannot be used to repeat switches, use fallthrough instead.
if (!Target->isPossibleContinueTarget()) {
TC.diagnose(S->getLoc(), diag::continue_not_in_this_stmt,
isa<SwitchStmt>(Target) ? "switch" : "if");
return nullptr;
}
S->setTarget(Target);
return S;
}
Stmt *visitFallthroughStmt(FallthroughStmt *S) {
if (!SwitchLevel) {
TC.diagnose(S->getLoc(), diag::fallthrough_outside_switch);
return nullptr;
}
if (!FallthroughDest) {
TC.diagnose(S->getLoc(), diag::fallthrough_from_last_case);
return nullptr;
}
if (FallthroughDest->hasBoundDecls())
TC.diagnose(S->getLoc(), diag::fallthrough_into_case_with_var_binding);
S->setFallthroughDest(FallthroughDest);
return S;
}
Stmt *visitSwitchStmt(SwitchStmt *S) {
bool hadTypeError = false;
// Type-check the subject expression.
Expr *subjectExpr = S->getSubjectExpr();
hadTypeError |= TC.typeCheckExpression(subjectExpr, DC);
subjectExpr = TC.coerceToMaterializable(subjectExpr);
if (subjectExpr) {
S->setSubjectExpr(subjectExpr);
}
Type subjectType = S->getSubjectExpr()->getType();
// Type-check the case blocks.
AddSwitchNest switchNest(*this);
AddLabeledStmt labelNest(*this, S);
for (unsigned i = 0, e = S->getCases().size(); i < e; ++i) {
auto *caseBlock = S->getCases()[i];
// Fallthrough transfers control to the next case block. In the
// final case block, it is invalid.
FallthroughDest = i+1 == e ? nullptr : S->getCases()[i+1];
for (auto &labelItem : caseBlock->getMutableCaseLabelItems()) {
// Resolve the pattern in the label.
Pattern *pattern = labelItem.getPattern();
if (auto *newPattern = TC.resolvePattern(pattern, DC,
/*isStmtCondition*/false)) {
pattern = newPattern;
// Coerce the pattern to the subject's type.
if (TC.coercePatternToType(pattern, DC, subjectType,
TR_InExpression)) {
// If that failed, mark any variables binding pieces of the pattern
// as invalid to silence follow-on errors.
pattern->forEachVariable([&](VarDecl *VD) {
VD->overwriteType(ErrorType::get(TC.Context));
VD->setInvalid();
});
hadTypeError = true;
}
labelItem.setPattern(pattern);
} else {
hadTypeError = true;
}
// Check the guard expression, if present.
if (auto *guard = labelItem.getGuardExpr()) {
hadTypeError |= TC.typeCheckCondition(guard, DC);
labelItem.setGuardExpr(guard);
}
}
// Type-check the body statements.
Stmt *body = caseBlock->getBody();
hadTypeError |= typeCheckStmt(body);
caseBlock->setBody(body);
}
return hadTypeError ? nullptr : S;
}
Stmt *visitCaseStmt(CaseStmt *S) {
// Cases are handled in visitSwitchStmt.
llvm_unreachable("case stmt outside of switch?!");
}
Stmt *visitCatchStmt(CatchStmt *S) {
// Catches are handled in visitDoCatchStmt.
llvm_unreachable("catch stmt outside of do-catch?!");
}
bool checkCatchStmt(CatchStmt *S) {
bool hadTypeError = false;
// Check the catch pattern.
hadTypeError |= TC.typeCheckCatchPattern(S, DC);
// Check the guard expression, if present.
if (Expr *guard = S->getGuardExpr()) {
hadTypeError |= TC.typeCheckCondition(guard, DC);
S->setGuardExpr(guard);
}
// Type-check the clause body.
Stmt *body = S->getBody();
hadTypeError |= typeCheckStmt(body);
S->setBody(body);
return hadTypeError;
}
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.
AddLabeledStmt loopNest(*this, S);
bool hadTypeError = false;
// Type-check the 'do' body. Type failures in here will generally
// not cause type failures in the 'catch' clauses.
Stmt *newBody = S->getBody();
hadTypeError |= typeCheckStmt(newBody);
S->setBody(newBody);
// Check all the catch clauses independently.
for (auto clause : S->getCatches()) {
hadTypeError |= checkCatchStmt(clause);
}
return hadTypeError ? nullptr : 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
bool TypeChecker::typeCheckCatchPattern(CatchStmt *S, DeclContext *DC) {
bool hadTypeError = false;
// Grab the standard exception type.
Type exnType = getExceptionType(DC, S->getCatchLoc());
Pattern *pattern = S->getErrorPattern();
if (Pattern *newPattern = resolvePattern(pattern, DC,
/*isStmtCondition*/false)) {
pattern = newPattern;
// Coerce the pattern to the exception type.
if (!exnType ||
coercePatternToType(pattern, DC, exnType, TR_InExpression)) {
// If that failed, be sure to give the variables error types
// before we type-check the guard. (This will probably kill
// most of the type-checking, but maybe not.)
pattern->forEachVariable([&](VarDecl *var) {
var->overwriteType(ErrorType::get(Context));
var->setInvalid();
});
hadTypeError = true;
}
S->setErrorPattern(pattern);
} else {
hadTypeError = true;
}
return hadTypeError;
}
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;
}
// Complain about l-values that are neither loaded nor stored.
if (E->getType()->isLValueType()) {
diagnose(E->getLoc(), diag::expression_unused_lvalue)
.highlight(E->getSourceRange());
return;
}
// Complain about functions that aren't called.
// TODO: What about tuples which contain functions by-value that are
// dead?
if (E->getType()->is<AnyFunctionType>()) {
diagnose(E->getLoc(), diag::expression_unused_function)
.highlight(E->getSourceRange());
return;
}
auto valueE = E->getValueProvidingExpr();
// Always complain about 'try?'.
if (auto *OTE = dyn_cast<OptionalTryExpr>(valueE)) {
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());
// FIXME: Complain about literals
// Check if we have a call to a function marked warn_unused_result.
if (auto call = dyn_cast<ApplyExpr>(valueE)) {
// Dig through all levels of calls.
Expr *fn = call->getFn()->getSemanticsProvidingExpr();
bool baseIsLValue = false;
while (auto applyFn = dyn_cast<ApplyExpr>(fn)) {
if (auto dotSyntax = dyn_cast<DotSyntaxCallExpr>(applyFn)) {
Expr *base = dotSyntax->getBase();
baseIsLValue = isa<LoadExpr>(base);
}
fn = applyFn->getFn()->getSemanticsProvidingExpr();
}
// 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 (callee) {
if (auto attr = callee->getAttrs().getAttribute<WarnUnusedResultAttr>()) {
if (!attr->getMutableVariant().empty() && baseIsLValue) {
DeclName replacementName(Context,
Context.getIdentifier(
attr->getMutableVariant()),
callee->getFullName().getArgumentNames());
diagnose(fn->getLoc(), diag::expression_unused_result_nonmutating,
callee->getFullName(), replacementName)
.fixItReplace(fn->getLoc(), attr->getMutableVariant());
return;
}
if (!attr->getMessage().empty()) {
diagnose(fn->getLoc(), diag::expression_unused_result_message,
callee->getFullName(), attr->getMessage());
return;
}
diagnose(fn->getLoc(), diag::expression_unused_result,
callee->getFullName());
return;
}
if (isa<ConstructorDecl>(callee) && !call->isImplicit()) {
diagnose(fn->getLoc(), diag::expression_unused_init_result);
}
}
}
}
Stmt *StmtChecker::visitBraceStmt(BraceStmt *BS) {
const SourceManager &SM = TC.Context.SourceMgr;
for (auto &elem : BS->getElements()) {
if (Expr *SubExpr = elem.dyn_cast<Expr*>()) {
SourceLoc Loc = SubExpr->getStartLoc();
if (EndTypeCheckLoc.isValid() &&
(Loc == EndTypeCheckLoc || SM.isBeforeInBuffer(EndTypeCheckLoc, Loc)))
break;
// Type check the expression.
TypeCheckExprOptions options = TypeCheckExprFlags::IsExprStmt;
bool isDiscarded = !(IsREPL && isa<TopLevelCodeDecl>(DC))
&& !TC.Context.LangOpts.Playground
&& !TC.Context.LangOpts.DebuggerSupport;
if (isDiscarded)
options |= TypeCheckExprFlags::IsDiscarded;
if (TC.typeCheckExpression(SubExpr, DC, Type(), CTP_Unused, options)) {
elem = SubExpr;
continue;
}
if (isDiscarded)
TC.checkIgnoredExpr(SubExpr);
elem = SubExpr;
continue;
}
if (Stmt *SubStmt = elem.dyn_cast<Stmt*>()) {
SourceLoc Loc = SubStmt->getStartLoc();
if (EndTypeCheckLoc.isValid() &&
(Loc == EndTypeCheckLoc || SM.isBeforeInBuffer(EndTypeCheckLoc, Loc)))
break;
if (!typeCheckStmt(SubStmt))
elem = SubStmt;
continue;
}
Decl *SubDecl = elem.get<Decl *>();
SourceLoc Loc = SubDecl->getStartLoc();
if (EndTypeCheckLoc.isValid() &&
(Loc == EndTypeCheckLoc || SM.isBeforeInBuffer(EndTypeCheckLoc, Loc)))
break;
TC.typeCheckDecl(SubDecl, /*isFirstPass*/false);
}
return BS;
}
/// Check the default arguments that occur within this pattern.
static void checkDefaultArguments(TypeChecker &tc, Pattern *pattern,
unsigned &nextArgIndex,
DeclContext *dc) {
assert(dc->isLocalContext());
switch (pattern->getKind()) {
case PatternKind::Tuple:
for (auto &field : cast<TuplePattern>(pattern)->getElements()) {
unsigned curArgIndex = nextArgIndex++;
if (field.getInit() &&
field.getPattern()->hasType() &&
!field.getPattern()->getType()->is<ErrorType>()) {
Expr *e = field.getInit()->getExpr();
// Re-use an existing initializer context if possible.
auto existingContext = e->findExistingInitializerContext();
DefaultArgumentInitializer *initContext;
if (existingContext) {
initContext = cast<DefaultArgumentInitializer>(existingContext);
assert(initContext->getIndex() == curArgIndex);
assert(initContext->getParent() == dc);
// Otherwise, allocate one temporarily.
} else {
initContext =
tc.Context.createDefaultArgumentContext(dc, curArgIndex);
}
// Type-check the initializer, then flag that we did so.
if (tc.typeCheckExpression(e, initContext,field.getPattern()->getType(),
CTP_DefaultParameter))
field.getInit()->setExpr(field.getInit()->getExpr(), true);
else
field.getInit()->setExpr(e, true);
tc.checkInitializerErrorHandling(initContext, e);
// Walk the checked initializer and contextualize any closures
// we saw there.
bool hasClosures = tc.contextualizeInitializer(initContext, e);
// If we created a new context and didn't run into any autoclosures
// during the walk, give the context back to the ASTContext.
if (!hasClosures && !existingContext)
tc.Context.destroyDefaultArgumentContext(initContext);
}
}
return;
case PatternKind::Paren:
return checkDefaultArguments(tc,
cast<ParenPattern>(pattern)->getSubPattern(),
nextArgIndex,
dc);
case PatternKind::Var:
return checkDefaultArguments(tc, cast<VarPattern>(pattern)->getSubPattern(),
nextArgIndex,
dc);
case PatternKind::Typed:
case PatternKind::Named:
case PatternKind::Any:
return;
#define PATTERN(Id, Parent)
#define REFUTABLE_PATTERN(Id, Parent) case PatternKind::Id:
#include "swift/AST/PatternNodes.def"
llvm_unreachable("pattern can't appear in argument list!");
}
llvm_unreachable("bad pattern kind!");
}
bool TypeChecker::typeCheckAbstractFunctionBodyUntil(AbstractFunctionDecl *AFD,
SourceLoc EndTypeCheckLoc) {
if (auto *FD = dyn_cast<FuncDecl>(AFD))
return typeCheckFunctionBodyUntil(FD, EndTypeCheckLoc);
if (auto *CD = dyn_cast<ConstructorDecl>(AFD))
return typeCheckConstructorBodyUntil(CD, EndTypeCheckLoc);
auto *DD = cast<DestructorDecl>(AFD);
return typeCheckDestructorBodyUntil(DD, EndTypeCheckLoc);
}
bool TypeChecker::typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD) {
if (!AFD->getBody())
return false;
Optional<FunctionBodyTimer> timer;
if (DebugTimeFunctionBodies)
timer.emplace(AFD);
if (typeCheckAbstractFunctionBodyUntil(AFD, SourceLoc()))
return true;
performAbstractFuncDeclDiagnostics(*this, AFD);
return false;
}
// Type check a function body (defined with the func keyword) that is either a
// named function or an anonymous func expression.
bool TypeChecker::typeCheckFunctionBodyUntil(FuncDecl *FD,
SourceLoc EndTypeCheckLoc) {
// Check the default argument definitions.
unsigned nextArgIndex = 0;
for (auto pattern : FD->getBodyParamPatterns()) {
checkDefaultArguments(*this, pattern, nextArgIndex, FD);
}
// Clang imported inline functions do not have a Swift body to
// typecheck.
if (FD->getClangDecl())
return false;
BraceStmt *BS = FD->getBody();
assert(BS && "Should have a body");
StmtChecker SC(*this, static_cast<AbstractFunctionDecl *>(FD));
SC.EndTypeCheckLoc = EndTypeCheckLoc;
bool HadError = SC.typeCheckBody(BS);
FD->setBody(BS);
return HadError;
}
Expr* TypeChecker::constructCallToSuperInit(ConstructorDecl *ctor,
ClassDecl *ClDecl) {
Expr *superRef = new (Context) SuperRefExpr(ctor->getImplicitSelfDecl(),
SourceLoc(), /*Implicit=*/true);
Expr *r = new (Context) UnresolvedConstructorExpr(superRef,
SourceLoc(),
SourceLoc(),
/*Implicit=*/true);
Expr *args = TupleExpr::createEmpty(Context, SourceLoc(), SourceLoc(),
/*Implicit=*/true);
r = new (Context) CallExpr(r, args, /*Implicit=*/true);
if (ctor->isBodyThrowing())
r = new (Context) TryExpr(SourceLoc(), r, Type(), /*Implicit=*/true);
if (typeCheckExpression(r, ctor, Type(), CTP_Unused,
TypeCheckExprFlags::IsDiscarded |
TypeCheckExprFlags::SuppressDiagnostics))
return nullptr;
return r;
}
/// Check a super.init call.
///
/// \returns true if an error occurred.
static bool checkSuperInit(TypeChecker &tc, ConstructorDecl *fromCtor,
ApplyExpr *apply, bool implicitlyGenerated) {
// Make sure we are referring to a designated initializer.
auto otherCtorRef = dyn_cast<OtherConstructorDeclRefExpr>(
apply->getFn()->getSemanticsProvidingExpr());
if (!otherCtorRef)
return false;
auto ctor = otherCtorRef->getDecl();
if (!ctor->isDesignatedInit()) {
if (!implicitlyGenerated) {
auto contextTy = fromCtor->getDeclContext()->getDeclaredTypeInContext();
if (auto classTy = contextTy->getClassOrBoundGenericClass()) {
assert(classTy->getSuperclass());
tc.diagnose(apply->getArg()->getLoc(), diag::chain_convenience_init,
classTy->getSuperclass());
tc.diagnose(ctor, diag::convenience_init_here);
}
}
return true;
}
// For an implicitly generated super.init() call, make sure there's
// only one designated initializer.
if (implicitlyGenerated) {
auto superclassTy = ctor->getExtensionType();
NameLookupOptions lookupOptions
= defaultConstructorLookupOptions | NameLookupFlags::KnownPrivate;
for (auto member : tc.lookupConstructors(fromCtor, superclassTy,
lookupOptions)) {
auto superclassCtor = dyn_cast<ConstructorDecl>(member.Decl);
if (!superclassCtor || !superclassCtor->isDesignatedInit() ||
superclassCtor == ctor)
continue;
// Found another designated initializer in the superclass. Don't add the
// super.init() call.
return true;
}
}
return false;
}
static bool isKnownEndOfConstructor(ASTNode N) {
auto *S = N.dyn_cast<Stmt*>();
if (!S) return false;
return isa<ReturnStmt>(S) || isa<FailStmt>(S);
}
bool TypeChecker::typeCheckConstructorBodyUntil(ConstructorDecl *ctor,
SourceLoc EndTypeCheckLoc) {
// Check the default argument definitions.
unsigned nextArgIndex = 0;
for (auto pattern : ctor->getBodyParamPatterns())
checkDefaultArguments(*this, pattern, nextArgIndex, ctor);
BraceStmt *body = ctor->getBody();
if (!body)
return true;
// 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.
if (body->getNumElements() == 0 ||
!isKnownEndOfConstructor(body->getElements().back())) {
SmallVector<ASTNode, 8> Elts(body->getElements().begin(),
body->getElements().end());
Elts.push_back(new (Context) ReturnStmt(SourceLoc(), /*value*/nullptr,
/*implicit*/true));
body = BraceStmt::create(Context, body->getLBraceLoc(), Elts,
body->getRBraceLoc(), body->isImplicit());
ctor->setBody(body);
}
// Type-check the body.
StmtChecker SC(*this, static_cast<AbstractFunctionDecl *>(ctor));
SC.EndTypeCheckLoc = EndTypeCheckLoc;
bool HadError = SC.typeCheckBody(body);
if (ctor->isInvalid())
return HadError;
// Determine whether we need to introduce a super.init call.
auto nominalDecl = ctor->getDeclContext()->getDeclaredTypeInContext()
->getNominalOrBoundGenericNominal();
ClassDecl *ClassD = dyn_cast<ClassDecl>(nominalDecl);
bool wantSuperInitCall = false;
if (ClassD) {
bool isDelegating = false;
ApplyExpr *initExpr = nullptr;
switch (ctor->getDelegatingOrChainedInitKind(&Diags, &initExpr)) {
case ConstructorDecl::BodyInitKind::Delegating:
isDelegating = true;
wantSuperInitCall = false;
break;
case ConstructorDecl::BodyInitKind::Chained:
checkSuperInit(*this, ctor, initExpr, false);
/// A convenience initializer cannot chain to a superclass constructor.
if (ctor->isConvenienceInit()) {
diagnose(initExpr->getLoc(), diag::delegating_convenience_super_init,
ctor->getDeclContext()->getDeclaredTypeOfContext());
}
SWIFT_FALLTHROUGH;
case ConstructorDecl::BodyInitKind::None:
wantSuperInitCall = false;
break;
case ConstructorDecl::BodyInitKind::ImplicitChained:
wantSuperInitCall = true;
break;
}
// A class designated initializer must never be delegating.
if (ctor->isDesignatedInit() && ClassD && isDelegating) {
diagnose(ctor->getLoc(),
diag::delegating_designated_init,
ctor->getDeclContext()->getDeclaredTypeOfContext())
.fixItInsert(ctor->getLoc(), "convenience ");
diagnose(initExpr->getLoc(), diag::delegation_here);
ctor->setInitKind(CtorInitializerKind::Convenience);
}
}
// If we want a super.init call...
if (wantSuperInitCall) {
// Find a default initializer in the superclass.
if (Expr *SuperInitCall = constructCallToSuperInit(ctor, ClassD)) {
// 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->getFn()->getSemanticsProvidingExpr())) {
Found = apply;
return { false, E };
}
}
return { Found == nullptr, E };
}
};
FindOtherConstructorRef finder;
SuperInitCall->walk(finder);
if (!checkSuperInit(*this, ctor, finder.Found, true)) {
// Store the super.init expression within the constructor declaration
// to be emitted during SILGen.
ctor->setSuperInitCall(SuperInitCall);
}
}
}
return HadError;
}
bool TypeChecker::typeCheckDestructorBodyUntil(DestructorDecl *DD,
SourceLoc EndTypeCheckLoc) {
StmtChecker SC(*this, static_cast<AbstractFunctionDecl *>(DD));
SC.EndTypeCheckLoc = EndTypeCheckLoc;
BraceStmt *Body = DD->getBody();
if (!Body)
return false;
bool HadError = SC.typeCheckBody(Body);
DD->setBody(Body);
return HadError;
}
void TypeChecker::typeCheckClosureBody(ClosureExpr *closure) {
BraceStmt *body = closure->getBody();
Optional<FunctionBodyTimer> timer;
if (DebugTimeFunctionBodies)
timer.emplace(closure);
StmtChecker(*this, closure).typeCheckBody(body);
if (body) {
closure->setBody(body, closure->hasSingleExpressionBody());
}
}
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(*this, TLCD).typeCheckStmt(Body);
TLCD->setBody(Body);
checkTopLevelErrorHandling(TLCD);
}