mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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:
@@ -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 *>>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user