diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index b3d6be59cc373..8d5c86284e653 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -2647,22 +2647,23 @@ namespace { result->addMember(p); } - for (auto &subscriptInfo : Impl.cxxSubscripts) { - auto declAndParameterType = subscriptInfo.first; - if (declAndParameterType.first != result) - continue; - - auto getterAndSetter = subscriptInfo.second; - auto subscript = synthesizer.makeSubscript(getterAndSetter.first, - getterAndSetter.second); - // Also add subscripts directly because they won't be found from the - // clang decl. - result->addMember(subscript); - - // Add the subscript as an alternative for the getter so that it gets - // carried into derived classes. - auto *subscriptImpl = getterAndSetter.first ? getterAndSetter.first : getterAndSetter.second; - Impl.addAlternateDecl(subscriptImpl, subscript); + auto it = Impl.cxxSubscripts.find(result); + if (it != Impl.cxxSubscripts.end()) { + for (auto &subscriptInfo : it->second) { + auto getterAndSetter = subscriptInfo.second; + auto subscript = synthesizer.makeSubscript(getterAndSetter.first, + getterAndSetter.second); + // Also add subscripts directly because they won't be found from the + // clang decl. + result->addMember(subscript); + + // Add the subscript as an alternative for the getter so that it + // gets carried into derived classes. + auto *subscriptImpl = getterAndSetter.first + ? getterAndSetter.first + : getterAndSetter.second; + Impl.addAlternateDecl(subscriptImpl, subscript); + } } auto getterAndSetterIt = Impl.cxxDereferenceOperators.find(result); @@ -3633,14 +3634,16 @@ namespace { return true; if (importedName.isSubscriptAccessor()) { - assert(func->getParameters()->size() == 1); - auto parameter = func->getParameters()->get(0); - auto parameterType = parameter->getTypeInContext(); - if (!typeDecl || !parameterType) - return false; - if (parameter->isInOut()) - // Subscripts with inout parameters are not allowed in Swift. - return false; + SmallVector params; + for (auto parameter : *(func->getParameters())) { + auto parameterType = parameter->getTypeInContext(); + if (!typeDecl || !parameterType) + return false; + if (parameter->isInOut()) + // Subscripts with inout parameters are not allowed in Swift. + return false; + params.push_back(parameterType.getPointer()); + } // Subscript setter is marked as mutating in Swift even if the // C++ `operator []` is `const`. if (importedName.getAccessorKind() == @@ -3649,7 +3652,8 @@ namespace { !typeDecl->getDeclaredType()->isForeignReferenceType()) func->setSelfAccessKind(SelfAccessKind::Mutating); - auto &getterAndSetter = Impl.cxxSubscripts[{typeDecl, parameterType}]; + auto &getterAndSetterMap = Impl.cxxSubscripts[typeDecl]; + auto &getterAndSetter = getterAndSetterMap[params]; switch (importedName.getAccessorKind()) { case ImportedAccessorKind::SubscriptGetter: diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 086307ae9d11d..f1cdbc3d8867b 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -25,6 +25,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/ForeignErrorConvention.h" +#include "swift/AST/Identifier.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" #include "swift/AST/RequirementSignature.h" @@ -683,8 +684,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// /// `.first` corresponds to a getter /// `.second` corresponds to a setter - llvm::MapVector, - std::pair> cxxSubscripts; + llvm::DenseMap, + std::pair, + std::map, unsigned>>> + cxxSubscripts; llvm::MapVector> cxxDereferenceOperators; diff --git a/lib/ClangImporter/SwiftDeclSynthesizer.cpp b/lib/ClangImporter/SwiftDeclSynthesizer.cpp index 3682d7539241c..61f6bd7a39000 100644 --- a/lib/ClangImporter/SwiftDeclSynthesizer.cpp +++ b/lib/ClangImporter/SwiftDeclSynthesizer.cpp @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -#include "CXXMethodBridging.h" #include "SwiftDeclSynthesizer.h" +#include "CXXMethodBridging.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/Attr.h" #include "swift/AST/AttrKind.h" @@ -47,7 +47,7 @@ static Argument createSelfArg(AccessorDecl *accessorDecl) { static CallExpr *createAccessorImplCallExpr(FuncDecl *accessorImpl, Argument selfArg, - DeclRefExpr *keyRefExpr = nullptr) { + ArrayRef keyRefExprs) { ASTContext &ctx = accessorImpl->getASTContext(); auto accessorImplExpr = @@ -60,12 +60,8 @@ static CallExpr *createAccessorImplCallExpr(FuncDecl *accessorImpl, accessorImplDotCallExpr->setType(accessorImpl->getMethodInterfaceType()); accessorImplDotCallExpr->setThrows(nullptr); - ArgumentList *argList; - if (keyRefExpr) { - argList = ArgumentList::forImplicitUnlabeled(ctx, {keyRefExpr}); - } else { - argList = ArgumentList::forImplicitUnlabeled(ctx, {}); - } + ArgumentList *argList = ArgumentList::forImplicitUnlabeled(ctx, keyRefExprs); + auto *accessorImplCallExpr = CallExpr::createImplicit(ctx, accessorImplDotCallExpr, argList); accessorImplCallExpr->setType(accessorImpl->getResultInterfaceType()); @@ -1556,33 +1552,36 @@ synthesizeUnwrappingGetterOrAddressGetterBody(AbstractFunctionDecl *afd, ASTContext &ctx = getterDecl->getASTContext(); auto selfArg = createSelfArg(getterDecl); - DeclRefExpr *keyRefExpr = getterDecl->getParameters()->size() == 0 - ? nullptr - : createParamRefExpr(getterDecl, 0); + SmallVector arguments; + for (size_t idx = 0, end = getterDecl->getParameters()->size(); idx < end; + ++idx) + arguments.push_back(createParamRefExpr(getterDecl, idx)); Type elementTy = getterDecl->getResultInterfaceType(); auto *getterImplCallExpr = - createAccessorImplCallExpr(getterImpl, selfArg, keyRefExpr); + createAccessorImplCallExpr(getterImpl, selfArg, arguments); // This default handles C++'s operator[] that returns a value type. Expr *propertyExpr = getterImplCallExpr; PointerTypeKind ptrKind; - // The following check returns true if the subscript operator returns a C++ - // reference type. This check actually checks to see if the type is a pointer - // type, but this does not apply to C pointers because they are Optional types - // when imported. TODO: Use a more obvious check here. + // The following check returns true if the subscript operator returns a + // C++ reference type. This check actually checks to see if the type is + // a pointer type, but this does not apply to C pointers because they + // are Optional types when imported. TODO: Use a more obvious check + // here. if (!isAddress && getterImpl->getResultInterfaceType()->getAnyPointerElementType(ptrKind)) { - // `getterImpl` can return either UnsafePointer or UnsafeMutablePointer. - // Retrieve the corresponding `.pointee` declaration. + // `getterImpl` can return either UnsafePointer or + // UnsafeMutablePointer. Retrieve the corresponding `.pointee` + // declaration. VarDecl *pointeePropertyDecl = ctx.getPointerPointeePropertyDecl(ptrKind); // Handle operator[] that returns a reference type. - SubstitutionMap subMap = SubstitutionMap::get( - ctx.getUnsafePointerDecl()->getGenericSignature(), {elementTy}, - LookUpConformanceInModule()); + SubstitutionMap subMap = + SubstitutionMap::get(ctx.getUnsafePointerDecl()->getGenericSignature(), + {elementTy}, LookUpConformanceInModule()); auto pointeePropertyRefExpr = new (ctx) MemberRefExpr( getterImplCallExpr, SourceLoc(), ConcreteDeclRef(pointeePropertyDecl, subMap), DeclNameLoc(), @@ -1627,16 +1626,15 @@ synthesizeUnwrappingSetterBody(AbstractFunctionDecl *afd, void *context) { auto selfArg = createSelfArg(setterDecl); DeclRefExpr *valueParamRefExpr = createParamRefExpr(setterDecl, 0); - // For a subscript this decl will have two parameters, for a pointee property - // it will only have one. - DeclRefExpr *keyParamRefExpr = setterDecl->getParameters()->size() == 1 - ? nullptr - : createParamRefExpr(setterDecl, 1); + SmallVector arguments; + for (size_t idx = 1, end = setterDecl->getParameters()->size(); idx < end; + ++idx) + arguments.push_back(createParamRefExpr(setterDecl, idx)); Type elementTy = valueParamRefExpr->getDecl()->getInterfaceType(); auto *setterImplCallExpr = - createAccessorImplCallExpr(setterImpl, selfArg, keyParamRefExpr); + createAccessorImplCallExpr(setterImpl, selfArg, arguments); VarDecl *pointeePropertyDecl = ctx.getPointerPointeePropertyDecl(PTK_UnsafeMutablePointer); @@ -1673,7 +1671,7 @@ synthesizeUnwrappingAddressSetterBody(AbstractFunctionDecl *afd, auto selfArg = createSelfArg(setterDecl); auto *setterImplCallExpr = - createAccessorImplCallExpr(setterImpl, selfArg, nullptr); + createAccessorImplCallExpr(setterImpl, selfArg, {}); auto *returnStmt = ReturnStmt::createImplicit(ctx, setterImplCallExpr); @@ -1701,15 +1699,16 @@ SubscriptDecl *SwiftDeclSynthesizer::makeSubscript(FuncDecl *getter, auto &ctx = ImporterImpl.SwiftContext; - assert(getterImpl->getParameters()->size() == 1 && - "subscript can only have 1 parameter"); - auto bodyParam = ParamDecl::clone(ctx, getterImpl->getParameters()->get(0)); - // If the subscript parameter is unnamed, give it a name to make sure SILGen - // creates a variable for it. - if (bodyParam->getName().empty()) - bodyParam->setName(ctx.getIdentifier("__index")); - - auto bodyParams = ParameterList::create(ctx, bodyParam); + SmallVector paramVec; + for (auto [i, param] : llvm::enumerate(*getterImpl->getParameters())) { + auto clonedParam = ParamDecl::clone(ctx, param); + // If the subscript parameter is unnamed, give it a name to make sure SILGen + // creates a variable for it. + if (clonedParam->getName().empty()) + clonedParam->setName(ctx.getIdentifier("__index" + std::to_string(i))); + paramVec.push_back(clonedParam); + } + auto bodyParams = ParameterList::create(ctx, paramVec); DeclName name(ctx, DeclBaseName::createSubscript(), bodyParams); auto dc = getterImpl->getDeclContext(); @@ -1744,8 +1743,11 @@ SubscriptDecl *SwiftDeclSynthesizer::makeSubscript(FuncDecl *getter, paramVarDecl->setSpecifier(ParamSpecifier::Default); paramVarDecl->setInterfaceType(elementTy); - auto setterParamList = - ParameterList::create(ctx, {paramVarDecl, bodyParams->get(0)}); + SmallVector setterParams; + 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, @@ -1918,7 +1920,7 @@ synthesizeSuccessorFuncBody(AbstractFunctionDecl *afd, void *context) { // Call `operator++`. auto incrementExpr = createAccessorImplCallExpr( - incrementImpl, Argument::implicitInOut(ctx, copyRefLValueExpr)); + incrementImpl, Argument::implicitInOut(ctx, copyRefLValueExpr), {}); auto copyRefRValueExpr = new (ctx) DeclRefExpr(copyDecl, DeclNameLoc(), /*implicit*/ true); @@ -2331,7 +2333,7 @@ synthesizeComputedGetterFromCXXMethod(AbstractFunctionDecl *afd, auto selfArg = createSelfArg(accessor); - auto *getterImplCallExpr = createAccessorImplCallExpr(method, selfArg); + auto *getterImplCallExpr = createAccessorImplCallExpr(method, selfArg, {}); auto &ctx = method->getASTContext(); auto *returnStmt = ReturnStmt::createImplicit(ctx, getterImplCallExpr); auto *body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc()); @@ -2349,7 +2351,7 @@ synthesizeComputedSetterFromCXXMethod(AbstractFunctionDecl *afd, DeclRefExpr *valueParamRefExpr = createParamRefExpr(setterDecl, 0); auto *getterImplCallExpr = - createAccessorImplCallExpr(setterImpl, selfArg, valueParamRefExpr); + createAccessorImplCallExpr(setterImpl, selfArg, {valueParamRefExpr}); auto body = BraceStmt::create(setterImpl->getASTContext(), SourceLoc(), {getterImplCallExpr}, SourceLoc()); diff --git a/test/Interop/Cxx/operators/Inputs/member-inline.h b/test/Interop/Cxx/operators/Inputs/member-inline.h index ff5a2a41f218d..2ec044e6c6a08 100644 --- a/test/Interop/Cxx/operators/Inputs/member-inline.h +++ b/test/Interop/Cxx/operators/Inputs/member-inline.h @@ -157,6 +157,20 @@ struct ReadWriteIntArray { }; }; +#if __cplusplus >= 202302L +struct NullarySubscript { + int operator[]() const { return 42; } + int &operator[]() { return field; } + int field; +}; + +struct BinarySubscript { + int operator[](int x, int y) const { return x + y; } + int &operator[](int, int) { return field; } + int field; +}; +#endif + struct __attribute__((swift_attr("import_owned"))) ReadOnlyIntArray { private: int values[5] = { 1, 2, 3, 4, 5 }; diff --git a/test/Interop/Cxx/operators/member-inline-module-interface.swift b/test/Interop/Cxx/operators/member-inline-module-interface.swift index a07a2cf864148..8c5338e5b3439 100644 --- a/test/Interop/Cxx/operators/member-inline-module-interface.swift +++ b/test/Interop/Cxx/operators/member-inline-module-interface.swift @@ -1,6 +1,6 @@ -// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -cxx-interoperability-mode=swift-5.9 | %FileCheck %s -// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -cxx-interoperability-mode=swift-6 | %FileCheck %s -// RUN: %target-swift-ide-test -print-module -module-to-print=MemberInline -I %S/Inputs -source-filename=x -cxx-interoperability-mode=upcoming-swift | %FileCheck %s +// 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 // CHECK: struct LoadableIntWrapper { // CHECK: func successor() -> LoadableIntWrapper @@ -58,6 +58,21 @@ // CHECK: } // CHECK: } +// CHECK: struct NullarySubscript { +// CHECK: subscript() -> Int32 +// CHECK: @available(*, unavailable, message: "use subscript") +// CHECK: func __operatorSubscriptConst() -> Int32 +// CHECK: @available(*, unavailable, message: "use subscript") +// CHECK: mutating func __operatorSubscript() -> UnsafeMutablePointer +// CHECK: } + +// CHECK: struct BinarySubscript { +// CHECK: subscript(x: Int32, y: Int32) -> Int32 +// CHECK: @available(*, unavailable, message: "use subscript") +// CHECK: func __operatorSubscriptConst(_ x: Int32, _ y: Int32) -> Int32 +// CHECK: @available(*, unavailable, message: "use subscript") +// CHECK: mutating func __operatorSubscript(_: Int32, _: Int32) -> UnsafeMutablePointer +// CHECK: } // CHECK: struct ReadOnlyIntArray { // CHECK: subscript(x: Int32) -> Int32 { get } @@ -212,10 +227,10 @@ // CHECK: } // CHECK: struct SubscriptUnnamedParameter { -// CHECK: subscript(__index: Int32) -> Int32 { get } +// CHECK: subscript(__index0: Int32) -> Int32 { get } // CHECK: } // CHECK: struct SubscriptUnnamedParameterReadWrite { -// CHECK: subscript(__index: Int32) -> Int32 +// CHECK: subscript(__index0: Int32) -> Int32 // CHECK: } // CHECK: struct Iterator { diff --git a/test/Interop/Cxx/operators/member-inline.swift b/test/Interop/Cxx/operators/member-inline.swift index 0adc43a2b6377..3212114e22218 100644 --- a/test/Interop/Cxx/operators/member-inline.swift +++ b/test/Interop/Cxx/operators/member-inline.swift @@ -1,6 +1,6 @@ // RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -cxx-interoperability-mode=swift-5.9) // RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -cxx-interoperability-mode=swift-6) -// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -cxx-interoperability-mode=upcoming-swift) +// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -cxx-interoperability-mode=upcoming-swift -Xcc -std=c++23 -D CPP23) // // REQUIRES: executable_test @@ -185,6 +185,20 @@ OperatorsTestSuite.test("ReadWriteIntArray.subscript (inline)") { expectEqual(234, resultAfter) } +#if CPP23 +OperatorsTestSuite.test("Subscript operators with parameter number != 1") { + var ns = NullarySubscript() + expectEqual(42, ns[]) + ns[] = 5 + expectEqual(5, ns.field) + + var bs = BinarySubscript() + expectEqual(10, bs[3, 7]) + bs[1, 2] = 6 + expectEqual(6, bs.field) +} +#endif + OperatorsTestSuite.test("DerivedFromReadWriteIntArray.subscript (inline, base class)") { var arr = DerivedFromReadWriteIntArray()