Skip to content

Commit 2d9cdb6

Browse files
committed
[Deserialization] Output a diagnostic when deserializing an invalid decl
The previous change 47b068d was partially reverted (when not allowing errors) as it output diagnostics for valid code. `Decl::isInvalid` had a special case for `AccessorDecl` where if its interface type hadn't already been computed, it would fallback to checking whether its storage (ie. its `VarDecl`) was valid. Since the `VarDecl` isn't fully deserialized when deserializing the `AccessorDecl`, this sometimes causes `isInvalid` to be true, even though it isn't actually invalid. The fallback introduced to `isInvalid` was to prevent a cycle during typechecking caused by `SimpleDidSetRequest` - during `didSet` typechecking, the body would be typechecked, which would then check whether the declaration is valid, calling the typechecking for the declaration again (and so on). This change removes the special case from `isInvalid`. The result of `isInvalid` is then correct for `AccessorDecl`, preventing spurious diagnostics. The `isInvalid` check in `performAbstractFuncDeclDiagnostics` has been removed to prevent the cycle there - it works perfectly fine without it. The unused variable diagnostics are skipped on seeing an error in the body anyway. The opaque type matching diagnostics make sense to keep when the return type is valid. Removing that check exposes another cycle when computing captures in body typechecking caused by `shouldMarkAsObjC` checking `isInvalid` as well. It already checks for `didSet`, so have moved that check above the `isInvalid` check. Resolves rdar://74541834
1 parent 44ed14e commit 2d9cdb6

File tree

5 files changed

+36
-30
lines changed

5 files changed

+36
-30
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,9 @@ NOTE(serialization_compatibility_version_mismatch,none,
776776
"(this is supported but may expose additional compiler issues)",
777777
(StringRef, StringRef, StringRef))
778778

779+
ERROR(serialization_invalid_decl,none,
780+
"deserialization of invalid declaration %0 from module '%1'",
781+
(DeclName, StringRef))
779782
ERROR(serialization_allowing_invalid_decl,none,
780783
"allowing deserialization of invalid declaration %0 from module '%1'",
781784
(DeclName, StringRef))

lib/AST/Decl.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -439,15 +439,9 @@ bool Decl::isInvalid() const {
439439
case DeclKind::Constructor:
440440
case DeclKind::Destructor:
441441
case DeclKind::Func:
442+
case DeclKind::Accessor:
442443
case DeclKind::EnumElement:
443444
return cast<ValueDecl>(this)->getInterfaceType()->hasError();
444-
445-
case DeclKind::Accessor: {
446-
auto *AD = cast<AccessorDecl>(this);
447-
if (AD->hasInterfaceType() && AD->getInterfaceType()->hasError())
448-
return true;
449-
return AD->getStorage()->isInvalid();
450-
}
451445
}
452446

453447
llvm_unreachable("Unknown decl kind");

lib/Sema/MiscDiagnostics.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3242,9 +3242,9 @@ performTopLevelDeclDiagnostics(TopLevelCodeDecl *TLCD) {
32423242
/// Perform diagnostics for func/init/deinit declarations.
32433243
void swift::performAbstractFuncDeclDiagnostics(AbstractFunctionDecl *AFD) {
32443244
// Don't produce these diagnostics for implicitly generated code.
3245-
if (AFD->getLoc().isInvalid() || AFD->isImplicit() || AFD->isInvalid())
3245+
if (AFD->getLoc().isInvalid() || AFD->isImplicit())
32463246
return;
3247-
3247+
32483248
// Check for unused variables, as well as variables that are could be
32493249
// declared as constants. Skip local functions though, since they will
32503250
// be checked as part of their parent function or TopLevelCodeDecl.

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,18 +1248,6 @@ Optional<ObjCReason> shouldMarkAsObjC(const ValueDecl *VD, bool allowImplicit) {
12481248

12491249
// Local function to determine whether we can implicitly infer @objc.
12501250
auto canInferImplicitObjC = [&](bool allowAnyAccess) {
1251-
if (VD->isInvalid())
1252-
return false;
1253-
if (VD->isOperator())
1254-
return false;
1255-
1256-
// Implicitly generated declarations are not @objc, except for constructors.
1257-
if (!allowImplicit && VD->isImplicit())
1258-
return false;
1259-
1260-
if (!allowAnyAccess && VD->getFormalAccess() <= AccessLevel::FilePrivate)
1261-
return false;
1262-
12631251
if (auto accessor = dyn_cast<AccessorDecl>(VD)) {
12641252
switch (accessor->getAccessorKind()) {
12651253
case AccessorKind::DidSet:
@@ -1275,6 +1263,19 @@ Optional<ObjCReason> shouldMarkAsObjC(const ValueDecl *VD, bool allowImplicit) {
12751263
break;
12761264
}
12771265
}
1266+
1267+
if (VD->isInvalid())
1268+
return false;
1269+
if (VD->isOperator())
1270+
return false;
1271+
1272+
// Implicitly generated declarations are not @objc, except for constructors.
1273+
if (!allowImplicit && VD->isImplicit())
1274+
return false;
1275+
1276+
if (!allowAnyAccess && VD->getFormalAccess() <= AccessLevel::FilePrivate)
1277+
return false;
1278+
12781279
return true;
12791280
};
12801281

lib/Serialization/Deserialization.cpp

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4085,15 +4085,23 @@ ModuleFile::getDeclChecked(
40854085
return deserialized;
40864086

40874087
auto *decl = declOrOffset.get();
4088-
if (isAllowModuleWithCompilerErrorsEnabled() && decl->isInvalid()) {
4089-
if (!isa<ParamDecl>(decl) && !decl->isImplicit()) {
4090-
// The parent function will be invalid if the parameter is invalid,
4091-
// implicits should have an invalid explicit as well
4092-
if (auto *VD = dyn_cast<ValueDecl>(decl)) {
4093-
getContext().Diags.diagnose(
4094-
VD->getLoc(), diag::serialization_allowing_invalid_decl,
4095-
VD->getName(), VD->getModuleContext()->getNameStr());
4096-
}
4088+
if (decl->isInvalid() && isa<ValueDecl>(decl)) {
4089+
auto *VD = cast<ValueDecl>(decl);
4090+
if (!isAllowModuleWithCompilerErrorsEnabled()) {
4091+
getContext().Diags.diagnose(
4092+
SourceLoc(), diag::serialization_invalid_decl,
4093+
VD->getName(), VD->getModuleContext()->getNameStr());
4094+
} else if (!isa<ParamDecl>(decl) && !decl->isImplicit()) {
4095+
// Grabbing the location of a parameter gets the location of its
4096+
// context, which isn't deserialized yet. Since the parent will be
4097+
// invalid if the parameter is, just skip it and wait for the
4098+
// parent to output the diagnostic.
4099+
//
4100+
// Any invalid implicit should have an invalid explicit, so ignore in
4101+
// preference for the explicit.
4102+
getContext().Diags.diagnose(
4103+
VD->getLoc(), diag::serialization_allowing_invalid_decl,
4104+
VD->getName(), VD->getModuleContext()->getNameStr());
40974105
}
40984106
}
40994107
} else if (matchAttributes) {

0 commit comments

Comments
 (0)