mirror of
https://github.com/apple/swift.git
synced 2026-02-27 18:26:24 +01:00
[cxx-interop] Synthesize .pointee and .successor() on-demand
.pointee is a computed variable whose getter and setter are synthesized from operator*() (dereference). .successor() is a method that is synthesized from operator++() (prefix increment). They are currently created at the end of importing each StructDecl. That relies on importing operators eagerly, which can lead to template over-instantiation issues, and is also extra work that we should try to avoid. Also, .pointee picks getters and setters based on the last-seen operator*() overload, which is an unintuitive programming model and also especially error-prone when matters of inheritance are involved. We should teach ClangImporter to look up and synthesize these members on-demand, rather than relying on synthesizing these eagerly. rdar://167360692
This commit is contained in:
@@ -959,6 +959,9 @@ public:
|
||||
ClangInheritanceInfo(ClangInheritanceInfo prev, clang::CXXBaseSpecifier base)
|
||||
: access(computeAccess(prev, base)) {}
|
||||
|
||||
/// Initialize an instance of this class at a particular given access level.
|
||||
ClangInheritanceInfo(clang::AccessSpecifier access) : access(access) {}
|
||||
|
||||
/// Whether this info represents a case of nested private inheritance.
|
||||
bool isNestedPrivate() const { return !access.has_value(); }
|
||||
|
||||
|
||||
@@ -545,10 +545,9 @@ conformToCxxIteratorIfNeeded(ClangImporter::Implementation &impl,
|
||||
}
|
||||
}
|
||||
|
||||
// Check if present: `var pointee: Pointee { get }`
|
||||
auto pointeeId = ctx.getIdentifier("pointee");
|
||||
auto pointee = lookupDirectSingleWithoutExtensions<VarDecl>(decl, pointeeId);
|
||||
if (!pointee || pointee->isGetterMutating() || pointee->getTypeInContext()->hasError())
|
||||
auto *pointee = impl.lookupAndImportPointee(decl);
|
||||
if (!pointee || pointee->isGetterMutating() ||
|
||||
pointee->getTypeInContext()->hasError())
|
||||
return;
|
||||
|
||||
// Check if `var pointee: Pointee` is settable. This is required for the
|
||||
@@ -556,10 +555,7 @@ conformToCxxIteratorIfNeeded(ClangImporter::Implementation &impl,
|
||||
// UnsafeCxxInputIterator.
|
||||
bool pointeeSettable = pointee->isSettable(nullptr);
|
||||
|
||||
// Check if present: `func successor() -> Self`
|
||||
auto successorId = ctx.getIdentifier("successor");
|
||||
auto successor =
|
||||
lookupDirectSingleWithoutExtensions<FuncDecl>(decl, successorId);
|
||||
auto *successor = impl.lookupAndImportSuccessor(decl);
|
||||
if (!successor || successor->isMutating())
|
||||
return;
|
||||
auto successorTy = successor->getResultInterfaceType();
|
||||
@@ -716,6 +712,9 @@ static void conformToCxxOptional(ClangImporter::Implementation &impl,
|
||||
if (!Wrapped)
|
||||
return;
|
||||
|
||||
if (!impl.lookupAndImportPointee(decl))
|
||||
return;
|
||||
|
||||
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Wrapped"),
|
||||
Wrapped->getUnderlyingType());
|
||||
impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxOptional});
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ImporterImpl.h"
|
||||
#include "SwiftDeclSynthesizer.h"
|
||||
#include "swift/AST/ASTContext.h"
|
||||
#include "swift/AST/Builtins.h"
|
||||
#include "swift/AST/ClangModuleLoader.h"
|
||||
@@ -32,22 +33,30 @@
|
||||
#include "swift/AST/Type.h"
|
||||
#include "swift/AST/TypeCheckRequests.h"
|
||||
#include "swift/AST/Types.h"
|
||||
#include "swift/Basic/Defer.h"
|
||||
#include "swift/Basic/LLVM.h"
|
||||
#include "swift/ClangImporter/ClangImporter.h"
|
||||
#include "swift/ClangImporter/ClangImporterRequests.h"
|
||||
#include "swift/ClangImporter/ClangModule.h"
|
||||
#include "swift/Subsystems.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/CXXInheritance.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclBase.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclTemplate.h"
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "clang/AST/Mangle.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/Basic/Specifiers.h"
|
||||
#include "clang/Index/IndexingAction.h"
|
||||
#include "clang/Sema/Lookup.h"
|
||||
#include "clang/Sema/Sema.h"
|
||||
#include "clang/Serialization/ASTReader.h"
|
||||
#include "clang/Serialization/ASTWriter.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/iterator_range.h"
|
||||
#include <utility>
|
||||
|
||||
using namespace swift;
|
||||
@@ -224,6 +233,7 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
|
||||
ClangInheritanceInfo inheritance = desc.inheritance;
|
||||
|
||||
auto &ctx = recordDecl->getASTContext();
|
||||
auto &Importer = *static_cast<ClangImporter *>(ctx.getClangModuleLoader());
|
||||
|
||||
// Whether to skip non-public members. Feature::ImportNonPublicCxxMembers says
|
||||
// to import all non-public members by default; if that is disabled, we only
|
||||
@@ -370,5 +380,272 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
|
||||
}
|
||||
}
|
||||
|
||||
if (result.empty() && !inheritance) {
|
||||
if (name.isSimpleName("pointee")) {
|
||||
if (auto *pointee = Importer.Impl.lookupAndImportPointee(inheritingDecl))
|
||||
result.push_back(pointee);
|
||||
} else if (name.isSimpleName("successor")) {
|
||||
if (auto *succ = Importer.Impl.lookupAndImportSuccessor(inheritingDecl))
|
||||
result.push_back(succ);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Perform a qualified lookup for \a name in \a Record, suppressing diagnostics
|
||||
/// and returning std::nullopt if the lookup was unsuccessful.
|
||||
static std::optional<clang::LookupResult>
|
||||
lookupCXXMember(clang::Sema &Sema, clang::DeclarationName name,
|
||||
const clang::CXXRecordDecl *Record) {
|
||||
auto R = clang::LookupResult(Sema, name, clang::SourceLocation(),
|
||||
clang::Sema::LookupMemberName);
|
||||
R.suppressDiagnostics();
|
||||
// NOTE: this will not find free-standing operator decl, i.e. not a member
|
||||
Sema.LookupQualifiedName(R, const_cast<clang::CXXRecordDecl *>(Record));
|
||||
|
||||
switch (R.getResultKind()) {
|
||||
case clang::LookupResultKind::Found:
|
||||
case clang::LookupResultKind::FoundOverloaded:
|
||||
return R;
|
||||
default:
|
||||
return std::nullopt; // Lookup didn't yield any results
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
/// Convenience wrapper around clang::LookupResult::iterator that yields
|
||||
/// std::pair<NamedDecl *, AccessSpecifier> rather than just NamedDecl *.
|
||||
struct ResultIterator
|
||||
: llvm::iterator_adaptor_base<
|
||||
ResultIterator, clang::LookupResult::iterator,
|
||||
clang::LookupResult::iterator::iterator_category,
|
||||
std::pair<const clang::NamedDecl *, clang::AccessSpecifier>> {
|
||||
explicit ResultIterator(const clang::LookupResult::iterator &it) { I = it; }
|
||||
value_type operator*() const {
|
||||
return std::make_pair(I.getDecl(), I.getAccess());
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/// Creates an iterable range of method decl-access pairs for the results found
|
||||
/// in \a R, originating from a qualified lookup in \a Origin.
|
||||
///
|
||||
/// This helper for the lookupAndImport* functions encapsulates casting the
|
||||
/// decls to clang::CXXMethodDecl *s and "looking through" using decls. It also
|
||||
/// ensures the access is set to clang::AS_none if the method is not inherited.
|
||||
static auto filterMethodOverloads(clang::LookupResult &R,
|
||||
const clang::CXXRecordDecl *Origin) {
|
||||
return llvm::make_filter_range(
|
||||
llvm::map_range(
|
||||
llvm::make_range(ResultIterator{R.begin()},
|
||||
ResultIterator{R.end()}),
|
||||
[=](std::pair<const clang::NamedDecl *, clang::AccessSpecifier> da) {
|
||||
auto [nd, access] = da;
|
||||
|
||||
if (auto *usd = dyn_cast<clang::UsingShadowDecl>(nd))
|
||||
nd = usd->getTargetDecl();
|
||||
|
||||
if (nd->getDeclContext() == Origin)
|
||||
access = clang::AS_none; // not inherited
|
||||
|
||||
auto *md = dyn_cast<clang::CXXMethodDecl>(nd);
|
||||
return std::make_pair(md, access);
|
||||
}),
|
||||
[](std::pair<const clang::CXXMethodDecl *, clang::AccessSpecifier> da) {
|
||||
return da.first;
|
||||
});
|
||||
}
|
||||
|
||||
/// Imports a C++ \a method to a Swift \a struct as a non-inherited member when
|
||||
/// \a access is AS_none, or an inherited member with effective \a access
|
||||
/// otherwise. Marks the method as unavailable with \a unavailabilityMsg.
|
||||
///
|
||||
/// Helper for the lookupAndImport* functions.
|
||||
static FuncDecl *importUnavailableMethod(ClangImporter::Implementation &Impl,
|
||||
const clang::CXXMethodDecl *method,
|
||||
clang::AccessSpecifier access,
|
||||
NominalTypeDecl *Struct,
|
||||
StringRef unavailabilityMsg) {
|
||||
auto *func =
|
||||
dyn_cast_or_null<FuncDecl>(Impl.importDecl(method, Impl.CurrentVersion));
|
||||
if (!func)
|
||||
return nullptr;
|
||||
if (auto inheritance = ClangInheritanceInfo(access))
|
||||
func = dyn_cast_or_null<FuncDecl>(
|
||||
Impl.importBaseMemberDecl(func, Struct, inheritance));
|
||||
if (!func)
|
||||
return nullptr;
|
||||
Impl.markUnavailable(func, unavailabilityMsg);
|
||||
return func;
|
||||
}
|
||||
|
||||
VarDecl *
|
||||
ClangImporter::Implementation::lookupAndImportPointee(NominalTypeDecl *Struct) {
|
||||
const auto *CXXRecord =
|
||||
dyn_cast<clang::CXXRecordDecl>(Struct->getClangDecl());
|
||||
|
||||
if (!CXXRecord || !isa<StructDecl>(Struct))
|
||||
// Do not synthesize successor() if this is not a C++ record, or if it is
|
||||
// a foreign reference type (successor() needs to copy values of this type),
|
||||
// which would be a ClassDecl rather than a StructDecl.
|
||||
return nullptr;
|
||||
|
||||
if (auto [it, inserted] = importedPointeeCache.try_emplace(Struct, nullptr);
|
||||
!inserted)
|
||||
return it->second;
|
||||
|
||||
// From this point onward, if we encounter some error and return, future calls
|
||||
// to this function will pick up the nullptr we cached using try_emplace. Note
|
||||
// that this may silently suppress unintentional cycles.
|
||||
|
||||
auto &Ctx = getClangASTContext();
|
||||
auto &Sema = getClangSema();
|
||||
|
||||
auto name = Ctx.DeclarationNames.getCXXOperatorName(
|
||||
clang::OverloadedOperatorKind::OO_Star);
|
||||
auto R = lookupCXXMember(Sema, name, CXXRecord);
|
||||
if (!R.has_value())
|
||||
return nullptr;
|
||||
|
||||
auto overloads = filterMethodOverloads(R.value(), CXXRecord);
|
||||
|
||||
const clang::CXXMethodDecl *CXXGetter = nullptr, *CXXSetter = nullptr;
|
||||
clang::AccessSpecifier CXXGetterAccess, CXXSetterAccess;
|
||||
|
||||
for (auto [method, access] : overloads) {
|
||||
if (method->isStatic() || method->isVolatile() ||
|
||||
method->getMinRequiredArguments() != 0 ||
|
||||
method->getRefQualifier() == clang::RQ_RValue)
|
||||
continue;
|
||||
|
||||
// A setter is anything that returns a mutable reference; anything else is
|
||||
// a getter.
|
||||
auto retTy = method->getReturnType();
|
||||
bool isSetter =
|
||||
retTy->isReferenceType() && !retTy->getPointeeType().isConstQualified();
|
||||
|
||||
if (isSetter) {
|
||||
if (!CXXSetter) {
|
||||
CXXSetter = method;
|
||||
CXXSetterAccess = access;
|
||||
} else if (!CXXSetter->isConst() && method->isConst()) {
|
||||
// Previously found a non-const setter, but prefer a const setter
|
||||
CXXSetter = method;
|
||||
CXXSetterAccess = access;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: a setter can also be used as a getter, so we don't check !isSetter
|
||||
if (!CXXGetter) {
|
||||
CXXGetter = method;
|
||||
CXXGetterAccess = access;
|
||||
} else if (!CXXGetter->isConst() && method->isConst()) {
|
||||
// Previously found a non-const getter, but prefer a const getter
|
||||
CXXGetter = method;
|
||||
CXXGetterAccess = access;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CXXGetter && !CXXSetter)
|
||||
return nullptr;
|
||||
|
||||
FuncDecl *getter = nullptr, *setter = nullptr;
|
||||
|
||||
if (CXXSetter) {
|
||||
setter = importUnavailableMethod(*this, CXXSetter, CXXSetterAccess, Struct,
|
||||
"use .pointee property");
|
||||
if (!setter)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (CXXGetter == CXXSetter) {
|
||||
getter = setter;
|
||||
} else if (CXXGetter) {
|
||||
getter = importUnavailableMethod(*this, CXXGetter, CXXGetterAccess, Struct,
|
||||
"use .pointee property");
|
||||
if (!getter)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SwiftDeclSynthesizer synth{*this};
|
||||
auto *pointee = synth.makeDereferencedPointeeProperty(getter, setter);
|
||||
if (!pointee)
|
||||
return nullptr;
|
||||
|
||||
importAttributes(CXXGetter ? CXXGetter : CXXSetter, pointee);
|
||||
|
||||
Struct->addMember(pointee);
|
||||
importedPointeeCache[Struct] = pointee;
|
||||
return pointee;
|
||||
}
|
||||
|
||||
FuncDecl *ClangImporter::Implementation::lookupAndImportSuccessor(
|
||||
NominalTypeDecl *Struct) {
|
||||
const auto *CXXRecord =
|
||||
dyn_cast<clang::CXXRecordDecl>(Struct->getClangDecl());
|
||||
|
||||
if (!CXXRecord || !isa<StructDecl>(Struct))
|
||||
// Do not synthesize successor() if this is not a C++ record, or if it is
|
||||
// a foreign reference type (successor() needs to copy values of this type),
|
||||
// which would be a ClassDecl rather than a StructDecl.
|
||||
return nullptr;
|
||||
|
||||
if (auto [it, inserted] = importedSuccessorCache.try_emplace(Struct, nullptr);
|
||||
!inserted)
|
||||
return it->second;
|
||||
|
||||
// From this point onward, if we encounter some error and return, future calls
|
||||
// to this function will pick up the nullptr we cached using try_emplace. Note
|
||||
// that this may silently suppress unintentional cycles.
|
||||
|
||||
auto &Ctx = getClangASTContext();
|
||||
auto &Sema = getClangSema();
|
||||
|
||||
auto name = Ctx.DeclarationNames.getCXXOperatorName(
|
||||
clang::OverloadedOperatorKind::OO_PlusPlus);
|
||||
auto R = lookupCXXMember(Sema, name, CXXRecord);
|
||||
if (!R.has_value())
|
||||
return nullptr;
|
||||
|
||||
auto overloads = filterMethodOverloads(R.value(), CXXRecord);
|
||||
|
||||
const clang::CXXMethodDecl *CXXMethod = nullptr;
|
||||
clang::AccessSpecifier CXXMethodAccess;
|
||||
|
||||
for (auto [method, access] : overloads) {
|
||||
if (method->isStatic() || method->isVolatile() ||
|
||||
method->getRefQualifier() == clang::RQ_RValue ||
|
||||
method->getMinRequiredArguments() != 0)
|
||||
continue;
|
||||
|
||||
// FIXME: we currently only support non-const overloads of operator++,
|
||||
// so skip const overloads for now. Synthesizing a nonmutating .successor()
|
||||
// from a const overload should be possible but currently causes a crash.
|
||||
|
||||
if (method->isConst())
|
||||
continue;
|
||||
|
||||
CXXMethod = method;
|
||||
CXXMethodAccess = access;
|
||||
}
|
||||
|
||||
if (!CXXMethod)
|
||||
return nullptr;
|
||||
|
||||
auto *incr = importUnavailableMethod(*this, CXXMethod, CXXMethodAccess,
|
||||
Struct, "use .pointee property");
|
||||
if (!incr)
|
||||
return nullptr;
|
||||
|
||||
SwiftDeclSynthesizer synth{*this};
|
||||
auto *succ = synth.makeSuccessorFunc(incr);
|
||||
if (!succ)
|
||||
return nullptr;
|
||||
|
||||
importAttributes(CXXMethod, succ);
|
||||
|
||||
Struct->addMember(succ);
|
||||
importedSuccessorCache[Struct] = succ;
|
||||
return succ;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/AST/StmtVisitor.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/Basic/OperatorKinds.h"
|
||||
#include "clang/Basic/Specifiers.h"
|
||||
#include "clang/Basic/TargetInfo.h"
|
||||
#include "clang/Lex/Preprocessor.h"
|
||||
@@ -86,6 +87,7 @@
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -2808,27 +2810,6 @@ namespace {
|
||||
Impl.addAlternateDecl(subscriptImpl, subscript);
|
||||
}
|
||||
}
|
||||
|
||||
auto getterAndSetterIt = Impl.cxxDereferenceOperators.find(result);
|
||||
if (getterAndSetterIt != Impl.cxxDereferenceOperators.end()) {
|
||||
// If this type has a dereference operator, synthesize a computed
|
||||
// property called `pointee` for it.
|
||||
auto getterAndSetter = getterAndSetterIt->second;
|
||||
|
||||
VarDecl *pointeeProperty =
|
||||
synthesizer.makeDereferencedPointeeProperty(
|
||||
getterAndSetter.first, getterAndSetter.second);
|
||||
|
||||
// Import the attributes from clang decl of dereference operator to
|
||||
// synthesized pointee property.
|
||||
FuncDecl *getterOrSetterImpl = getterAndSetter.first
|
||||
? getterAndSetter.first
|
||||
: getterAndSetter.second;
|
||||
Impl.importAttributesFromClangDeclToSynthesizedSwiftDecl(
|
||||
getterOrSetterImpl, pointeeProperty);
|
||||
|
||||
result->addMember(pointeeProperty);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto classDecl = dyn_cast<ClassDecl>(result)) {
|
||||
@@ -3742,9 +3723,9 @@ namespace {
|
||||
}
|
||||
|
||||
/// Handles special functions such as subscripts and dereference operators.
|
||||
bool
|
||||
processSpecialImportedFunc(FuncDecl *func, ImportedName importedName,
|
||||
clang::OverloadedOperatorKind cxxOperatorKind) {
|
||||
bool processSpecialImportedFunc(FuncDecl *func, ImportedName importedName,
|
||||
const clang::FunctionDecl *clangFunc) {
|
||||
auto cxxOperatorKind = clangFunc->getOverloadedOperator();
|
||||
if (cxxOperatorKind == clang::OverloadedOperatorKind::OO_None)
|
||||
return true;
|
||||
// If this operator was renamed via swift_name attribute, the imported
|
||||
@@ -3795,50 +3776,36 @@ namespace {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (importedName.isDereferenceAccessor()) {
|
||||
auto &getterAndSetter = Impl.cxxDereferenceOperators[typeDecl];
|
||||
|
||||
switch (importedName.getAccessorKind()) {
|
||||
case ImportedAccessorKind::DereferenceGetter:
|
||||
getterAndSetter.first = func;
|
||||
break;
|
||||
case ImportedAccessorKind::DereferenceSetter:
|
||||
getterAndSetter.second = func;
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("invalid dereference operator kind");
|
||||
}
|
||||
|
||||
Impl.markUnavailable(func, "use .pointee property");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cxxOperatorKind == clang::OverloadedOperatorKind::OO_PlusPlus) {
|
||||
// Make sure the type is not a foreign reference type.
|
||||
// We cannot handle `operator++` for those types, since the
|
||||
// current implementation creates a new instance of the type.
|
||||
if (func->getParameters()->size() == 0 && !isa<ClassDecl>(typeDecl)) {
|
||||
// This is a pre-increment operator. We synthesize a
|
||||
// non-mutating function called `successor() -> Self`.
|
||||
FuncDecl *successorFunc = synthesizer.makeSuccessorFunc(func);
|
||||
|
||||
// Import the clang decl attributes to synthesized successor function.
|
||||
Impl.importAttributesFromClangDeclToSynthesizedSwiftDecl(func, successorFunc);
|
||||
|
||||
typeDecl->addMember(successorFunc);
|
||||
|
||||
Impl.markUnavailable(func, "use .successor()");
|
||||
} else {
|
||||
Impl.markUnavailable(func, "unable to create .successor() func");
|
||||
}
|
||||
func->overwriteAccess(AccessLevel::Private);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if this method _is_ an overloaded operator but is not a
|
||||
// call / subscript / dereference / increment. Those
|
||||
// operators do not need static versions.
|
||||
if (cxxOperatorKind != clang::OverloadedOperatorKind::OO_Call) {
|
||||
switch (cxxOperatorKind) {
|
||||
case clang::OverloadedOperatorKind::OO_None:
|
||||
llvm_unreachable("should only be handling operators at this point");
|
||||
case clang::OverloadedOperatorKind::OO_PlusPlus:
|
||||
if (clangFunc->getMinRequiredArguments() != 0)
|
||||
// Do not allow post-increment to be used from Swift
|
||||
Impl.markUnavailable(func, "unable to create .successor() func");
|
||||
else if (auto *method = dyn_cast<clang::CXXMethodDecl>(clangFunc);
|
||||
method && method->getRefQualifier() == clang::RQ_RValue)
|
||||
// TODO: we shouldn't have to handle this case. We only mark it as
|
||||
// unavailable for now to preserve old behavior, where r-value-this
|
||||
// operator ++ overloads are always unavailable.
|
||||
Impl.markUnavailable(func, "use .successor()");
|
||||
break;
|
||||
|
||||
case clang::OverloadedOperatorKind::OO_Star:
|
||||
if (auto *method = dyn_cast<clang::CXXMethodDecl>(clangFunc);
|
||||
method && method->getRefQualifier() == clang::RQ_RValue)
|
||||
// TODO: we shouldn't have to handle this case. We only mark it as
|
||||
// unavailable for now to preserve old behavior, where r-value-this
|
||||
// operator * overloads are always unavailable.
|
||||
Impl.markUnavailable(func, "use .pointee property");
|
||||
break;
|
||||
case clang::OverloadedOperatorKind::OO_Call:
|
||||
case clang::OverloadedOperatorKind::OO_Subscript:
|
||||
break;
|
||||
default:
|
||||
auto opFuncDecl = synthesizer.makeOperator(func, cxxOperatorKind);
|
||||
Impl.addAlternateDecl(func, opFuncDecl);
|
||||
|
||||
@@ -4186,8 +4153,7 @@ namespace {
|
||||
}
|
||||
func->setAccess(importer::convertClangAccess(decl->getAccess()));
|
||||
|
||||
bool success = processSpecialImportedFunc(
|
||||
func, importedName, decl->getOverloadedOperator());
|
||||
bool success = processSpecialImportedFunc(func, importedName, decl);
|
||||
if (!success)
|
||||
return nullptr;
|
||||
}
|
||||
@@ -5022,8 +4988,8 @@ namespace {
|
||||
clonedMethod->overwriteAccess(
|
||||
importer::convertClangAccess(decl->getAccess()));
|
||||
|
||||
bool success = processSpecialImportedFunc(
|
||||
clonedMethod, importedName, targetMethod->getOverloadedOperator());
|
||||
bool success = processSpecialImportedFunc(clonedMethod, importedName,
|
||||
targetMethod);
|
||||
if (!success)
|
||||
return nullptr;
|
||||
|
||||
@@ -9423,17 +9389,6 @@ static void filterUsableVersionedAttrs(
|
||||
}
|
||||
}
|
||||
|
||||
void ClangImporter::Implementation::importAttributesFromClangDeclToSynthesizedSwiftDecl(Decl *sourceDecl, Decl* synthesizedDecl)
|
||||
{
|
||||
// sourceDecl->getClangDecl() can be null because some lazily instantiated cases like C++ members that were instantiated from using-shadow-decls have no corresponding Clang decl.
|
||||
// FIXME: Need to include the cases where correspondoing clang decl is not present.
|
||||
if (auto clangDeclForSource =
|
||||
dyn_cast_or_null<clang::NamedDecl>(
|
||||
sourceDecl->getClangDecl())) {
|
||||
importAttributes(clangDeclForSource, synthesizedDecl);
|
||||
}
|
||||
}
|
||||
|
||||
/// Import Clang attributes as Swift attributes.
|
||||
void ClangImporter::Implementation::importAttributes(
|
||||
const clang::NamedDecl *ClangDecl,
|
||||
@@ -10653,6 +10608,11 @@ void ClangImporter::Implementation::loadAllMembersOfRecordDecl(
|
||||
loadAllMembersOfRecordDecl(swiftDecl, baseRecord, baseInheritance);
|
||||
}
|
||||
}
|
||||
|
||||
if ((isa<clang::CXXRecordDecl>(swiftDecl->getClangDecl())) && !inheritance) {
|
||||
lookupAndImportPointee(swiftDecl);
|
||||
lookupAndImportSuccessor(swiftDecl);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "swift/ClangImporter/ClangModule.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclVisitor.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Basic/IdentifierTable.h"
|
||||
@@ -51,6 +52,7 @@
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/PointerIntPair.h"
|
||||
#include "llvm/ADT/SmallBitVector.h"
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
@@ -689,9 +691,6 @@ public:
|
||||
std::map<SmallVector<TypeBase *>, unsigned>>>
|
||||
cxxSubscripts;
|
||||
|
||||
llvm::MapVector<NominalTypeDecl *, std::pair<FuncDecl *, FuncDecl *>>
|
||||
cxxDereferenceOperators;
|
||||
|
||||
llvm::SmallPtrSet<const clang::Decl *, 1> synthesizedAndAlwaysVisibleDecls;
|
||||
|
||||
private:
|
||||
@@ -711,6 +710,29 @@ private:
|
||||
llvm::DenseSet<std::pair<const clang::CXXRecordDecl *, DeclName>>
|
||||
unavailableMethods;
|
||||
|
||||
public:
|
||||
// Attempt to lookup and import the synthesized .pointee computed property.
|
||||
//
|
||||
// Requires that \a Record is a (Swift) StructDecl and is import from
|
||||
// a CXXRecordDecl.
|
||||
//
|
||||
// This function is idempotent, and if successful, ensures the synthesized
|
||||
// .pointee that it returns is a mamber of \a Record.
|
||||
VarDecl *lookupAndImportPointee(NominalTypeDecl *Record);
|
||||
|
||||
// Attempt to lookup and import the synthesized .successor() method.
|
||||
//
|
||||
// Requires that \a Record is a (Swift) StructDecl and is import from
|
||||
// a CXXRecordDecl.
|
||||
//
|
||||
// This function is idempotent, and if successful, ensures the synthesized
|
||||
// .successor() that it returns is a mamber of \a Record.
|
||||
FuncDecl *lookupAndImportSuccessor(NominalTypeDecl *Record);
|
||||
|
||||
private:
|
||||
llvm::DenseMap<NominalTypeDecl *, VarDecl *> importedPointeeCache;
|
||||
llvm::DenseMap<NominalTypeDecl *, FuncDecl *> importedSuccessorCache;
|
||||
|
||||
public:
|
||||
llvm::DenseMap<const clang::ParmVarDecl*, FuncDecl*> defaultArgGenerators;
|
||||
|
||||
@@ -1111,15 +1133,6 @@ public:
|
||||
/// retrieving a chached source file as needed.
|
||||
void importNontrivialAttribute(Decl *MappedDecl, StringRef attributeText);
|
||||
|
||||
/// Utility function to import Clang attributes from a source Swift decl to
|
||||
/// synthesized Swift decl.
|
||||
///
|
||||
/// \param SourceDecl The Swift decl to copy the atteribute from.
|
||||
/// \param SynthesizedDecl The synthesized Swift decl to attach attributes to.
|
||||
void
|
||||
importAttributesFromClangDeclToSynthesizedSwiftDecl(Decl *SourceDecl,
|
||||
Decl *SynthesizedDecl);
|
||||
|
||||
/// Import attributes from the given Clang declaration to its Swift
|
||||
/// equivalent.
|
||||
///
|
||||
|
||||
@@ -71,7 +71,6 @@
|
||||
// CHECK: public struct OperatorBasePrivateInheritance : CxxConvertibleToBool {
|
||||
// CHECK-NEXT: public init()
|
||||
// CHECK-NEXT: public subscript(x: Int32) -> Int32 { get }
|
||||
// CHECK-NEXT: public var pointee: Int32 { get }
|
||||
// CHECK-NEXT: public func __convertToBool() -> Bool
|
||||
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
|
||||
// CHECK-NEXT: public func __operatorStar() -> Int32
|
||||
@@ -80,4 +79,5 @@
|
||||
// CHECK-NEXT: public func __operatorExclaim() -> OperatorBase
|
||||
// CHECK-NEXT: @available(*, unavailable, message: "use subscript")
|
||||
// CHECK-NEXT: public func __operatorSubscriptConst(_ x: Int32) -> Int32
|
||||
// CHECK-NEXT: public var pointee: Int32 { get }
|
||||
// CHECK-NEXT: }
|
||||
|
||||
@@ -536,7 +536,7 @@ struct ClassWithOperatorStarAvailable {
|
||||
int value;
|
||||
|
||||
public:
|
||||
int &operator*() { return value; }
|
||||
const int &operator*() const { return value; }
|
||||
};
|
||||
|
||||
struct DerivedClassWithOperatorStarAvailable : ClassWithOperatorStarAvailable {
|
||||
@@ -546,7 +546,8 @@ struct ClassWithOperatorStarUnavailable {
|
||||
int value;
|
||||
|
||||
public:
|
||||
int &operator*() __attribute__((availability(swift, unavailable))) {
|
||||
const int &operator*() const
|
||||
__attribute__((availability(swift, unavailable))) {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -22,3 +22,8 @@ module RenamedOperators {
|
||||
header "renamed-operators.h"
|
||||
requires cplusplus
|
||||
}
|
||||
|
||||
module PointeeOverloads {
|
||||
header "pointee-overloads.h"
|
||||
requires cplusplus
|
||||
}
|
||||
|
||||
97
test/Interop/Cxx/operators/Inputs/pointee-overloads.h
Normal file
97
test/Interop/Cxx/operators/Inputs/pointee-overloads.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
struct Pointee_Const {
|
||||
int x = 111;
|
||||
const int &operator*() const { return x; }
|
||||
};
|
||||
|
||||
struct Pointee_NonConst {
|
||||
int x = 222;
|
||||
int &operator*() { return x; }
|
||||
};
|
||||
|
||||
struct Pointee_Const_NonConst {
|
||||
int x = 333;
|
||||
int y = 444;
|
||||
const int &operator*() const { return x; }
|
||||
int &operator*() { return y; }
|
||||
};
|
||||
|
||||
struct Pointee_NonConst_Const {
|
||||
int x = 333;
|
||||
int y = 444;
|
||||
int &operator*() { return y; }
|
||||
const int &operator*() const { return x; }
|
||||
};
|
||||
|
||||
struct Pointee_NonConst_NonConst {
|
||||
int x = 333;
|
||||
int y = 444;
|
||||
const int &operator*() const { return x; }
|
||||
const int &operator*() { return y; }
|
||||
};
|
||||
|
||||
struct Pointee_Volatile {
|
||||
int x = 404;
|
||||
volatile int &operator*() volatile { return x; }
|
||||
};
|
||||
|
||||
struct Pointee_ConstVolatile {
|
||||
int x = 404;
|
||||
const volatile int &operator*() const volatile { return x; }
|
||||
};
|
||||
|
||||
struct Pointee_Volatile_Const {
|
||||
int x = 404;
|
||||
int y = 555;
|
||||
volatile int &operator*() volatile { return x; }
|
||||
const int &operator*() const { return y; }
|
||||
};
|
||||
|
||||
struct Pointee_NonConstGetter {
|
||||
int x = 666;
|
||||
const int &operator*() { return x; }
|
||||
};
|
||||
|
||||
struct Pointee_MutableConst {
|
||||
mutable int x = 666;
|
||||
int &operator*() const { return x; }
|
||||
};
|
||||
|
||||
struct Pointee_LConst {
|
||||
int x = 1111;
|
||||
const int &operator*() const & { return x; }
|
||||
};
|
||||
|
||||
struct Pointee_LNonConst {
|
||||
int x = 2222;
|
||||
int &operator*() & { return x; }
|
||||
};
|
||||
|
||||
struct Pointee_LConst_LNonConst {
|
||||
int x = 3333;
|
||||
int y = 4444;
|
||||
const int &operator*() const & { return x; }
|
||||
int &operator*() & { return y; }
|
||||
};
|
||||
|
||||
struct Pointee_LNonConst_LConst {
|
||||
int x = 3333;
|
||||
int y = 4444;
|
||||
int &operator*() & { return y; }
|
||||
const int &operator*() const & { return x; }
|
||||
};
|
||||
|
||||
struct Pointee_LConst_RConst {
|
||||
int x = 5555;
|
||||
int y = 6666;
|
||||
const int &operator*() const & { return x; }
|
||||
const int &operator*() const && { return y; }
|
||||
};
|
||||
|
||||
struct Pointee_LNonConst_RNonConst {
|
||||
int x = 5555;
|
||||
int y = 6666;
|
||||
int &operator*() & { return x; }
|
||||
int &operator*() && { return y; }
|
||||
};
|
||||
@@ -1,14 +1,12 @@
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -cxx-interoperability-mode=swift-5.9 -Xcc -std=c++23 | %FileCheck %s
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -cxx-interoperability-mode=swift-6 -Xcc -std=c++23 | %FileCheck %s
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -cxx-interoperability-mode=upcoming-swift -Xcc -std=c++23 | %FileCheck %s
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -cxx-interoperability-mode=default -Xcc -std=c++23 | %FileCheck %s
|
||||
|
||||
// CHECK: struct LoadableIntWrapper {
|
||||
// CHECK: func successor() -> LoadableIntWrapper
|
||||
// CHECK: static func - (lhs: inout LoadableIntWrapper, rhs: LoadableIntWrapper) -> LoadableIntWrapper
|
||||
// CHECK: static func += (lhs: inout LoadableIntWrapper, rhs: LoadableIntWrapper)
|
||||
// CHECK: mutating func callAsFunction() -> Int32
|
||||
// CHECK: mutating func callAsFunction(_ x: Int32) -> Int32
|
||||
// CHECK: mutating func callAsFunction(_ x: Int32, _ y: Int32) -> Int32
|
||||
// CHECK: func successor() -> LoadableIntWrapper
|
||||
// CHECK: }
|
||||
// CHECK: func == (lhs: LoadableIntWrapper, rhs: LoadableIntWrapper) -> Bool
|
||||
// CHECK: func -= (lhs: inout LoadableIntWrapper, rhs: LoadableIntWrapper)
|
||||
@@ -234,36 +232,35 @@
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct Iterator {
|
||||
// CHECK: var pointee: Int32 { mutating get set }
|
||||
// CHECK: @available(*, unavailable, message: "use .pointee property")
|
||||
// CHECK: mutating func __operatorStar() -> UnsafeMutablePointer<Int32>
|
||||
// CHECK: var pointee: Int32 { mutating get set }
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct ConstIterator {
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: @available(*, unavailable, message: "use .pointee property")
|
||||
// CHECK: func __operatorStar() -> UnsafePointer<Int32>
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct ConstIteratorByVal {
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: @available(*, unavailable, message: "use .pointee property")
|
||||
// CHECK: func __operatorStar() -> Int32
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct AmbiguousOperatorStar {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: var pointee: Int32
|
||||
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
|
||||
// CHECK-NEXT: mutating func __operatorStar() -> UnsafeMutablePointer<Int32>
|
||||
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
|
||||
// CHECK-NEXT: func __operatorStar() -> UnsafePointer<Int32>
|
||||
// CHECK-NEXT: var value: Int32
|
||||
// CHECK-NEXT: var pointee: Int32
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK: struct AmbiguousOperatorStar2 {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: var pointee: Int32
|
||||
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
|
||||
// CHECK-NEXT: mutating func __operatorStar() -> UnsafeMutablePointer<Int32>
|
||||
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
|
||||
@@ -271,29 +268,30 @@
|
||||
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
|
||||
// CHECK-NEXT: func __operatorStar() -> UnsafePointer<Int32>
|
||||
// CHECK-NEXT: var value: Int32
|
||||
// CHECK-NEXT: var pointee: Int32
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK: struct DerivedFromConstIterator {
|
||||
// CHECK-NEXT: init()
|
||||
// TODO: @available(*, unavailable, message: "use .pointee property")
|
||||
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
|
||||
// CHECK-NEXT: func __operatorStar() -> UnsafePointer<Int32>
|
||||
// TODO: `var pointee` should be printed here
|
||||
// CHECK: }
|
||||
// CHECK-NEXT: var pointee: Int32 { get }
|
||||
// CHECK-NEXT: }
|
||||
|
||||
// CHECK: struct DerivedFromConstIteratorPrivatelyWithUsingDecl {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: var pointee: Int32 { get }
|
||||
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
|
||||
// CHECK-NEXT: func __operatorStar() -> UnsafePointer<Int32>
|
||||
// CHECK-NEXT: var pointee: Int32 { get }
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct DerivedFromAmbiguousOperatorStarPrivatelyWithUsingDecl {
|
||||
// CHECK-NEXT: init()
|
||||
// CHECK-NEXT: var pointee: Int32
|
||||
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
|
||||
// CHECK-NEXT: mutating func __operatorStar() -> UnsafeMutablePointer<Int32>
|
||||
// CHECK-NEXT: @available(*, unavailable, message: "use .pointee property")
|
||||
// CHECK-NEXT: func __operatorStar() -> UnsafePointer<Int32>
|
||||
// CHECK-NEXT: var pointee: Int32
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct DerivedFromLoadableIntWrapperWithUsingDecl {
|
||||
|
||||
@@ -71,8 +71,7 @@ let voidReturnTypeResult: HasPreIncrementOperatorWithVoidReturnType = voidReturn
|
||||
let immortalIncrement = myCounter.successor() // expected-error {{value of type 'ImmortalCounter' has no member 'successor'}}
|
||||
|
||||
let derivedConstIter = DerivedFromConstIteratorPrivately()
|
||||
derivedConstIter.pointee // expected-error {{value of type 'DerivedFromConstIteratorPrivately' has no member 'pointee'}}
|
||||
// FIXME: inheriting operators is currently flaky. the error should be {{'pointee' is inaccessible due to 'private' protection level}}
|
||||
derivedConstIter.pointee // expected-error {{'pointee' is inaccessible due to 'private' protection level}}
|
||||
|
||||
let derivedConstIterWithUD = DerivedFromConstIteratorPrivatelyWithUsingDecl()
|
||||
let _ = derivedConstIterWithUD.pointee
|
||||
|
||||
114
test/Interop/Cxx/operators/pointee-overloads-typechecker.swift
Normal file
114
test/Interop/Cxx/operators/pointee-overloads-typechecker.swift
Normal file
@@ -0,0 +1,114 @@
|
||||
// RUN: %target-typecheck-verify-swift -I %S/Inputs -cxx-interoperability-mode=default -suppress-notes
|
||||
import PointeeOverloads
|
||||
|
||||
let lPointee_Const = Pointee_Const()
|
||||
let _ = lPointee_Const.pointee
|
||||
|
||||
var vPointee_Const = Pointee_Const()
|
||||
let _ = vPointee_Const.pointee
|
||||
vPointee_Const.pointee = 42 // expected-error {{'pointee' is a get-only property}}
|
||||
|
||||
let lPointee_NonConst = Pointee_NonConst()
|
||||
let _ = lPointee_NonConst.pointee // expected-error {{cannot use mutating getter on immutable value}}
|
||||
|
||||
var vPointee_NonConst = Pointee_NonConst()
|
||||
let _ = vPointee_NonConst.pointee
|
||||
vPointee_NonConst.pointee = 42
|
||||
|
||||
let lPointee_Const_NonConst = Pointee_Const_NonConst()
|
||||
let _ = lPointee_Const_NonConst.pointee
|
||||
|
||||
var vPointee_Const_NonConst = Pointee_Const_NonConst()
|
||||
let _ = vPointee_Const_NonConst.pointee
|
||||
vPointee_Const_NonConst.pointee = 42
|
||||
|
||||
let lPointee_NonConst_Const = Pointee_NonConst_Const()
|
||||
let _ = lPointee_NonConst_Const.pointee
|
||||
|
||||
var vPointee_NonConst_Const = Pointee_NonConst_Const()
|
||||
let _ = vPointee_NonConst_Const.pointee
|
||||
vPointee_NonConst_Const.pointee = 42
|
||||
|
||||
let lPointee_NonConst_NonConst = Pointee_NonConst_NonConst()
|
||||
let _ = lPointee_NonConst_NonConst.pointee
|
||||
|
||||
var vPointee_NonConst_NonConst = Pointee_NonConst_NonConst()
|
||||
let _ = vPointee_NonConst_NonConst.pointee
|
||||
vPointee_NonConst_NonConst.pointee = 42 // expected-error {{'pointee' is a get-only property}}
|
||||
|
||||
let lPointee_Volatile = Pointee_Volatile()
|
||||
let _ = lPointee_Volatile.pointee // expected-error {{has no member 'pointee'}}
|
||||
|
||||
var vPointee_Volatile = Pointee_Volatile()
|
||||
let _ = vPointee_Volatile.pointee // expected-error {{has no member 'pointee'}}
|
||||
vPointee_Volatile.pointee = 42 // expected-error {{has no member 'pointee'}}
|
||||
|
||||
let lPointee_ConstVolatile = Pointee_ConstVolatile()
|
||||
let _ = lPointee_ConstVolatile.pointee // expected-error {{has no member 'pointee'}}
|
||||
|
||||
var vPointee_ConstVolatile = Pointee_ConstVolatile()
|
||||
let _ = vPointee_ConstVolatile.pointee // expected-error {{has no member 'pointee'}}
|
||||
vPointee_ConstVolatile.pointee = 42 // expected-error {{has no member 'pointee'}}
|
||||
|
||||
let lPointee_Volatile_Const = Pointee_Volatile_Const()
|
||||
let _ = lPointee_Volatile_Const.pointee
|
||||
|
||||
var vPointee_Volatile_Const = Pointee_Volatile_Const()
|
||||
let _ = vPointee_Volatile_Const.pointee
|
||||
vPointee_Volatile_Const.pointee = 42 // expected-error {{'pointee' is a get-only property}}
|
||||
|
||||
let lPointee_NonConstGetter = Pointee_NonConstGetter()
|
||||
let _ = lPointee_NonConstGetter.pointee // expected-error {{cannot use mutating getter on immutable value}}
|
||||
|
||||
var vPointee_NonConstGetter = Pointee_NonConstGetter()
|
||||
let _ = vPointee_NonConstGetter.pointee
|
||||
vPointee_NonConstGetter.pointee = 42 // expected-error {{'pointee' is a get-only property}}
|
||||
|
||||
let lPointee_MutableConst = Pointee_MutableConst()
|
||||
let _ = lPointee_MutableConst.pointee
|
||||
|
||||
var vPointee_MutableConst = Pointee_MutableConst()
|
||||
let _ = vPointee_MutableConst.pointee
|
||||
vPointee_MutableConst.pointee = 42
|
||||
|
||||
let lPointee_LConst = Pointee_LConst()
|
||||
let _ = lPointee_LConst.pointee
|
||||
|
||||
var vPointee_LConst = Pointee_LConst()
|
||||
let _ = vPointee_LConst.pointee
|
||||
vPointee_LConst.pointee = 42 // expected-error {{'pointee' is a get-only property}}
|
||||
|
||||
let lPointee_LNonConst = Pointee_LNonConst()
|
||||
let _ = lPointee_LNonConst.pointee // expected-error {{cannot use mutating getter on immutable value}}
|
||||
|
||||
var vPointee_LNonConst = Pointee_LNonConst()
|
||||
let _ = vPointee_LNonConst.pointee
|
||||
vPointee_LNonConst.pointee = 42
|
||||
|
||||
let lPointee_LConst_LNonConst = Pointee_LConst_LNonConst()
|
||||
let _ = lPointee_LConst_LNonConst.pointee
|
||||
|
||||
var vPointee_LConst_LNonConst = Pointee_LConst_LNonConst()
|
||||
let _ = vPointee_LConst_LNonConst.pointee
|
||||
vPointee_LConst_LNonConst.pointee = 42
|
||||
|
||||
let lPointee_LNonConst_LConst = Pointee_LNonConst_LConst()
|
||||
let _ = lPointee_LNonConst_LConst.pointee
|
||||
|
||||
var vPointee_LNonConst_LConst = Pointee_LNonConst_LConst()
|
||||
let _ = vPointee_LNonConst_LConst.pointee
|
||||
vPointee_LNonConst_LConst.pointee = 42
|
||||
|
||||
let lPointee_LConst_RConst = Pointee_LConst_RConst()
|
||||
let _ = lPointee_LConst_RConst.pointee
|
||||
|
||||
var vPointee_LConst_RConst = Pointee_LConst_RConst()
|
||||
let _ = vPointee_LConst_RConst.pointee
|
||||
vPointee_LConst_RConst.pointee = 42 // expected-error {{'pointee' is a get-only property}}
|
||||
|
||||
let lPointee_LNonConst_RNonConst = Pointee_LNonConst_RNonConst()
|
||||
let _ = lPointee_LNonConst_RNonConst.pointee // expected-error {{cannot use mutating getter on immutable value}}
|
||||
|
||||
var vPointee_LNonConst_RNonConst = Pointee_LNonConst_RNonConst()
|
||||
let _ = vPointee_LNonConst_RNonConst.pointee
|
||||
vPointee_LNonConst_RNonConst.pointee = 42
|
||||
156
test/Interop/Cxx/operators/pointee-overloads.swift
Normal file
156
test/Interop/Cxx/operators/pointee-overloads.swift
Normal file
@@ -0,0 +1,156 @@
|
||||
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=default)
|
||||
//
|
||||
// REQUIRES: executable_test
|
||||
|
||||
import PointeeOverloads
|
||||
import StdlibUnittest
|
||||
|
||||
var PointeeOverloadTestSuite = TestSuite("OperatorOverload")
|
||||
|
||||
PointeeOverloadTestSuite.test("Pointee_Const") {
|
||||
let a = Pointee_Const()
|
||||
expectEqual(a.pointee, 111)
|
||||
|
||||
var b = Pointee_Const()
|
||||
expectEqual(b.pointee, 111)
|
||||
b.x = 100
|
||||
expectEqual(b.pointee, 100)
|
||||
}
|
||||
|
||||
PointeeOverloadTestSuite.test("Pointee_NonConst") {
|
||||
var a = Pointee_NonConst()
|
||||
expectEqual(a.pointee, 222)
|
||||
a.pointee = 200
|
||||
expectEqual(a.x, 200)
|
||||
a.x = 202
|
||||
expectEqual(a.pointee, 202)
|
||||
}
|
||||
|
||||
PointeeOverloadTestSuite.test("Pointee_Const_NonConst") {
|
||||
let a = Pointee_Const_NonConst()
|
||||
expectEqual(a.pointee, 333)
|
||||
|
||||
var b = Pointee_Const_NonConst()
|
||||
expectEqual(b.pointee, 333)
|
||||
b.pointee = 400
|
||||
expectEqual(b.pointee, 333)
|
||||
expectEqual(b.x, 333)
|
||||
expectEqual(b.y, 400)
|
||||
b.y = 440
|
||||
expectEqual(b.pointee, 333)
|
||||
expectEqual(b.x, 333)
|
||||
expectEqual(b.y, 440)
|
||||
}
|
||||
|
||||
PointeeOverloadTestSuite.test("Pointee_NonConst_Const") {
|
||||
let a = Pointee_NonConst_Const()
|
||||
expectEqual(a.pointee, 333)
|
||||
|
||||
var b = Pointee_NonConst_Const()
|
||||
expectEqual(b.pointee, 333)
|
||||
b.pointee = 400
|
||||
expectEqual(b.pointee, 333)
|
||||
expectEqual(b.x, 333)
|
||||
expectEqual(b.y, 400)
|
||||
}
|
||||
|
||||
PointeeOverloadTestSuite.test("Pointee_NonConst_NonConst") {
|
||||
let a = Pointee_NonConst_NonConst()
|
||||
expectEqual(a.pointee, 333)
|
||||
|
||||
var b = Pointee_NonConst_NonConst()
|
||||
expectEqual(b.pointee, 333)
|
||||
}
|
||||
|
||||
PointeeOverloadTestSuite.test("Pointee_Volatile_Const") {
|
||||
var a = Pointee_Volatile_Const()
|
||||
expectEqual(a.pointee, 555)
|
||||
}
|
||||
|
||||
PointeeOverloadTestSuite.test("Pointee_NonConstGetter") {
|
||||
var a = Pointee_NonConstGetter()
|
||||
expectEqual(a.pointee, 666)
|
||||
// a.pointee = 6466 // get-only property
|
||||
}
|
||||
|
||||
PointeeOverloadTestSuite.test("Pointee_MutableConst") {
|
||||
let a = Pointee_MutableConst() // for some reason let is ok here
|
||||
expectEqual(a.pointee, 666)
|
||||
|
||||
a.pointee = 600 // FIXME: assigning to mutable doesn't seem to work
|
||||
expectNotEqual(a.pointee, 600) // FIXME: this should be expectEqual
|
||||
expectNotEqual(a.x, 600) // FIXME: this should be expectEqual
|
||||
}
|
||||
|
||||
PointeeOverloadTestSuite.test("Pointee_LConst") {
|
||||
let a = Pointee_LConst()
|
||||
expectEqual(a.pointee, 1111)
|
||||
|
||||
var b = Pointee_LConst()
|
||||
expectEqual(b.pointee, 1111)
|
||||
b.x = 1000
|
||||
expectEqual(b.pointee, 1000)
|
||||
}
|
||||
|
||||
PointeeOverloadTestSuite.test("Pointee_LNonConst") {
|
||||
var a = Pointee_LNonConst()
|
||||
expectEqual(a.pointee, 2222)
|
||||
a.pointee = 2000
|
||||
expectEqual(a.x, 2000)
|
||||
a.x = 2020
|
||||
expectEqual(a.pointee, 2020)
|
||||
}
|
||||
|
||||
PointeeOverloadTestSuite.test("Pointee_LConst_LNonConst") {
|
||||
let a = Pointee_LConst_LNonConst()
|
||||
expectEqual(a.pointee, 3333)
|
||||
|
||||
var b = Pointee_LConst_LNonConst()
|
||||
expectEqual(b.pointee, 3333)
|
||||
b.pointee = 4000
|
||||
expectEqual(b.pointee, 3333)
|
||||
expectEqual(b.x, 3333)
|
||||
expectEqual(b.y, 4000)
|
||||
}
|
||||
|
||||
PointeeOverloadTestSuite.test("Pointee_LNonConst_LConst") {
|
||||
let a = Pointee_LConst_LNonConst()
|
||||
expectEqual(a.pointee, 3333)
|
||||
|
||||
var b = Pointee_LConst_LNonConst()
|
||||
expectEqual(b.pointee, 3333)
|
||||
b.pointee = 4000
|
||||
expectEqual(b.pointee, 3333)
|
||||
expectEqual(b.x, 3333)
|
||||
expectEqual(b.y, 4000)
|
||||
b.y = 4400
|
||||
expectEqual(b.pointee, 3333)
|
||||
expectEqual(b.x, 3333)
|
||||
expectEqual(b.y, 4400)
|
||||
}
|
||||
|
||||
PointeeOverloadTestSuite.test("Pointee_LConst_RConst") {
|
||||
let a = Pointee_LConst_RConst()
|
||||
expectEqual(a.pointee, 5555)
|
||||
|
||||
var b = Pointee_LConst_RConst()
|
||||
expectEqual(b.pointee, 5555)
|
||||
b.x = 5000
|
||||
b.y = 6000
|
||||
expectEqual(b.pointee, 5000)
|
||||
}
|
||||
|
||||
PointeeOverloadTestSuite.test("Pointee_LNonConst_RNonConst") {
|
||||
var a = Pointee_LNonConst_RNonConst()
|
||||
expectEqual(a.pointee, 5555)
|
||||
a.pointee = 5000
|
||||
expectEqual(a.x, 5000)
|
||||
expectEqual(a.y, 6666)
|
||||
a.x = 5050
|
||||
a.y = 6060
|
||||
expectEqual(a.pointee, 5050)
|
||||
expectEqual(a.x, 5050)
|
||||
expectEqual(a.y, 6060)
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
@@ -12,10 +12,10 @@
|
||||
// CHECK-STRING: typealias size_t = size_t
|
||||
// CHECK-STRING: static func to_string(_ _Val: Int32) -> std.string
|
||||
// CHECK-STRING: static func to_wstring(_ _Val: Int32) -> std.wstring
|
||||
// CHECK-STRING: struct basic_string<CChar, std.char_traits<CChar>, std.allocator<CChar>> : CxxRandomAccessCollection {
|
||||
// CHECK-STRING: struct basic_string<CChar, std.char_traits<CChar>, std.allocator<CChar>> : CxxMutableRandomAccessCollection {
|
||||
// CHECK-STRING: typealias value_type = CChar
|
||||
// CHECK-STRING: }
|
||||
// CHECK-STRING: struct basic_string<CChar16, std.char_traits<CChar16>, std.allocator<CChar16>> : CxxRandomAccessCollection {
|
||||
// CHECK-STRING: struct basic_string<CChar16, std.char_traits<CChar16>, std.allocator<CChar16>> : CxxMutableRandomAccessCollection {
|
||||
// CHECK-STRING: typealias value_type = UInt16
|
||||
// FIXME: why the value type is different from CChar16?
|
||||
// CHECK-STRING: }
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=CustomIterator -source-filename=x -I %S/Inputs -cxx-interoperability-mode=swift-6 -Xcc -std=c++20 | %FileCheck %s
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=CustomIterator -source-filename=x -I %S/Inputs -cxx-interoperability-mode=upcoming-swift -Xcc -std=c++20 | %FileCheck %s
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=CustomIterator -source-filename=x -I %S/Inputs -cxx-interoperability-mode=default -Xcc -std=c++20 | %FileCheck %s
|
||||
|
||||
// Ubuntu 20.04 ships with an old version of libstdc++, which does not provide
|
||||
// std::contiguous_iterator_tag from C++20.
|
||||
@@ -7,29 +6,29 @@
|
||||
// UNSUPPORTED: LinuxDistribution=amzn-2
|
||||
|
||||
// CHECK: struct ConstContiguousIterator : UnsafeCxxContiguousIterator, UnsafeCxxRandomAccessIterator, UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> ConstContiguousIterator
|
||||
// CHECK: var pointee: Int32
|
||||
// CHECK: func successor() -> ConstContiguousIterator
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: typealias Distance = Int32
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct HasCustomContiguousIteratorTag : UnsafeCxxContiguousIterator, UnsafeCxxRandomAccessIterator, UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> HasCustomContiguousIteratorTag
|
||||
// CHECK: var pointee: Int32
|
||||
// CHECK: func successor() -> HasCustomContiguousIteratorTag
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: typealias Distance = Int32
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct MutableContiguousIterator : UnsafeCxxMutableContiguousIterator, UnsafeCxxMutableRandomAccessIterator, UnsafeCxxMutableInputIterator {
|
||||
// CHECK: func successor() -> MutableContiguousIterator
|
||||
// CHECK: var pointee: Int32
|
||||
// CHECK: func successor() -> MutableContiguousIterator
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: typealias Distance = Int32
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct HasNoContiguousIteratorConcept : UnsafeCxxRandomAccessIterator, UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> HasNoContiguousIteratorConcept
|
||||
// CHECK: var pointee: Int32
|
||||
// CHECK: func successor() -> HasNoContiguousIteratorConcept
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: typealias Distance = Int32
|
||||
// CHECK: }
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=CustomIterator -source-filename=x -I %S/Inputs -enable-experimental-cxx-interop | %FileCheck %s
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=CustomIterator -source-filename=x -I %S/Inputs -cxx-interoperability-mode=swift-6 | %FileCheck %s
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=CustomIterator -source-filename=x -I %S/Inputs -cxx-interoperability-mode=upcoming-swift | %FileCheck %s
|
||||
// RUN: %target-swift-ide-test -print-module -module-to-print=CustomIterator -source-filename=x -I %S/Inputs -cxx-interoperability-mode=default | %FileCheck %s
|
||||
|
||||
// CHECK: struct ConstIterator : UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> ConstIterator
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: func successor() -> ConstIterator
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: static func == (lhs: ConstIterator, other: ConstIterator) -> Bool
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct ConstRACIterator : UnsafeCxxRandomAccessIterator, UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> ConstRACIterator
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: func successor() -> ConstRACIterator
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: typealias Distance = Int32
|
||||
// CHECK: static func += (lhs: inout ConstRACIterator, v: ConstRACIterator.difference_type)
|
||||
@@ -20,8 +18,8 @@
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct ConstRACIteratorRefPlusEq : UnsafeCxxRandomAccessIterator, UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> ConstRACIterator
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: func successor() -> ConstRACIterator
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: typealias Distance = Int32
|
||||
// CHECK: static func += (lhs: inout ConstRACIteratorRefPlusEq, v: ConstRACIteratorRefPlusEq.difference_type)
|
||||
@@ -30,35 +28,35 @@
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct ConstIteratorOutOfLineEq : UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> ConstIteratorOutOfLineEq
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: func successor() -> ConstIteratorOutOfLineEq
|
||||
// CHECK: }
|
||||
// CHECK: func == (lhs: ConstIteratorOutOfLineEq, rhs: ConstIteratorOutOfLineEq) -> Bool
|
||||
|
||||
// CHECK: struct MinimalIterator : UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> MinimalIterator
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: func successor() -> MinimalIterator
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: static func == (lhs: MinimalIterator, other: MinimalIterator) -> Bool
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct ForwardIterator : UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> ForwardIterator
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: func successor() -> ForwardIterator
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: static func == (lhs: ForwardIterator, other: ForwardIterator) -> Bool
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct HasCustomIteratorTag : UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> HasCustomIteratorTag
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: func successor() -> HasCustomIteratorTag
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: static func == (lhs: HasCustomIteratorTag, other: HasCustomIteratorTag) -> Bool
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct HasCustomRACIteratorTag : UnsafeCxxRandomAccessIterator, UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> HasCustomRACIteratorTag
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: func successor() -> HasCustomRACIteratorTag
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: typealias Distance = Int32
|
||||
// CHECK: static func += (lhs: inout HasCustomRACIteratorTag, x: Int32)
|
||||
@@ -67,8 +65,8 @@
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct HasCustomInheritedRACIteratorTag : UnsafeCxxRandomAccessIterator, UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> HasCustomInheritedRACIteratorTag
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: func successor() -> HasCustomInheritedRACIteratorTag
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: typealias Distance = Int32
|
||||
// CHECK: struct CustomTag0 {
|
||||
@@ -84,22 +82,22 @@
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct HasCustomIteratorTagInline : UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> HasCustomIteratorTagInline
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: func successor() -> HasCustomIteratorTagInline
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: static func == (lhs: HasCustomIteratorTagInline, other: HasCustomIteratorTagInline) -> Bool
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct HasTypedefIteratorTag : UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> HasTypedefIteratorTag
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: func successor() -> HasTypedefIteratorTag
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: static func == (lhs: HasTypedefIteratorTag, other: HasTypedefIteratorTag) -> Bool
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct MutableRACIterator : UnsafeCxxMutableRandomAccessIterator, UnsafeCxxMutableInputIterator {
|
||||
// CHECK: func successor() -> MutableRACIterator
|
||||
// CHECK: var pointee: Int32
|
||||
// CHECK: func successor() -> MutableRACIterator
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: typealias Distance = Int32
|
||||
// CHECK: }
|
||||
@@ -113,21 +111,21 @@
|
||||
// CHECK-NOT: struct HasNoDereferenceOperator : UnsafeCxxInputIterator
|
||||
|
||||
// CHECK: struct TemplatedIterator<CInt> : UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> TemplatedIterator<CInt>
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: func successor() -> TemplatedIterator<CInt>
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: static func == (lhs: TemplatedIterator<CInt>, other: TemplatedIterator<CInt>) -> Bool
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct TemplatedIteratorOutOfLineEq<CInt> : UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> TemplatedIteratorOutOfLineEq<CInt>
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: func successor() -> TemplatedIteratorOutOfLineEq<CInt>
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct TemplatedRACIteratorOutOfLineEq<CInt> : UnsafeCxxRandomAccessIterator, UnsafeCxxInputIterator {
|
||||
// CHECK: func successor() -> TemplatedRACIteratorOutOfLineEq<CInt>
|
||||
// CHECK: var pointee: Int32 { get }
|
||||
// CHECK: func successor() -> TemplatedRACIteratorOutOfLineEq<CInt>
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: typealias Distance = TemplatedRACIteratorOutOfLineEq<CInt>.difference_type
|
||||
// CHECK: }
|
||||
@@ -153,13 +151,13 @@
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct InputOutputIterator : UnsafeCxxMutableInputIterator {
|
||||
// CHECK: func successor() -> InputOutputIterator
|
||||
// CHECK: var pointee: Int32
|
||||
// CHECK: func successor() -> InputOutputIterator
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: }
|
||||
|
||||
// CHECK: struct InputOutputConstIterator : UnsafeCxxMutableInputIterator {
|
||||
// CHECK: func successor() -> InputOutputConstIterator
|
||||
// CHECK: var pointee: Int32 { get nonmutating set }
|
||||
// CHECK: func successor() -> InputOutputConstIterator
|
||||
// CHECK: typealias Pointee = Int32
|
||||
// CHECK: }
|
||||
|
||||
Reference in New Issue
Block a user