Sema: Keep track of per-source file synthesized declarations

This allows conformance checking to be performed after all
members of a type have been type checked.
This commit is contained in:
Slava Pestov
2018-04-02 17:27:24 -07:00
parent 3bcdf1a027
commit fd79f66d39
5 changed files with 53 additions and 24 deletions

View File

@@ -854,6 +854,14 @@ public:
/// complete, we diagnose.
llvm::SetVector<const DeclAttribute *> AttrsRequiringFoundation;
/// A set of synthesized declarations that need to be type checked.
llvm::SmallVector<Decl *, 8> SynthesizedDecls;
/// We might perform type checking on the same source file more than once,
/// if its the main file or a REPL instance, so keep track of the last
/// checked synthesized declaration to avoid duplicating work.
unsigned LastCheckedSynthesizedDecl = 0;
/// A mapping from Objective-C selectors to the methods that have
/// those selectors.
llvm::DenseMap<ObjCSelector, llvm::TinyPtrVector<AbstractFunctionDecl *>>

View File

@@ -1342,8 +1342,12 @@ void ASTContext::addExternalDecl(Decl *decl) {
void ASTContext::addSynthesizedDecl(Decl *decl) {
auto *mod = cast<FileUnit>(decl->getDeclContext()->getModuleScopeContext());
if (mod->getKind() == FileUnitKind::ClangModule)
if (mod->getKind() == FileUnitKind::ClangModule) {
ExternalDefinitions.insert(decl);
return;
}
cast<SourceFile>(mod)->SynthesizedDecls.push_back(decl);
}
void ASTContext::addCleanup(std::function<void(void)> cleanup) {

View File

@@ -1050,8 +1050,6 @@ deriveHashable_hashValue(TypeChecker &tc, Decl *parentDecl,
getterDecl->setValidationStarted();
getterDecl->copyFormalAccessFrom(typeDecl);
tc.Context.addSynthesizedDecl(getterDecl);
// Finish creating the property.
hashValueDecl->setImplicit();
hashValueDecl->setInterfaceType(intType);
@@ -1073,6 +1071,9 @@ deriveHashable_hashValue(TypeChecker &tc, Decl *parentDecl,
parentDC);
patDecl->setImplicit();
tc.Context.addSynthesizedDecl(hashValueDecl);
tc.Context.addSynthesizedDecl(getterDecl);
auto dc = cast<IterableDeclContext>(parentDecl);
dc->addMember(getterDecl);
dc->addMember(hashValueDecl);

View File

@@ -1443,9 +1443,18 @@ bool TypeChecker::typeCheckAbstractFunctionBodyUntil(AbstractFunctionDecl *AFD,
}
bool TypeChecker::typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD) {
// HACK: don't type-check the same function body twice. This is
// supposed to be handled by just not enqueuing things twice,
// but that gets tricky with synthesized function bodies.
if (AFD->isBodyTypeChecked())
return false;
if (!AFD->getBody())
return false;
FrontendStatsTracer StatsTracer(Context.Stats, "typecheck-fn", AFD);
PrettyStackTraceDecl StackEntry("type-checking", AFD);
if (Context.Stats)
Context.Stats->getFrontendCounters().NumFunctionsTypechecked++;
@@ -1456,7 +1465,10 @@ bool TypeChecker::typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD) {
for (auto paramList : AFD->getParameterLists())
requestRequiredNominalTypeLayoutForParameters(paramList);
if (typeCheckAbstractFunctionBodyUntil(AFD, SourceLoc()))
bool error = typeCheckAbstractFunctionBodyUntil(AFD, SourceLoc());
AFD->setBodyTypeCheckedIfPresent();
if (error)
return true;
performAbstractFuncDeclDiagnostics(*this, AFD);

View File

@@ -405,9 +405,10 @@ void TypeChecker::bindExtension(ExtensionDecl *ext) {
::bindExtensionDecl(ext, *this);
}
static void typeCheckFunctionsAndExternalDecls(TypeChecker &TC) {
static void typeCheckFunctionsAndExternalDecls(SourceFile &SF, TypeChecker &TC) {
unsigned currentFunctionIdx = 0;
unsigned currentExternalDef = TC.Context.LastCheckedExternalDefinition;
unsigned currentSynthesizedDecl = SF.LastCheckedSynthesizedDecl;
do {
// Type check the body of each of the function in turn. Note that outside
// functions must be visited before nested functions for type-checking to
@@ -416,30 +417,16 @@ static void typeCheckFunctionsAndExternalDecls(TypeChecker &TC) {
++currentFunctionIdx) {
auto *AFD = TC.definedFunctions[currentFunctionIdx];
// HACK: don't type-check the same function body twice. This is
// supposed to be handled by just not enqueuing things twice,
// but that gets tricky with synthesized function bodies.
if (AFD->isBodyTypeChecked()) continue;
FrontendStatsTracer StatsTracer(TC.Context.Stats, "typecheck-fn", AFD);
PrettyStackTraceDecl StackEntry("type-checking", AFD);
TC.typeCheckAbstractFunctionBody(AFD);
AFD->setBodyTypeCheckedIfPresent();
}
// Type check external definitions.
for (unsigned n = TC.Context.ExternalDefinitions.size();
currentExternalDef != n;
++currentExternalDef) {
auto decl = TC.Context.ExternalDefinitions[currentExternalDef];
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(decl)) {
// HACK: don't type-check the same function body twice. This is
// supposed to be handled by just not enqueuing things twice,
// but that gets tricky with synthesized function bodies.
if (AFD->isBodyTypeChecked()) continue;
PrettyStackTraceDecl StackEntry("type-checking", AFD);
TC.typeCheckAbstractFunctionBody(AFD);
continue;
}
@@ -474,6 +461,15 @@ static void typeCheckFunctionsAndExternalDecls(TypeChecker &TC) {
TC.finalizeDecl(decl);
}
// Type check synthesized functions and their bodies.
for (unsigned n = SF.SynthesizedDecls.size();
currentSynthesizedDecl != n;
++currentSynthesizedDecl) {
auto decl = SF.SynthesizedDecls[currentSynthesizedDecl];
TC.typeCheckDecl(decl, /*isFirstPass*/true);
TC.typeCheckDecl(decl, /*isFirstPass*/false);
}
// Ensure that the requirements of the given conformance are
// fully checked.
for (unsigned i = 0; i != TC.PartiallyCheckedConformances.size(); ++i) {
@@ -492,6 +488,7 @@ static void typeCheckFunctionsAndExternalDecls(TypeChecker &TC) {
} while (currentFunctionIdx < TC.definedFunctions.size() ||
currentExternalDef < TC.Context.ExternalDefinitions.size() ||
currentSynthesizedDecl < SF.SynthesizedDecls.size() ||
!TC.DeclsToFinalize.empty() ||
!TC.DelayedRequirementSignatures.empty() ||
!TC.UsedConformances.empty() ||
@@ -499,6 +496,7 @@ static void typeCheckFunctionsAndExternalDecls(TypeChecker &TC) {
// FIXME: Horrible hack. Store this somewhere more appropriate.
TC.Context.LastCheckedExternalDefinition = currentExternalDef;
SF.LastCheckedSynthesizedDecl = currentSynthesizedDecl;
// Now that all types have been finalized, run any delayed
// circularity checks.
@@ -540,7 +538,7 @@ void swift::typeCheckExternalDefinitions(SourceFile &SF) {
assert(SF.ASTStage == SourceFile::TypeChecked);
auto &Ctx = SF.getASTContext();
TypeChecker TC(Ctx);
typeCheckFunctionsAndExternalDecls(TC);
typeCheckFunctionsAndExternalDecls(SF, TC);
}
void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC,
@@ -555,6 +553,12 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC,
auto &Ctx = SF.getASTContext();
// Make sure we have a type checker.
//
// FIXME: We should never have a type checker here, but currently do when
// we're using immediate together with -enable-source-import.
//
// This possibility should be eliminated, since it results in duplicated
// work.
Optional<TypeChecker> MyTC;
if (!Ctx.getLazyResolver())
MyTC.emplace(Ctx);
@@ -655,7 +659,7 @@ void swift::performTypeChecking(SourceFile &SF, TopLevelContext &TLC,
if (SF.Kind == SourceFileKind::REPL && !Ctx.hadError())
TC.processREPLTopLevel(SF, TLC, StartElem);
typeCheckFunctionsAndExternalDecls(TC);
typeCheckFunctionsAndExternalDecls(SF, TC);
}
// Checking that benefits from having the whole module available.