Skip to content

[cxx-interop] Allow Swift extensions to access non-public C++ members #77726

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

Closed
wants to merge 36 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b9712f1
Return empty discriminator for ClangModuleUnits
j-hui Nov 20, 2024
957d8e3
Add method to retrieve FileID from SourceFile
j-hui Nov 20, 2024
9696c4b
Allow C++ records to permit non-public access in certain files
j-hui Nov 20, 2024
2fb681e
Import non-public non-fields from C++
j-hui Nov 20, 2024
b1a506a
Add hasAttrs() check before iterating through attributes
j-hui Nov 20, 2024
3711c15
Import C++ functions according to their access level
j-hui Nov 20, 2024
0fb0366
Adjust import access levels across ClangImporter to reflect decl
j-hui Nov 20, 2024
6d942d2
Revert change to access level of nsErrorDecl
j-hui Nov 20, 2024
966d184
Revert change to access level of imported CFClassTypes
j-hui Nov 21, 2024
d2e2f1e
Fix convertClangAccess() for the AS_none case
j-hui Nov 21, 2024
d33028e
Skip inherited private fields
j-hui Nov 21, 2024
4c61111
Handle non-public, possibly nested inheritance
j-hui Nov 21, 2024
bf316a6
Remove seemingly stale comment
j-hui Nov 21, 2024
83ed35f
Add error message when user specifies too many IMPLEMENTATION_FILEIDs
j-hui Nov 21, 2024
1c91839
Format le code
j-hui Nov 21, 2024
1b62a43
Fix errors in access-specifiers test
j-hui Nov 21, 2024
fc124b1
Fix functions module interface test (by overwriting it)
j-hui Nov 21, 2024
4f108b9
Add access specifiers to function modules test
j-hui Nov 21, 2024
074784f
Fix using base members typechecker test
j-hui Nov 21, 2024
b3d726b
Fix up access permissions of accessors too
j-hui Nov 21, 2024
8f4487a
Fix member inline module interface test
j-hui Nov 21, 2024
fcff7e3
Format le code
j-hui Nov 21, 2024
a6014f6
Add annotation macro to the bridging header
j-hui Nov 21, 2024
2f15d6c
Improve SWIFT_PRIVATE_FILEID diagnostics
j-hui Nov 22, 2024
c731bc8
Add a test case that checks access controls
j-hui Nov 22, 2024
56087f8
Format non-public.h
j-hui Nov 22, 2024
3561e1a
Move 'access' tests under 'class' directory
j-hui Nov 22, 2024
fc34aa7
Add (nested) non-public inheritance test case
j-hui Nov 23, 2024
575a880
Import non-public members in ClangRecordMemberLookup too (wip)
j-hui Dec 2, 2024
e47f225
Fix misspecified test case
j-hui Dec 2, 2024
cbf1a84
Remove profound, thought-provoking remark
j-hui Dec 2, 2024
63efa33
Acknowledge Swift 'final' in comment
j-hui Dec 2, 2024
e60768d
Elaborate comment for SWIFT_PRIVATE_FILEID annotation
j-hui Dec 2, 2024
91a4dd6
When in Rome, emit diagnostics as the Romans do
j-hui Dec 2, 2024
ef6ab8f
Only importBaseMemberDecl on original ClangRecordMemberLookup
j-hui Dec 5, 2024
36a221e
Wip/abandoned work
j-hui Dec 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions include/swift/AST/ClangModuleLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,10 @@ class ClangModuleLoader : public ModuleLoader {

/// Imports a clang decl from a base class, cloning it for \param newContext
/// if it wasn't cloned for this specific context before.
virtual ValueDecl *importBaseMemberDecl(ValueDecl *decl,
DeclContext *newContext) = 0;
virtual ValueDecl *
importBaseMemberDecl(ValueDecl *decl,
DeclContext *newContext,
clang::AccessSpecifier inheritance) = 0;

/// Emits diagnostics for any declarations named name
/// whose direct declaration context is a TU.
Expand Down
15 changes: 15 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2778,6 +2778,10 @@ class ValueDecl : public Decl {

/// Whether we've evaluated the ApplyAccessNoteRequest.
unsigned accessNoteApplied : 1;

/// Whether this declaration represents an inherited C++ member that is
/// lazily cloned from a base class.
unsigned isLazilyInheritedClone : 1;
} LazySemanticInfo = { };

friend class DynamicallyReplacedDeclRequest;
Expand Down Expand Up @@ -3181,6 +3185,17 @@ class ValueDecl : public Decl {
LazySemanticInfo.isIUO = isIUO;
}

/// Returns true if this decl represents an inherited C++ member that is
/// lazily cloned from a base class.
bool isLazilyInheritedClone() const {
return LazySemanticInfo.isLazilyInheritedClone;
}

/// Marks this decl as representing an inherited C++ member.
void setLazilyInheritedClone(bool isLazilyInheritedClone) {
LazySemanticInfo.isLazilyInheritedClone = isLazilyInheritedClone;
}

/// Returns the protocol requirements that this decl conforms to.
ArrayRef<ValueDecl *>
getSatisfiedProtocolRequirements(bool Sorted = false) const;
Expand Down
17 changes: 17 additions & 0 deletions include/swift/AST/DiagnosticsClangImporter.def
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,23 @@ NOTE(iterator_method_unavailable, none, "C++ method '%0' that returns an "
NOTE(iterator_potentially_unsafe, none, "C++ methods that return iterators "
"are potentially unsafe; try using Swift collection APIs instead", ())

ERROR(private_fileid_attr_repeated, none,
"multiple SWIFT_PRIVATE_FILEID annotations were found on '%0'",
(StringRef))
ERROR(private_fileid_attr_on_incomplete_type, none,
"SWIFT_PRIVATE_FILEID cannot be applied to incomplete type, '%0'",
(StringRef))
NOTE(private_fileid_attr_here, none,
"SWIFT_PRIVATE_FILEID annotation found here", ())

WARNING(private_fileid_attr_format_invalid, none,
"SWIFT_PRIVATE_FILEID annotation on '%0' does not have a valid file ID",
(StringRef))
REMARK(private_fileid_attr_format_specification, none,
"file IDs have the following format: 'ModuleName/FileName.swift'", ())
NOTE(private_fileid_attr_format_suggestion, none,
"did you mean '%0'?", (StringRef))

ERROR(reference_type_must_have_retain_release_attr, none,
"reference type '%1' must have %select{'retain:'|'release:'}0 Swift "
"attribute",
Expand Down
8 changes: 8 additions & 0 deletions include/swift/AST/SourceFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,14 @@ class SourceFile final : public FileUnit {
Scope = nullptr;
}

/// Whether the given string matches the file ID of this file.
bool matchesFileID(StringRef fileID) const;

/// Parses a file ID string into the module name and file name (with ".swift"
/// suffix). Returns nullopt if the file ID string is invalid.
static std::optional<std::pair<StringRef, StringRef>>
parseFileID(StringRef fileID);

/// Retrieves the previously set delayed parser state, asserting that it
/// exists.
PersistentParserState *getDelayedParserState() {
Expand Down
18 changes: 17 additions & 1 deletion include/swift/ClangImporter/ClangImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
#ifndef SWIFT_CLANG_IMPORTER_H
#define SWIFT_CLANG_IMPORTER_H

#include "swift/AST/AttrKind.h"
#include "swift/AST/ClangModuleLoader.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/Support/VirtualFileSystem.h"

/// The maximum number of SIMD vector elements we currently try to import.
Expand Down Expand Up @@ -656,7 +658,8 @@ class ClangImporter final : public ClangModuleLoader {
Decl *importDeclDirectly(const clang::NamedDecl *decl) override;

ValueDecl *importBaseMemberDecl(ValueDecl *decl,
DeclContext *newContext) override;
DeclContext *newContext,
clang::AccessSpecifier inheritance) override;

/// Emits diagnostics for any declarations named name
/// whose direct declaration context is a TU.
Expand Down Expand Up @@ -729,6 +732,19 @@ ValueDecl *getImportedMemberOperator(const DeclBaseName &name,
NominalTypeDecl *selfType,
std::optional<Type> parameterType);

/// Read file IDs from 'private_fileid' Swift attributes on a Clang decl.
///
/// May return >1 fileID when a decl is annotated more than once, which should
/// be treated as an error and appropriately diagnosed (using the included
/// SourceLocation).
SmallVector<std::pair<StringRef, clang::SourceLocation>, 1>
getPrivateFileIDAttrs(const clang::Decl *decl);

/// Map the access specifier of a Clang record member to a Swift access level.
///
/// This mapping is conservative: the resulting Swift access should be at _most_
/// as permissive as the input C++ access.
AccessLevel convertClangAccess(clang::AccessSpecifier access);
} // namespace importer

struct ClangInvocationFileMapping {
Expand Down
30 changes: 24 additions & 6 deletions include/swift/ClangImporter/ClangImporterRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "swift/AST/SimpleRequest.h"
#include "swift/Basic/Statistic.h"
#include "clang/AST/Type.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/TinyPtrVector.h"

Expand Down Expand Up @@ -125,29 +126,46 @@ class CXXNamespaceMemberLookup
private:
friend SimpleRequest;

// Evaluation.
TinyPtrVector<ValueDecl *>
evaluate(Evaluator &evaluator, CXXNamespaceMemberLookupDescriptor desc) const;
};

/// The input type for a record member lookup request.
///
/// These lookups may be requested recursively in the case of inheritance,
/// for which we separately keep track of the derived class where we started
/// looking (startDecl) and the access level for the current inheritance.
struct ClangRecordMemberLookupDescriptor final {
NominalTypeDecl *recordDecl;
DeclName name;
NominalTypeDecl *recordDecl; // Where we are currently looking
NominalTypeDecl *startDecl; // Where we started looking
DeclName name; // What we are looking for
clang::AccessSpecifier inheritance; // Public, protected, or private inheritance
// (clang::AS_none means no inheritance)

ClangRecordMemberLookupDescriptor(NominalTypeDecl *recordDecl, DeclName name)
: recordDecl(recordDecl), name(name) {
: recordDecl(recordDecl), startDecl(recordDecl), name(name), inheritance(clang::AS_none) {
assert(isa<clang::RecordDecl>(recordDecl->getClangDecl()));
}

ClangRecordMemberLookupDescriptor(NominalTypeDecl *recordDecl, DeclName name,
clang::AccessSpecifier inheritance, NominalTypeDecl *startDecl)
: recordDecl(recordDecl), startDecl(startDecl), name(name), inheritance(inheritance) {
assert(isa<clang::RecordDecl>(recordDecl->getClangDecl()));
assert(isa<clang::RecordDecl>(startDecl->getClangDecl()));
assert(inheritance != clang::AS_none && "recursive member lookup should use non-none inheritance");
}

public:
friend class ClangRecordMemberLookup;

friend llvm::hash_code
hash_value(const ClangRecordMemberLookupDescriptor &desc) {
return llvm::hash_combine(desc.name, desc.recordDecl);
return llvm::hash_combine(desc.name, desc.recordDecl, desc.inheritance);
}

friend bool operator==(const ClangRecordMemberLookupDescriptor &lhs,
const ClangRecordMemberLookupDescriptor &rhs) {
return lhs.name == rhs.name && lhs.recordDecl == rhs.recordDecl;
return lhs.name == rhs.name && lhs.recordDecl == rhs.recordDecl && lhs.inheritance == rhs.inheritance;
}

friend bool operator!=(const ClangRecordMemberLookupDescriptor &lhs,
Expand Down
8 changes: 7 additions & 1 deletion lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "swift/Basic/Defer.h"
#include "swift/Basic/SourceManager.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/Demangling/Demangler.h"
#include "swift/Demangling/ManglingMacros.h"
#include "swift/Demangling/ManglingUtils.h"
Expand All @@ -53,8 +54,8 @@
#include "clang/AST/Mangle.h"
#include "clang/Basic/CharInfo.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
Expand Down Expand Up @@ -1016,6 +1017,11 @@ static StringRef getPrivateDiscriminatorIfNecessary(const Decl *decl) {
auto topLevelSubcontext = decl->getDeclContext()->getModuleScopeContext();
auto fileUnit = cast<FileUnit>(topLevelSubcontext);

// Clang modules do not provide a namespace, so no discriminator is needed
// here, even for non-public declarations.
if (isa<ClangModuleUnit>(fileUnit))
return StringRef();

Identifier discriminator =
fileUnit->getDiscriminatorForPrivateDecl(decl);
assert(!discriminator.empty());
Expand Down
29 changes: 21 additions & 8 deletions lib/AST/DeclContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
//===----------------------------------------------------------------------===//

#include "swift/AST/DeclContext.h"
#include "swift/AST/AccessScope.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/AccessScope.h"
#include "swift/AST/ClangModuleLoader.h"
#include "swift/AST/Expr.h"
#include "swift/AST/FileUnit.h"
Expand All @@ -23,16 +23,17 @@
#include "swift/AST/Module.h"
#include "swift/AST/ParseRequests.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/Types.h"
#include "swift/AST/TypeCheckRequests.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Basic/Statistic.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "clang/AST/ASTContext.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/SaveAndRestore.h"
#include "clang/AST/ASTContext.h"
#include "llvm/Support/raw_ostream.h"
using namespace swift;

#define DEBUG_TYPE "Name lookup"
Expand Down Expand Up @@ -1298,16 +1299,28 @@ bool AccessScope::allowsPrivateAccess(const DeclContext *useDC, const DeclContex
return usePkg->isSamePackageAs(srcPkg);
}
}
// Do not allow access if the sourceDC is in a different file
auto useSF = useDC->getOutermostParentSourceFile();
if (useSF != sourceDC->getOutermostParentSourceFile())
return false;

// Do not allow access if the sourceDC does not represent a type.
auto sourceNTD = sourceDC->getSelfNominalTypeDecl();
if (!sourceNTD)
return false;

// Do not allow access if the sourceDC is in a different file
auto useSF = useDC->getOutermostParentSourceFile();
if (useSF != sourceDC->getOutermostParentSourceFile()) {
// This might be a C++ declaration with a SWIFT_IMPLEMENTATION_FILEID
// attribute, which asks us to treat it as if it were defined in the file
// with the specified FileID.
bool blessedUseSF = false;
if (auto clangDecl = sourceNTD->getDecl()->getClangDecl()) {
auto blessedFileID = importer::getPrivateFileIDAttrs(clangDecl);
if (!blessedFileID.empty())
blessedUseSF = useSF->matchesFileID(blessedFileID[0].first);
}
if (!blessedUseSF)
return false;
}

// Compare the private scopes and iterate over the parent types.
sourceDC = getPrivateDeclContext(sourceDC, useSF);
while (!useDC->isModuleContext()) {
Expand Down
26 changes: 26 additions & 0 deletions lib/AST/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3614,6 +3614,32 @@ ASTScope &SourceFile::getScope() {
return *Scope.get();
}

bool SourceFile::matchesFileID(StringRef fileID) const {
// Never match with SourceFiles that do not correpond to a file on disk
if (getFilename().empty())
return false;

auto parsed = parseFileID(fileID);
if (!parsed)
return false;

auto moduleName = parsed->first;
auto fileName = parsed->second;

return moduleName == getParentModule()->getNameStr() &&
fileName == llvm::sys::path::filename(getFilename());
}

std::optional<std::pair<StringRef, StringRef>>
SourceFile::parseFileID(StringRef fileID) {
auto names = fileID.split('/');
auto moduleName = names.first;
auto fileName = names.second;
if (moduleName.empty() || fileName.empty() || !fileName.ends_with(".swift"))
return {};
return {{moduleName, fileName}};
}

Identifier SourceFile::getPrivateDiscriminator(bool createIfMissing) const {
if (!PrivateDiscriminator.empty() || !createIfMissing)
return PrivateDiscriminator;
Expand Down
Loading