Skip to content

[DO NOT MERGE] Test #79348 on Windows #79534

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 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0f8b97a
[cxx-interop] Import non-public inherited members
j-hui Feb 1, 2025
1f853c0
Add tests for importing non-public inherited members
j-hui Feb 2, 2025
5438249
Check for null pointers
j-hui Feb 7, 2025
18ab1b4
Fix some test cases
j-hui Feb 7, 2025
8eec909
Handle inaccessible private members
j-hui Feb 11, 2025
9c39093
Formatting
j-hui Feb 11, 2025
a5ac168
Merge remote-tracking branch 'origin/main' into import-inherited-members
j-hui Feb 11, 2025
6a9a291
Add hash_value() to ClangInheritanceInfo
j-hui Feb 11, 2025
917bdd1
Import inaccessible private base class members but make them unavailable
j-hui Feb 12, 2025
17d2be9
Adjust module interface test case for using base members to acccount …
j-hui Feb 14, 2025
3ca3fde
Correct silly typo in header guard
j-hui Feb 14, 2025
8652f1b
Add test case for shadowing of non-public members
j-hui Feb 14, 2025
6bb3409
Formatting
j-hui Feb 14, 2025
606cc1d
Address review feedback
j-hui Feb 15, 2025
fccb266
Get rid of confusing and error-prone ClangInheritanceInfo::forUsingDe…
j-hui Feb 17, 2025
742aad9
Merge remote-tracking branch 'origin/main' into import-inherited-members
j-hui Feb 18, 2025
e18734a
Add 'using' tests with more emphasis on access levels and inheritance
j-hui Feb 20, 2025
0d2fac5
Add protected inheritance test case to using-base-members
j-hui Feb 20, 2025
ee61104
Add some test cases to using-base-members test case
j-hui Feb 21, 2025
9ae6bde
REVERTME: debug prints for windows test
j-hui Feb 21, 2025
584bf46
REVERTME: disable every single test other than use-std-optional.swift…
j-hui Feb 21, 2025
6f7169c
REVERTME: adjust print debug statements
j-hui Feb 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
14 changes: 11 additions & 3 deletions include/swift/AST/ClangModuleLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class SourceLocation;

namespace swift {

class ClangInheritanceInfo;
class ConcreteDeclRef;
class Decl;
class FuncDecl;
Expand Down Expand Up @@ -202,10 +203,17 @@ class ClangModuleLoader : public ModuleLoader {
/// Imports a clang decl directly, rather than looking up its name.
virtual Decl *importDeclDirectly(const clang::NamedDecl *decl) = 0;

/// Imports a clang decl from a base class, cloning it for \param newContext
/// if it wasn't cloned for this specific context before.
/// Clones an imported \param decl from its base class to its derived class
/// \param newContext where it is inherited. Its access level is determined
/// with respect to \param inheritance, which signifies whether \param decl
/// was inherited via C++ public/protected/private inheritance.
///
/// This function uses a cache so that it is idempotent; successive
/// invocations will only generate one cloned ValueDecl (and all return
/// a pointer to it). Returns a NULL pointer upon failure.
virtual ValueDecl *importBaseMemberDecl(ValueDecl *decl,
DeclContext *newContext) = 0;
DeclContext *newContext,
ClangInheritanceInfo inheritance) = 0;

/// Emits diagnostics for any declarations named name
/// whose direct declaration context is a TU.
Expand Down
117 changes: 115 additions & 2 deletions include/swift/ClangImporter/ClangImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#ifndef SWIFT_CLANG_IMPORTER_H
#define SWIFT_CLANG_IMPORTER_H

#include "swift/AST/Attr.h"
#include "swift/AST/AttrKind.h"
#include "swift/AST/ClangModuleLoader.h"
#include "clang/Basic/Specifiers.h"
Expand Down Expand Up @@ -72,6 +73,7 @@ namespace swift {
class ASTContext;
class CompilerInvocation;
class ClangImporterOptions;
class ClangInheritanceInfo;
class ClangModuleUnit;
class ClangNode;
class ConcreteDeclRef;
Expand Down Expand Up @@ -660,8 +662,8 @@ class ClangImporter final : public ClangModuleLoader {
/// Imports a clang decl directly, rather than looking up it's name.
Decl *importDeclDirectly(const clang::NamedDecl *decl) override;

ValueDecl *importBaseMemberDecl(ValueDecl *decl,
DeclContext *newContext) override;
ValueDecl *importBaseMemberDecl(ValueDecl *decl, DeclContext *newContext,
ClangInheritanceInfo inheritance) override;

/// Emits diagnostics for any declarations named name
/// whose direct declaration context is a TU.
Expand Down Expand Up @@ -779,6 +781,117 @@ ClangInvocationFileMapping getClangInvocationFileMapping(
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs = nullptr,
bool suppressDiagnostic = false);

/// Information used to compute the access level of inherited C++ members.
class ClangInheritanceInfo {
/// The cumulative inheritance access specifier, that is used to compute the
/// effective access level of a particular inherited member.
///
/// When constructing ClangInheritanceInfo for nested inheritance, this field
/// gets clamped to the least permissive level between its current value and
/// the inheritance access specifier.
///
/// See ClangInheritanceInfo::cumulativeInheritedAccess() for an example.
clang::AccessSpecifier access;

/// This flag indicates that we encountered private inheritance beyond the
/// direct base class when crawling up the class hierarchy, meaning all of the
/// inherited members from further up the hierarchy should be treated as if
/// they were declared private and marked unavailable.
///
/// See ClangInheritanceInfo::cumulativeNestedPrivate() for an example.
bool nestedPrivate;

public:
/// Default constructor for this class that is used as the base case when
/// recursively walking up a class inheritance hierarchy.
ClangInheritanceInfo() : access(clang::AS_none), nestedPrivate(false) {}

/// Inductive case for this class that is used to accumulate inheritance
/// metadata for cases of (nested) inheritance.
ClangInheritanceInfo(ClangInheritanceInfo prev, clang::CXXBaseSpecifier base)
: access(cumulativeInheritedAccess(prev, base)),
nestedPrivate(cumulativeNestedPrivate(prev, base)) {}

/// Whether this is info represents a case of C++ inheritance.
///
/// Returns \c false for the default instance of this class.
bool isInheriting() const { return access != clang::AS_none; }

/// Whether this is info represents a case of C++ inheritance.
operator bool() const { return isInheriting(); }

/// Compute the (Swift) access level for inherited base member \param decl,
/// for when its inherited (cloned) member in the derived class.
///
/// This access level is determined by whichever is more restrictive: what the
/// \param decl was declared with (in its base class), or what it is being
/// inherited with (ClangInheritanceInfo::access).
///
/// Always returns swift::AccessLevel::Public (i.e., corresponding to
/// clang::AS_none) if this ClangInheritanceInfo::isInheriting() is \c false.
AccessLevel accessForBaseDecl(const ValueDecl *baseDecl) const;

/// Marks \param clonedDecl as unavailable (using \c @available) if it
/// cannot be accessed from the derived class, either because \param baseDecl
/// was declared as private in the base class, or because \param clonedDecl
/// was inherited with private inheritance.
///
/// Does nothing if this ClangInheritanceInfo::isInheriting() is \c false.
void setUnavailableIfNecessary(const ValueDecl *baseDecl,
ValueDecl *clonedDecl) const;

friend llvm::hash_code hash_value(const ClangInheritanceInfo &info) {
return llvm::hash_combine(info.access, info.nestedPrivate);
}

private:
/// An example of how ClangInheritanceInfo:iaccess is accumulated while
/// recursively traversing the class hierarchy starting from \c D:
///
/// \code{.cpp}
/// struct A { ... }; // access = private
/// struct B : public A { ... }; // access = private
/// struct C : private B { ... }; // access = protected
/// struct D : protected C { ... }; // access = none [base case]
/// \endcode
static clang::AccessSpecifier
cumulativeInheritedAccess(ClangInheritanceInfo prev,
clang::CXXBaseSpecifier base) {
assert(base.getAccessSpecifier() != clang::AS_none &&
"this should always be public, protected, or private");
static_assert(clang::AS_private > clang::AS_protected &&
clang::AS_protected > clang::AS_public &&
"using std::max() relies on this ordering");
if (prev)
return std::max(prev.access, base.getAccessSpecifier());

return base.getAccessSpecifier();
}

/// An example of how ClangInheritanceInfo::nestedPrivate is accumulated while
/// recursively traversing the class hierarchy starting from \c D:
///
/// \code{.cpp}
/// struct A { ... }; // nestedPrivate = true
/// struct B : public A { ... }; // nestedPrivate = true
/// struct C : private B { ... }; // nestedPrivate = false
/// struct D : private C { ... }; // nestedPrivate = false [base case]
/// \endcode
static bool cumulativeNestedPrivate(ClangInheritanceInfo prev,
clang::CXXBaseSpecifier base) {
if (prev.nestedPrivate)
// Encountered private inheritance before, so that should be propagated
return true;

if (prev.isInheriting())
// This is a case of nested inheritance; return true if it is private
return base.getAccessSpecifier() == clang::AS_private;

// This is the first level of inheritance
return false;
}
};

} // end namespace swift

#endif
29 changes: 21 additions & 8 deletions include/swift/ClangImporter/ClangImporterRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "swift/Basic/Statistic.h"
#include "swift/ClangImporter/ClangImporter.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 @@ -133,25 +134,33 @@ class CXXNamespaceMemberLookup
};

/// 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;
NominalTypeDecl *inheritingDecl;
DeclName name;
NominalTypeDecl *recordDecl; // Where we are currently looking
NominalTypeDecl *inheritingDecl; // Where we started looking from
DeclName name; // What we are looking for
ClangInheritanceInfo inheritance;

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

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

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

friend bool operator!=(const ClangRecordMemberLookupDescriptor &lhs,
Expand All @@ -165,10 +174,14 @@ struct ClangRecordMemberLookupDescriptor final {
// This private constructor should only be used in ClangRecordMemberLookup,
// for recursively traversing base classes that inheritingDecl inherites from.
ClangRecordMemberLookupDescriptor(NominalTypeDecl *recordDecl, DeclName name,
NominalTypeDecl *inheritingDecl)
: recordDecl(recordDecl), inheritingDecl(inheritingDecl), name(name) {
NominalTypeDecl *inheritingDecl,
ClangInheritanceInfo inheritance)
: recordDecl(recordDecl), inheritingDecl(inheritingDecl), name(name),
inheritance(inheritance) {
assert(isa<clang::RecordDecl>(recordDecl->getClangDecl()));
assert(isa<clang::CXXRecordDecl>(inheritingDecl->getClangDecl()));
assert(inheritance.isInheriting() &&
"recursive calls should indicate inheritance");
assert(recordDecl != inheritingDecl &&
"recursive calls should lookup elsewhere");
}
Expand Down
25 changes: 21 additions & 4 deletions lib/ClangImporter/ClangDerivedConformances.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,23 +670,40 @@ void swift::conformToCxxOptionalIfNeeded(
assert(clangDecl);
ASTContext &ctx = decl->getASTContext();

if (!isStdDecl(clangDecl, {"optional"}))
llvm::errs() << "{{{\n";
llvm::errs() << __FUNCTION__ << ":" << __LINE__ << ": trying to conform " << decl->getNameStr() << " to CxxOptional\n";
if (!isStdDecl(clangDecl, {"optional"})) {
llvm::errs() << __FUNCTION__ << ":" << __LINE__ << ": !isStdDecl()\n";
llvm::errs() << "}}}\n\n";
return;
}
llvm::errs() << __FUNCTION__ << ":" << __LINE__ << ": isStdDecl(optional) (we're in the running)\n";
decl->dump(llvm::errs());
clangDecl->dump(llvm::errs());

ProtocolDecl *cxxOptionalProto =
ctx.getProtocol(KnownProtocolKind::CxxOptional);
// If the Cxx module is missing, or does not include one of the necessary
// protocol, bail.
if (!cxxOptionalProto)
if (!cxxOptionalProto) {
llvm::errs() << __FUNCTION__ << ":" << __LINE__ << ": !cxxOptionalProto\n";
llvm::errs() << "}}}\n\n";
return;
}

auto pointeeId = ctx.getIdentifier("pointee");
auto pointees = lookupDirectWithoutExtensions(decl, pointeeId);
if (pointees.size() != 1)
if (pointees.size() != 1) {
llvm::errs() << __FUNCTION__ << ":" << __LINE__ << ": point.size() != 1 (==" << pointees.size() << ") \n";
llvm::errs() << "}}}\n\n";
return;
}
auto pointee = dyn_cast<VarDecl>(pointees.front());
if (!pointee)
if (!pointee) {
llvm::errs() << __FUNCTION__ << ":" << __LINE__ << ": !pointee\n";
llvm::errs() << "}}}\n\n";
return;
}
auto pointeeTy = pointee->getInterfaceType();

impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Wrapped"), pointeeTy);
Expand Down
Loading