[Deserialization] Output a diagnostic for invalid decls or types

47b068d445 output a diagnostic if a
deserialized decl was invalid (checking `Decl::isInvalid`). It had two
major issues:
  1. It incorrectly output diagnostics for valid modules
  2. It did not catch call invalid declarations

(1) is caused by `isInvalid` falling back to checking the storage for
accessors when the interface type hasn't already been computed (a
fallback to prevent a cycle due to `SimpleDidSetRequest` typechecking
the body). Since the `VarDecl` hasn't finished deserializing, it returns
`true` for its `isInvalid` check (even though it would later return
`false`).

For (2), only `ValueDecl`s would ever be invalid, since other
declarations use a bit in `Decl` to check for validity. As that's not
serialized, those would always be valid in deserialization.

To avoid both these issues, instead output a flag for each declaration
representing whether it is invalid (or not). Read that during
deserialization and output a diagnostic if it is invalid. To be extra
sure that a diagnostic is always output on an error, also output one
when deserializing any `ErrorType`. This ensures that SILGen does not
run when allowing errors (and an error is present), as it is likely to
crash when presented with an invalid AST.

Resolves rdar://74541834
This commit is contained in:
Ben Barham
2021-02-27 14:16:33 +10:00
parent a9290166d8
commit ce70727e6f
8 changed files with 94 additions and 37 deletions

View File

@@ -2369,6 +2369,8 @@ class DeclDeserializer {
ASTContext &ctx;
Serialized<Decl *> &declOrOffset;
bool IsInvalid = false;
DeclAttribute *DAttrs = nullptr;
DeclAttribute **AttrsNext = &DAttrs;
@@ -2421,6 +2423,22 @@ public:
if (!decl)
return;
if (IsInvalid) {
decl->setInvalidBit();
DeclName name;
if (auto *VD = dyn_cast<ValueDecl>(decl)) {
name = VD->getName();
}
auto diagId = ctx.LangOpts.AllowModuleWithCompilerErrors
? diag::serialization_allowing_invalid_decl
: diag::serialization_invalid_decl;
ctx.Diags.diagnose(SourceLoc(), diagId, name,
decl->getDescriptiveKind(),
MF.getAssociatedModule()->getNameStr());
}
if (DAttrs)
decl->getAttrs().setRawAttributeChain(DAttrs);
@@ -2436,10 +2454,9 @@ public:
}
}
/// Deserializes decl attribute and attribute-like records from
/// \c MF.DeclTypesCursor until a non-attribute record is found,
/// passing each one to AddAttribute.
llvm::Error deserializeDeclAttributes();
/// Deserializes records common to all decls from \c MF.DeclTypesCursor (ie.
/// the invalid flag, attributes, and discriminators)
llvm::Error deserializeDeclCommon();
Expected<Decl *> getDeclCheckedImpl(
llvm::function_ref<bool(DeclAttributes)> matchAttributes = nullptr);
@@ -4083,19 +4100,6 @@ ModuleFile::getDeclChecked(
matchAttributes);
if (!deserialized)
return deserialized;
auto *decl = declOrOffset.get();
if (isAllowModuleWithCompilerErrorsEnabled() && decl->isInvalid()) {
if (!isa<ParamDecl>(decl) && !decl->isImplicit()) {
// The parent function will be invalid if the parameter is invalid,
// implicits should have an invalid explicit as well
if (auto *VD = dyn_cast<ValueDecl>(decl)) {
getContext().Diags.diagnose(
VD->getLoc(), diag::serialization_allowing_invalid_decl,
VD->getName(), VD->getModuleContext()->getNameStr());
}
}
}
} else if (matchAttributes) {
// Decl was cached but we may need to filter it
if (!matchAttributes(declOrOffset.get()->getAttrs()))
@@ -4115,7 +4119,7 @@ ModuleFile::getDeclChecked(
return declOrOffset;
}
llvm::Error DeclDeserializer::deserializeDeclAttributes() {
llvm::Error DeclDeserializer::deserializeDeclCommon() {
using namespace decls_block;
SmallVector<uint64_t, 64> scratch;
@@ -4132,7 +4136,10 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() {
unsigned recordID = MF.fatalIfUnexpected(
MF.DeclTypeCursor.readRecord(entry.ID, scratch, &blobData));
if (isDeclAttrRecord(recordID)) {
if (recordID == ERROR_FLAG) {
assert(!IsInvalid && "Error flag written multiple times");
IsInvalid = true;
} else if (isDeclAttrRecord(recordID)) {
DeclAttribute *Attr = nullptr;
bool skipAttr = false;
switch (recordID) {
@@ -4459,7 +4466,7 @@ llvm::Error DeclDeserializer::deserializeDeclAttributes() {
// because it requires `DifferentiableAttr::setOriginalDeclaration` to
// be called first. `DifferentiableAttr::setOriginalDeclaration` cannot
// be called here because the original declaration is not accessible in
// this function (`DeclDeserializer::deserializeDeclAttributes`).
// this function (`DeclDeserializer::deserializeDeclCommon`).
diffAttrParamIndicesMap[diffAttr] = indices;
diffAttr->setDerivativeGenericSignature(derivativeGenSig);
Attr = diffAttr;
@@ -4616,9 +4623,9 @@ Expected<Decl *>
DeclDeserializer::getDeclCheckedImpl(
llvm::function_ref<bool(DeclAttributes)> matchAttributes) {
auto attrError = deserializeDeclAttributes();
if (attrError)
return std::move(attrError);
auto commonError = deserializeDeclCommon();
if (commonError)
return std::move(commonError);
if (matchAttributes) {
// Deserialize the full decl only if matchAttributes finds a match.
@@ -5764,19 +5771,28 @@ public:
Expected<Type> deserializeErrorType(ArrayRef<uint64_t> scratch,
StringRef blobData) {
if (!MF.isAllowModuleWithCompilerErrorsEnabled())
MF.fatal();
TypeID origID;
decls_block::ErrorTypeLayout::readRecord(scratch, origID);
auto origTy = MF.getTypeChecked(origID);
if (!origTy)
return origTy.takeError();
auto origTyOrError = MF.getTypeChecked(origID);
if (!origTyOrError)
return origTyOrError.takeError();
if (!origTy.get())
auto origTy = *origTyOrError;
auto diagId = ctx.LangOpts.AllowModuleWithCompilerErrors
? diag::serialization_allowing_error_type
: diag::serialization_error_type;
// Generally not a super useful diagnostic, so only output once if there
// hasn't been any other diagnostic yet to ensure nothing slips by and
// causes SILGen to crash.
if (!ctx.hadError()) {
ctx.Diags.diagnose(SourceLoc(), diagId, StringRef(origTy.getString()),
MF.getAssociatedModule()->getNameStr());
}
if (!origTy)
return ErrorType::get(ctx);
return ErrorType::get(origTy.get());
return ErrorType::get(origTy);
}
};
}