Skip to content

Add a high-level request to type check a file #28544

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/swift/AST/ASTTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ SWIFT_TYPEID_NAMED(ParamDecl *, ParamDecl)
SWIFT_TYPEID_NAMED(PatternBindingEntry *, PatternBindingEntry)
SWIFT_TYPEID_NAMED(PrecedenceGroupDecl *, PrecedenceGroupDecl)
SWIFT_TYPEID_NAMED(ProtocolDecl *, ProtocolDecl)
SWIFT_TYPEID_NAMED(SourceFile *, SourceFile)
SWIFT_TYPEID_NAMED(TypeAliasDecl *, TypeAliasDecl)
SWIFT_TYPEID_NAMED(ValueDecl *, ValueDecl)
SWIFT_TYPEID_NAMED(VarDecl *, VarDecl)
1 change: 1 addition & 0 deletions include/swift/AST/ASTTypeIDs.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ struct PropertyWrapperMutability;
class ProtocolDecl;
class Requirement;
enum class ResilienceExpansion : unsigned;
class SourceFile;
class Type;
class ValueDecl;
class VarDecl;
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/SourceFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,11 @@ inline bool ModuleDecl::EntryPointInfoTy::markDiagnosedMainClassWithScript() {
return !res;
}

inline void simple_display(llvm::raw_ostream &out, const SourceFile *SF) {
assert(SF && "Cannot display null source file!");

out << "source_file " << '\"' << SF->getFilename() << '\"';
}
} // end namespace swift

#endif
21 changes: 21 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -1948,6 +1948,27 @@ class DynamicallyReplacedDeclRequest
bool isCached() const { return true; }
};

class TypeCheckSourceFileRequest :
public SimpleRequest<TypeCheckSourceFileRequest,
bool (SourceFile *, unsigned),
CacheKind::SeparatelyCached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

// Evaluation.
llvm::Expected<bool> evaluate(Evaluator &evaluator,
SourceFile *SF, unsigned StartElem) const;

public:
// Separate caching.
bool isCached() const { return true; }
Optional<bool> getCachedResult() const;
void cacheResult(bool result) const;
};

// Allow AnyValue to compare two Type values, even though Type doesn't
// support ==.
template<>
Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ SWIFT_REQUEST(TypeChecker, HasDefaultInitRequest,
bool(NominalTypeDecl *), Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, SynthesizeDefaultInitRequest,
ConstructorDecl *(NominalTypeDecl *), Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, TypeCheckSourceFileRequest,
bool(SouceFile *, unsigned), SeparatelyCached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, TypeWitnessRequest,
TypeWitnessAndDecl(NormalProtocolConformance *,
AssociatedTypeDecl *),
Expand Down
47 changes: 47 additions & 0 deletions lib/AST/TypeCheckRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
#include "swift/AST/NameLookup.h"
#include "swift/AST/PropertyWrappers.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/TypeLoc.h"
#include "swift/AST/TypeRepr.h"
#include "swift/AST/Types.h"
#include "swift/Subsystems.h"

using namespace swift;

Expand Down Expand Up @@ -1238,3 +1240,48 @@ void CallerSideDefaultArgExprRequest::cacheResult(Expr *expr) const {
auto *defaultExpr = std::get<0>(getStorage());
defaultExpr->ContextOrCallerSideExpr = expr;
}

//----------------------------------------------------------------------------//
// TypeCheckSourceFileRequest computation.
//----------------------------------------------------------------------------//

Optional<bool> TypeCheckSourceFileRequest::getCachedResult() const {
auto *SF = std::get<0>(getStorage());
if (SF->ASTStage == SourceFile::TypeChecked)
return true;

return None;
}

void TypeCheckSourceFileRequest::cacheResult(bool result) const {
auto *SF = std::get<0>(getStorage());

// Verify that we've checked types correctly.
SF->ASTStage = SourceFile::TypeChecked;

{
auto &Ctx = SF->getASTContext();
FrontendStatsTracer tracer(Ctx.Stats, "AST verification");
// Verify the SourceFile.
swift::verify(*SF);

// Verify imported modules.
//
// Skip per-file verification in whole-module mode. Verifying imports
// between files could cause the importer to cache declarations without
// adding them to the ASTContext. This happens when the importer registers a
// declaration without a valid TypeChecker instance, as is the case during
// verification. A subsequent file may require that declaration to be fully
// imported (e.g. to synthesized a function body), but since it has already
// been cached, it will never be added to the ASTContext. The solution is to
// skip verification and avoid caching it.
#ifndef NDEBUG
if (!Ctx.TypeCheckerOpts.DelayWholeModuleChecking &&
SF->Kind != SourceFileKind::REPL &&
SF->Kind != SourceFileKind::SIL &&
!Ctx.LangOpts.DebuggerSupport) {
Ctx.verifyAllLoadedModules();
}
#endif
}
}
1 change: 1 addition & 0 deletions lib/Frontend/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "swift/AST/FileSystem.h"
#include "swift/AST/IncrementalRanges.h"
#include "swift/AST/Module.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/FileTypes.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Basic/Statistic.h"
Expand Down
68 changes: 26 additions & 42 deletions lib/Sema/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/Statistic.h"
#include "swift/Basic/STLExtras.h"
#include "swift/Basic/Timer.h"
Expand Down Expand Up @@ -322,26 +323,35 @@ static void typeCheckFunctionsAndExternalDecls(SourceFile &SF, TypeChecker &TC)
}

void swift::performTypeChecking(SourceFile &SF, unsigned StartElem) {
if (SF.ASTStage == SourceFile::TypeChecked)
return;
return (void)evaluateOrDefault(SF.getASTContext().evaluator,
TypeCheckSourceFileRequest{&SF, StartElem},
false);
}

llvm::Expected<bool>
TypeCheckSourceFileRequest::evaluate(Evaluator &eval,
SourceFile *SF, unsigned StartElem) const {
assert(SF && "Source file cannot be null!");
assert(SF->ASTStage != SourceFile::TypeChecked &&
"Should not be re-typechecking this file!");

// Eagerly build the top-level scopes tree before type checking
// because type-checking expressions mutates the AST and that throws off the
// scope-based lookups. Only the top-level scopes because extensions have not
// been bound yet.
auto &Ctx = SF.getASTContext();
if (Ctx.LangOpts.EnableASTScopeLookup && SF.isSuitableForASTScopes())
SF.getScope()
auto &Ctx = SF->getASTContext();
if (Ctx.LangOpts.EnableASTScopeLookup && SF->isSuitableForASTScopes())
SF->getScope()
.buildEnoughOfTreeForTopLevelExpressionsButDontRequestGenericsOrExtendedNominals();

BufferIndirectlyCausingDiagnosticRAII cpr(SF);
BufferIndirectlyCausingDiagnosticRAII cpr(*SF);

// Make sure we have a type checker.
TypeChecker &TC = createTypeChecker(Ctx);

// Make sure that name binding has been completed before doing any type
// checking.
performNameBinding(SF, StartElem);
performNameBinding(*SF, StartElem);

// Could build scope maps here because the AST is stable now.

Expand All @@ -352,22 +362,22 @@ void swift::performTypeChecking(SourceFile &SF, unsigned StartElem) {
// Disable this optimization if we're compiling SwiftOnoneSupport, because
// we _definitely_ need to look inside every declaration to figure out
// what gets prespecialized.
if (SF.getParentModule()->isOnoneSupportModule())
if (SF->getParentModule()->isOnoneSupportModule())
Ctx.TypeCheckerOpts.SkipNonInlinableFunctionBodies = false;

if (!Ctx.LangOpts.DisableAvailabilityChecking) {
// Build the type refinement hierarchy for the primary
// file before type checking.
TypeChecker::buildTypeRefinementContextHierarchy(SF, StartElem);
TypeChecker::buildTypeRefinementContextHierarchy(*SF, StartElem);
}

// Resolve extensions. This has to occur first during type checking,
// because the extensions need to be wired into the AST for name lookup
// to work.
::bindExtensions(SF);
::bindExtensions(*SF);

// Type check the top-level elements of the source file.
for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) {
for (auto D : llvm::makeArrayRef(SF->Decls).slice(StartElem)) {
if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
// Immediately perform global name-binding etc.
TypeChecker::typeCheckTopLevelCodeDecl(TLCD);
Expand All @@ -379,44 +389,18 @@ void swift::performTypeChecking(SourceFile &SF, unsigned StartElem) {

// If we're in REPL mode, inject temporary result variables and other stuff
// that the REPL needs to synthesize.
if (SF.Kind == SourceFileKind::REPL && !Ctx.hadError())
TypeChecker::processREPLTopLevel(SF, StartElem);
if (SF->Kind == SourceFileKind::REPL && !Ctx.hadError())
TypeChecker::processREPLTopLevel(*SF, StartElem);

typeCheckFunctionsAndExternalDecls(SF, TC);
typeCheckFunctionsAndExternalDecls(*SF, TC);
}

// Checking that benefits from having the whole module available.
if (!Ctx.TypeCheckerOpts.DelayWholeModuleChecking) {
performWholeModuleTypeChecking(SF);
performWholeModuleTypeChecking(*SF);
}

// Verify that we've checked types correctly.
SF.ASTStage = SourceFile::TypeChecked;

{
FrontendStatsTracer tracer(Ctx.Stats, "AST verification");
// Verify the SourceFile.
verify(SF);

// Verify imported modules.
//
// Skip per-file verification in whole-module mode. Verifying imports
// between files could cause the importer to cache declarations without
// adding them to the ASTContext. This happens when the importer registers a
// declaration without a valid TypeChecker instance, as is the case during
// verification. A subsequent file may require that declaration to be fully
// imported (e.g. to synthesized a function body), but since it has already
// been cached, it will never be added to the ASTContext. The solution is to
// skip verification and avoid caching it.
#ifndef NDEBUG
if (!Ctx.TypeCheckerOpts.DelayWholeModuleChecking &&
SF.Kind != SourceFileKind::REPL &&
SF.Kind != SourceFileKind::SIL &&
!Ctx.LangOpts.DebuggerSupport) {
Ctx.verifyAllLoadedModules();
}
#endif
}
return true;
}

void swift::performWholeModuleTypeChecking(SourceFile &SF) {
Expand Down
2 changes: 1 addition & 1 deletion test/decl/class/circular_inheritance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class Outer3 // expected-error {{circular reference}}
}

// CHECK: ===CYCLE DETECTED===
// CHECK-NEXT: `--{{.*}}HasCircularInheritanceRequest(circular_inheritance.(file).Left@
// CHECK-LABEL: `--{{.*}}HasCircularInheritanceRequest(circular_inheritance.(file).Left@
// CHECK-NEXT: `--{{.*}}SuperclassDeclRequest({{.*Left}}
// CHECK: `--{{.*}}InheritedDeclsReferencedRequest(circular_inheritance.(file).Left@
// CHECK: `--{{.*}}SuperclassDeclRequest
Expand Down