Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 7 additions & 14 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6257,9 +6257,9 @@ makeBaseClassMemberAccessors(DeclContext *declContext,

// Use 'address' or 'mutableAddress' accessors for non-copyable
// types, unless the base accessor returns it by value.
bool useAddress = contextTy->isNoncopyable() &&
(baseClassVar->getReadImpl() == ReadImplKind::Stored ||
baseClassVar->getAccessor(AccessorKind::Address));
bool useAddress = baseClassVar->getAccessor(AccessorKind::Address) ||
(contextTy->isNoncopyable() &&
baseClassVar->getReadImpl() == ReadImplKind::Stored);

ParameterList *bodyParams = nullptr;
if (auto subscript = dyn_cast<SubscriptDecl>(baseClassVar)) {
Expand Down Expand Up @@ -6407,12 +6407,6 @@ static ValueDecl *cloneBaseMemberDecl(ValueDecl *decl, DeclContext *newContext,
}

if (auto subscript = dyn_cast<SubscriptDecl>(decl)) {
auto contextTy =
newContext->mapTypeIntoEnvironment(subscript->getElementInterfaceType());
// Subscripts that return non-copyable types are not yet supported.
// See: https://github.com/apple/swift/issues/70047.
if (contextTy->isNoncopyable())
return nullptr;
auto out = SubscriptDecl::create(
subscript->getASTContext(), subscript->getName(), subscript->getStaticLoc(),
subscript->getStaticSpelling(), subscript->getSubscriptLoc(),
Expand Down Expand Up @@ -8349,7 +8343,7 @@ const clang::CXXConstructorDecl *
importer::findCopyConstructor(const clang::CXXRecordDecl *decl) {
for (auto ctor : decl->ctors()) {
if (ctor->isCopyConstructor() &&
// FIXME: Support default arguments (rdar://142414553)
// FIXME: Support default arguments
Copy link
Contributor

Choose a reason for hiding this comment

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

Accidental change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Doug noticed these radars numbers and asked for them to be removed. Since this is a small change, in a file that this patch touches, I thought it would be reasonable to do it here instead of creating a separate patch just for this. Would you prefer me to do it in a separate patch?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's better to keep it out of this PR, since you're not modifying this code part here.

If you'd like to remove there rdar links, could you please file GitHub issues and link to them in the comments instead? That way someone encountering this FIXME and actually fixing it would remember to close the corresponding issue. If there isn't an issue link, the issue (or rdar) probably won't get closed.

ctor->getNumParams() == 1 && ctor->getAccess() == clang::AS_public &&
!ctor->isDeleted() && !ctor->isIneligibleOrNotSelected())
return ctor;
Expand All @@ -8364,9 +8358,8 @@ static bool hasMoveTypeOperations(const clang::CXXRecordDecl *decl) {
return llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
return ctor->isMoveConstructor() && !ctor->isDeleted() &&
!ctor->isIneligibleOrNotSelected() &&
// FIXME: Support default arguments (rdar://142414553)
ctor->getNumParams() == 1 &&
ctor->getAccess() == clang::AS_public;
// FIXME: Support default arguments
Copy link
Contributor

Choose a reason for hiding this comment

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

Accidental change?

ctor->getNumParams() == 1 && ctor->getAccess() == clang::AS_public;
});
}

Expand All @@ -8393,7 +8386,7 @@ static bool
hasConstructorWithUnsupportedDefaultArgs(const clang::CXXRecordDecl *decl) {
return llvm::any_of(decl->ctors(), [](clang::CXXConstructorDecl *ctor) {
return (ctor->isCopyConstructor() || ctor->isMoveConstructor()) &&
// FIXME: Support default arguments (rdar://142414553)
// FIXME: Support default arguments
ctor->getNumParams() != 1;
});
}
Expand Down
53 changes: 38 additions & 15 deletions lib/ClangImporter/SwiftDeclSynthesizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1688,8 +1688,13 @@ synthesizeUnwrappingAddressSetterBody(AbstractFunctionDecl *afd,
ASTContext &ctx = setterDecl->getASTContext();

auto selfArg = createSelfArg(setterDecl);
SmallVector<Expr *> arguments;
for (size_t idx = 0, end = setterDecl->getParameters()->size(); idx < end;
++idx)
arguments.push_back(createParamRefExpr(setterDecl, idx));

auto *setterImplCallExpr =
createAccessorImplCallExpr(setterImpl, selfArg, {});
createAccessorImplCallExpr(setterImpl, selfArg, arguments);

auto *returnStmt = ReturnStmt::createImplicit(ctx, setterImplCallExpr);

Expand All @@ -1708,7 +1713,6 @@ SubscriptDecl *SwiftDeclSynthesizer::makeSubscript(FuncDecl *getter,
FuncDecl *getterImpl = getter ? getter : setter;
FuncDecl *setterImpl = setter;

// FIXME: support unsafeAddress accessors.
// Get the return type wrapped in `Unsafe(Mutable)Pointer<T>`.
const auto rawElementTy = getterImpl->getResultInterfaceType();
// Unwrap `T`. Use rawElementTy for return by value.
Expand Down Expand Up @@ -1737,17 +1741,27 @@ SubscriptDecl *SwiftDeclSynthesizer::makeSubscript(FuncDecl *getter,
getterImpl->getClangNode());
subscript->copyFormalAccessFrom(getterImpl);

AccessorDecl *getterDecl =
AccessorDecl::create(ctx, getterImpl->getLoc(), getterImpl->getLoc(),
AccessorKind::Get, subscript,
/*async*/ false, SourceLoc(),
/*throws*/ false, SourceLoc(),
/*ThrownType=*/TypeLoc(), bodyParams, elementTy, dc);
// Use 'address' or 'mutableAddress' accessors for non-copyable
// types that are returned indirectly.
bool useAddress =
rawElementTy->getAnyPointerElementType() && elementTy->isNoncopyable();

AccessorDecl *getterDecl = AccessorDecl::create(
ctx, getterImpl->getLoc(), getterImpl->getLoc(),
useAddress ? AccessorKind::Address : AccessorKind::Get, subscript,
/*async*/ false, SourceLoc(),
/*throws*/ false, SourceLoc(),
/*ThrownType=*/TypeLoc(), bodyParams,
useAddress ? elementTy->wrapInPointer(PTK_UnsafePointer) : elementTy, dc);
getterDecl->copyFormalAccessFrom(subscript);
getterDecl->setImplicit();
if (!useAddress)
getterDecl->setImplicit();
Comment on lines +1757 to +1758
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we mark this getter as implicit unconditionally?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we mark an Address or MutableAddress accessor as implicit, then in AbstractStorageDecl::getOpaqueAccessor we return nullptr instead of returning the accessor, which causes the compiler to crash. So I think that these kind of accessors are never supposed to be implicit, though I'm not sure what's the reasoning behind this. We also use this trick in SwiftDeclSynthesizer::makeDereferencedPointeeProperty

getterDecl->setIsDynamic(false);
getterDecl->setIsTransparent(true);
getterDecl->setBodySynthesizer(synthesizeUnwrappingGetterBody, getterImpl);
getterDecl->setBodySynthesizer(useAddress
? synthesizeUnwrappingAddressGetterBody
: synthesizeUnwrappingGetterBody,
getterImpl);

if (getterImpl->isMutating()) {
getterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
Expand All @@ -1763,22 +1777,31 @@ SubscriptDecl *SwiftDeclSynthesizer::makeSubscript(FuncDecl *getter,
paramVarDecl->setInterfaceType(elementTy);

SmallVector<ParamDecl *> setterParams;
setterParams.push_back(paramVarDecl);
if (!useAddress)
setterParams.push_back(paramVarDecl);
setterParams.append(bodyParams->begin(), bodyParams->end());

auto setterParamList = ParameterList::create(ctx, setterParams);

setterDecl = AccessorDecl::create(
ctx, setterImpl->getLoc(), setterImpl->getLoc(), AccessorKind::Set,
ctx, setterImpl->getLoc(), setterImpl->getLoc(),
useAddress ? AccessorKind::MutableAddress : AccessorKind::Set,
subscript,
/*async*/ false, SourceLoc(),
/*throws*/ false, SourceLoc(), /*ThrownType=*/TypeLoc(),
setterParamList, TupleType::getEmpty(ctx), dc);
setterParamList,
useAddress ? elementTy->wrapInPointer(PTK_UnsafeMutablePointer)
: TupleType::getEmpty(ctx),
dc);
setterDecl->copyFormalAccessFrom(subscript);
setterDecl->setImplicit();
if (!useAddress)
setterDecl->setImplicit();
Comment on lines +1797 to +1798
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto

setterDecl->setIsDynamic(false);
setterDecl->setIsTransparent(true);
setterDecl->setBodySynthesizer(synthesizeUnwrappingSetterBody, setterImpl);
setterDecl->setBodySynthesizer(useAddress
? synthesizeUnwrappingAddressSetterBody
: synthesizeUnwrappingSetterBody,
setterImpl);

if (setterImpl->isMutating()) {
setterDecl->setSelfAccessKind(SelfAccessKind::Mutating);
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/GenStruct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ namespace {
return nullptr;
for (auto ctor : cxxRecordDecl->ctors()) {
if (ctor->isMoveConstructor() &&
// FIXME: Support default arguments (rdar://142414553)
// FIXME: Support default arguments
ctor->getNumParams() == 1 &&
ctor->getAccess() == clang::AS_public && !ctor->isDeleted() &&
!ctor->isIneligibleOrNotSelected())
Expand Down
24 changes: 23 additions & 1 deletion test/Interop/Cxx/class/noncopyable-irgen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// RUN: %target-swift-frontend -cxx-interoperability-mode=default -emit-ir -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t%{fs-sep}Inputs %t%{fs-sep}test.swift -Xcc -fignore-exceptions -verify -verify-additional-file %t%{fs-sep}Inputs%{fs-sep}noncopyable.h -verify-additional-prefix TEST2- -D TEST2
// RUN: %target-swift-frontend -cxx-interoperability-mode=default -emit-ir -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t%{fs-sep}Inputs %t%{fs-sep}test.swift -Xcc -fignore-exceptions -verify -verify-additional-file %t%{fs-sep}Inputs%{fs-sep}noncopyable.h -verify-additional-prefix TEST3- -D TEST3
// RUN: %target-swift-frontend -cxx-interoperability-mode=default -emit-ir -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t%{fs-sep}Inputs %t%{fs-sep}test.swift -Xcc -fignore-exceptions -verify -verify-additional-file %t%{fs-sep}Inputs%{fs-sep}noncopyable.h -verify-additional-prefix TEST4- -D TEST4 -Xcc -std=c++20
// RUN: %target-swift-frontend -cxx-interoperability-mode=default -emit-ir -I %swift_src_root/lib/ClangImporter/SwiftBridging -I %t%{fs-sep}Inputs %t%{fs-sep}test.swift -Xcc -fignore-exceptions -verify -verify-additional-file %t%{fs-sep}Inputs%{fs-sep}noncopyable.h -verify-additional-prefix TEST5- -D TEST5

//--- Inputs/module.modulemap
module Test {
Expand All @@ -17,8 +18,15 @@ module Test {

struct NonCopyable {
NonCopyable() = default;
NonCopyable(const NonCopyable& other) = delete; // expected-note {{'NonCopyable' has been explicitly marked deleted here}}
NonCopyable(int x) : number(x) {}
NonCopyable(const NonCopyable& other) = delete;
// expected-TEST1-note@-1 {{'NonCopyable' has been explicitly marked deleted here}}
// expected-TEST2-note@-2 {{'NonCopyable' has been explicitly marked deleted here}}
// expected-TEST3-note@-3 {{'NonCopyable' has been explicitly marked deleted here}}
// expected-TEST4-note@-4 {{'NonCopyable' has been explicitly marked deleted here}}
NonCopyable(NonCopyable&& other) = default;

int number = 0;
};

template <typename T>
Expand Down Expand Up @@ -73,6 +81,11 @@ template <typename T> struct Requires {
using RequiresOwnsNonCopyable = Requires<OwnsT<NonCopyable>>;
#endif

struct HasSubscript {
NonCopyable &operator[](int idx) { return nc; }
NonCopyable nc;
};

//--- test.swift
import Test
import CxxStdlib
Expand Down Expand Up @@ -109,4 +122,13 @@ func requires() {
takeCopyable(s)
}

#elseif TEST5
func useSubscript() {
var obj = HasSubscript(nc: NonCopyable(5))
let _ = obj[42] // expected-TEST5-error {{'obj.subscript' is borrowed and cannot be consumed}}
// expected-TEST5-note@-1 {{consumed here}}

func borrow(_ x: borrowing NonCopyable) -> Int32 { return x.number; }
let _ = borrow(obj[42])
}
#endif
14 changes: 14 additions & 0 deletions test/Interop/Cxx/class/noncopyable-typechecker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ module Test {

struct NonCopyable {
NonCopyable() = default;
NonCopyable(int x) : number(x) {}
NonCopyable(const NonCopyable& other) = delete; // expected-note {{'NonCopyable' has been explicitly marked deleted here}}
NonCopyable(NonCopyable&& other) = default;

int number = 0;
};

template <typename T>
Expand Down Expand Up @@ -137,6 +140,11 @@ struct FieldDependsOnMe { // used to trigger a request cycle
OneField<NoFields<FieldDependsOnMe, NonCopyable>> field;
};

struct HasConstSubscript {
const NonCopyable &operator[](int idx) { return nc; }
NonCopyable nc;
};

//--- test.swift
import Test
import CxxStdlib
Expand Down Expand Up @@ -218,3 +226,9 @@ func couldCreateCycleOfCxxValueSemanticsRequests() {
let d3 = FieldDependsOnMe()
takeCopyable(d3) // expected-error {{global function 'takeCopyable' requires that 'FieldDependsOnMe' conform to 'Copyable'}}
}

func useConstSubscript() {
var obj = HasConstSubscript(nc: NonCopyable(5))
_ = obj[42]
obj[42] = NonCopyable(7) // expected-error {{cannot assign through subscript: subscript is get-only}}
}
74 changes: 74 additions & 0 deletions test/Interop/Cxx/class/noncopyable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// RUN: %empty-directory(%t)
// RUN: split-file %s %t
// RUN: %target-build-swift %t%{fs-sep}test.swift -I %t%{fs-sep}Inputs -o %t%{fs-sep}out -cxx-interoperability-mode=default
// RUN: %target-codesign %t%{fs-sep}out
// RUN: %target-run %t%{fs-sep}out
//
// REQUIRES: executable_test

//--- Inputs/module.modulemap
module Test {
header "noncopyable.h"
requires cplusplus
}

//--- Inputs/noncopyable.h
#include <string>

struct NonCopyable {
NonCopyable() = default;
NonCopyable(int x) : number(x) {}
NonCopyable(const NonCopyable& other) = delete;
NonCopyable(NonCopyable&& other) = default;

int number = 0;
};

template<typename T>
struct HasSubscript {
T &operator[](int idx) { return element; }
T element;
};

using HasSubscriptInt = HasSubscript<int>;
using HasSubscriptNonCopyable = HasSubscript<NonCopyable>;

struct InheritsFromHasSubscript : public HasSubscript<NonCopyable> {};

//--- test.swift
import StdlibUnittest
import Test

var NonCopyableTestSuite = TestSuite("NonCopyable")

func borrow(_ x: borrowing NonCopyable) -> Int32 { return x.number; }

NonCopyableTestSuite.test("Use subscript") {
var o1 = HasSubscriptInt(element: 7)
expectEqual(o1[42], 7)

var o2 = HasSubscriptNonCopyable(element: NonCopyable(5))
expectEqual(borrow(o2[42]), 5)

var inherited = InheritsFromHasSubscript()
expectEqual(borrow(inherited[42]), 0)
}

NonCopyableTestSuite.test("Mutate subscript") {
var o1 = HasSubscriptInt(element: 7)
expectEqual(o1[42], 7)
o1[42] = 8
expectEqual(o1[42], 8)

var o2 = HasSubscriptNonCopyable(element: NonCopyable(5))
expectEqual(borrow(o2[42]), 5)
o2[42] = NonCopyable(16)
expectEqual(borrow(o2[42]), 16)

var inherited = InheritsFromHasSubscript()
expectEqual(borrow(inherited[42]), 0)
inherited[42] = NonCopyable(3)
expectEqual(borrow(inherited[42]), 3)
}

runAllTests()
10 changes: 10 additions & 0 deletions test/Interop/Cxx/stdlib/Inputs/std-vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,24 @@ using VectorOfImmortalRefPtr = std::vector<ImmortalRef *>;

struct NonCopyable {
NonCopyable() = default;
NonCopyable(int x) : number(x) {}
NonCopyable(const NonCopyable &other) = delete;
NonCopyable(NonCopyable &&other) = default;
~NonCopyable() {}

int number = 0;
};

using VectorOfNonCopyable = std::vector<NonCopyable>;
using VectorOfPointer = std::vector<NonCopyable *>;

inline VectorOfNonCopyable makeVectorOfNonCopyable() {
VectorOfNonCopyable vec;
vec.emplace_back(1);
vec.emplace_back(2);
return vec;
}

struct HasVector {
std::vector<NonCopyable> field;
};
Expand Down
20 changes: 20 additions & 0 deletions test/Interop/Cxx/stdlib/use-std-vector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -211,4 +211,24 @@ StdVectorTestSuite.test("VectorOfImmortalRefPtr").require(.stdlib_5_8).code {
expectEqual(v[0]?.value, 123)
}

StdVectorTestSuite.test("Subscript of VectorOfNonCopyable") {
var v = makeVectorOfNonCopyable()
expectEqual(v.size(), 2)
expectFalse(v.empty())

func getNumber(_ x: borrowing NonCopyable) -> Int32 {
return x.number
}

expectEqual(getNumber(v[0]), 1)
expectEqual(getNumber(v[1]), 2)

v[0] = NonCopyable(3)
v[1] = NonCopyable(4)

expectEqual(getNumber(v[0]), 3)
expectEqual(getNumber(v[1]), 4)
}


runAllTests()