Skip to content

Fix effective context construction for NS_OPTIONS in linkage spec #62236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

NuriAmari
Copy link
Contributor

@NuriAmari NuriAmari commented Nov 23, 2022

This patch is motivated by more errors produced when finalizing the SwiftLookupTable with cxx-interop enabled. In particular, SwiftLookupTable::translateContextDecl only knows how to convert a small set of decls into a StoredContext. When cxx-interop is turned on, we get more LinkageSpecDecl nodes that ordinary.

If we ever created an EffectiveClangContext which holds reference to a LinkageSpecDecl we'll get mapping errors such those shown in the added test.

In this patch, we have modified the constructor of EffectiveClangContext to skip over any LinkageSpecDecls. We have noticed that determineEffectiveContext includes some of the same behavior. In particular, it dispatches to getRedeclContext which will skip so called "transparent" contexts, LinkageSpecDecls included:

DeclContext *DeclContext::getRedeclContext() {
  DeclContext *Ctx = this;

  // In C, a record type is the redeclaration context for its fields only. If
  // we arrive at a record context after skipping anything else, we should skip
  // the record as well. Currently, this means skipping enumerations because
  // they're the only transparent context that can exist within a struct or
  // union.
  bool SkipRecords = getDeclKind() == Decl::Kind::Enum &&
                     !getParentASTContext().getLangOpts().CPlusPlus;

  // Skip through contexts to get to the redeclaration context. Transparent
  // contexts are always skipped.
  while ((SkipRecords && Ctx->isRecord()) || Ctx->isTransparentContext())
    Ctx = Ctx->getParent();
  return Ctx;
}

This particular error, can also be fixed by calling determineEffectiveContext on produced contexts like so:

diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp
--- a/lib/ClangImporter/ImportName.cpp
+++ b/lib/ClangImporter/ImportName.cpp
@@ -1773,7 +1773,8 @@
         // It also means they may not have intended this API to be imported like this.
         if (importer::isUnavailableInSwift(typedefType->getDecl(), nullptr, true)) {
           result.setDeclName(swiftCtx.getIdentifier(typedefType->getDecl()->getName()));
-          result.setEffectiveContext(D->getDeclContext());
+          result.setEffectiveContext(
+              determineEffectiveContext(D, D->getDeclContext(), version));
           return result;
         }
       }

We could use some input on if this unwrapping should occur at all times, and the best place for it (determineEffectiveContext vs EffectiveClangContext::EffectiveClangContext) as the logic seems split between the two at the moment.

@NuriAmari
Copy link
Contributor Author

@swift-ci please test

@NuriAmari NuriAmari force-pushed the ns-option-linkage-spec-decl-fixes branch from ba232be to 79d7484 Compare November 25, 2022 18:34
@NuriAmari
Copy link
Contributor Author

@swift-ci please test

2 similar comments
@NuriAmari
Copy link
Contributor Author

@swift-ci please test

@NuriAmari
Copy link
Contributor Author

@swift-ci please test

@plotfi
Copy link
Contributor

plotfi commented Nov 28, 2022

Very nice work @NuriAmari. I think I overlooked the possibility of nesting LinkageSpecDecls when I previously did the fix for EffectiveClangContext. I suspect that this would be the case in UIKit because the UIKIT_EXTERN macro is used in code as well when the [extern_C] specifier is also set in the module.modulemap.

@NuriAmari
Copy link
Contributor Author

@swift-ci please test source compatibility

@NuriAmari NuriAmari added the c++ interop Feature: Interoperability with C++ label Nov 28, 2022
@NuriAmari NuriAmari marked this pull request as ready for review November 28, 2022 22:51
@NuriAmari NuriAmari requested review from CodaFi, beccadax and zoecarver and removed request for hyp, egorzhdan and zoecarver November 28, 2022 22:51
Copy link
Contributor

@zoecarver zoecarver left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test with multiple extern "C" blocks? (That seems to be why you added the while loop, right?)

@zoecarver
Copy link
Contributor

This LGTM though

@NuriAmari
Copy link
Contributor Author

Can you add a test with multiple extern "C" blocks? (That seems to be why you added the while loop, right?)

The existing test already has two nested, one explicit and one introduced by UIKIT_EXTERN.

@plotfi
Copy link
Contributor

plotfi commented Dec 2, 2022

Can you add a test with multiple extern "C" blocks? (That seems to be why you added the while loop, right?)

I think in practice the double extern C happens implicitly by having an extern C both in code and in the module specifier in the modulemap. I think for testing there should probably be a doubly nested extern C in code case and an extern C from module map plus an extern C from code case.

@NuriAmari
Copy link
Contributor Author

I think in practice the double extern C happens implicitly by having an extern C both in code and in the module specifier in the modulemap. I think for testing there should probably be a doubly nested extern C in code case and an extern C from module map plus an extern C from code case.

Alright, I'll add a test to cover the "extern C from module map" case.

When the ClangImporter imports a name, it associates it with a
an EffectiveClangContext. An EffectiveClangContext can be thought of
as the Clang scope the declaration will reside in, as far as importing
into Swift is concerned. This helps API notes and NS_SWIFT_NAME
to manipulate the SDK interface presented to Swift users.

When a entry is added to the Swift lookup table, it is associated
with a context. This context is a type and a name, used to effectively
namespace entries in the table. This context is derived from the
EffectiveClangContext associated with the name. This translation is
handled by SwiftLookupTable::translateContextDecl among other machinery.
This method in particular, understands only how to translate a set of
Clang nodes, and fails to create a context in other cases.

Prior to this patch, the EffectiveClangContext of declarations annotated
with UIKIT_EXTERN, with cxx-interop turned on, was a LinkageSpecDecl.
This results in context translation failure, and warnings produced in
SwiftLookupTable::finalizeLookupTable. This patch corrects name import
behavior to skip over the LinkageSpecDecl, and use the enclosing
TranslationUnit instead. This is appropriate and performed by
`determineEffectiveContext` as a LinkageSpecDecl is a so called
"transparent" context. That is its members are semantically declared and
accessible outside the context without additional qualification.

This patch tests using API notes, as that is the method UIKit uses. The
issue could just as easily be surface with a NS_SWIFT_NAME annotation.
Even without any annotation at all, the we would still fail to
translate, though there would likely be no corresponding warnings.
@NuriAmari NuriAmari force-pushed the ns-option-linkage-spec-decl-fixes branch from 48308b8 to 3a555bd Compare December 3, 2022 22:13
@NuriAmari
Copy link
Contributor Author

@swift-ci please test

@NuriAmari NuriAmari merged commit ce61927 into swiftlang:main Dec 4, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ interop Feature: Interoperability with C++
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants