[ClangImporter] Fix IUO ordering bug

The IUO-ness of imported declarations is not actually computed by IsImplicitlyUnwrappedOptionalRequest. Instead, ClangImporter manually sets the bit to `true` after the declaration’s type is imported and expects IsImplicitlyUnwrappedOptionalRequest to always set it to `false` for all other imported declarations.

Normally, declaration types are imported greedily as soon as the declaration is created. However, a ClangImporter refactoring in apple/swift#61026 deferred the import of a VarDecl’s type, and therefore the setting of its IUO bit, until the first time InterfaceTypeRequest is evaluated.

It turns out that there is nothing to guarantee that InterfaceTypeRequest will be evaluated before IsImplicitlyUnwrappedOptionalRequest, so if isImplicitlyUnwrappedOptional() was fetched before getInterfaceType() was called, it would return an incorrect result. The only known client that accesses the information in this order is the API digester, but in theory any part of the compiler could fall into this trap.

Force the evaluation of InterfaceTypeRequest during IsImplicitlyUnwrappedOptionalRequest when necessary to compute the IUO bit for an imported VarDecl, and add a test to prove that this fixes the observed bug in the API digester.
This commit is contained in:
Becca Royal-Gordon
2023-11-29 16:27:20 -08:00
parent a8c4323219
commit b699f1c7a3
4 changed files with 29 additions and 0 deletions

View File

@@ -1854,6 +1854,20 @@ IsImplicitlyUnwrappedOptionalRequest::evaluate(Evaluator &evaluator,
}
case DeclKind::Var:
if (decl->hasClangNode()) {
// ClangImporter does not use this request to compute whether imported
// declarations are IUOs; instead, it explicitly sets the bit itself when
// it imports the declaration's type. For most declarations this is done
// greedily, but for VarDecls, it is deferred until `getInterfaceType()`
// is called for the first time. (See apple/swift#61026.)
//
// Force the interface type, then see if a result for this request is now
// cached.
// FIXME: This is a little gross.
(void)decl->getInterfaceType();
if (auto cachedResult = this->getCachedResult())
return *cachedResult;
}
TyR = cast<VarDecl>(decl)->getTypeReprOrParentPatternTypeRepr();
break;

View File

@@ -0,0 +1 @@
void *ImportedIUOVar;

View File

@@ -0,0 +1,3 @@
module ImportedIUO {
header "imported_iuo.h"
}

View File

@@ -0,0 +1,11 @@
RUN: %empty-directory(%t)
RUN: %api-digester -dump-sdk -module ImportedIUO -o %t/digest.json -module-cache-path %t/module-cache %clang-importer-sdk-nosource -I %S/Inputs/ImportedIUO -avoid-location
RUN: %FileCheck --input-file %t/digest.json %s
CHECK: "name": "ImportedIUOVar",
CHECK-NEXT: "printedName": "ImportedIUOVar",
CHECK-NEXT: "children": [
CHECK-NEXT: {
CHECK-NEXT: "kind": "TypeNominal",
CHECK-NEXT: "name": "ImplicitlyUnwrappedOptional",
CHECK-NEXT: "printedName": "Swift.UnsafeMutableRawPointer!",