Skip to content

Commit 7fef830

Browse files
committed
[cxx-interop] Add CxxStack protocol for std::stack ergonomics.
1 parent 2c335ee commit 7fef830

File tree

11 files changed

+192
-0
lines changed

11 files changed

+192
-0
lines changed

include/swift/AST/KnownProtocols.def

+1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ PROTOCOL(CxxSet)
137137
PROTOCOL(CxxRandomAccessCollection)
138138
PROTOCOL(CxxMutableRandomAccessCollection)
139139
PROTOCOL(CxxSequence)
140+
PROTOCOL(CxxStack)
140141
PROTOCOL(CxxUniqueSet)
141142
PROTOCOL(CxxVector)
142143
PROTOCOL(CxxSpan)

lib/AST/ASTContext.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1475,6 +1475,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
14751475
case KnownProtocolKind::CxxMutableRandomAccessCollection:
14761476
case KnownProtocolKind::CxxSet:
14771477
case KnownProtocolKind::CxxSequence:
1478+
case KnownProtocolKind::CxxStack:
14781479
case KnownProtocolKind::CxxUniqueSet:
14791480
case KnownProtocolKind::CxxVector:
14801481
case KnownProtocolKind::CxxSpan:

lib/ClangImporter/ClangDerivedConformances.cpp

+39
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,45 @@ void swift::conformToCxxSequenceIfNeeded(
865865
}
866866
}
867867

868+
void swift::conformToCxxStackIfNeeded(
869+
ClangImporter::Implementation &impl, NominalTypeDecl *decl,
870+
const clang::CXXRecordDecl *clangDecl) {
871+
PrettyStackTraceDecl trace("conforming to CxxStack", decl);
872+
assert(decl);
873+
assert(clangDecl);
874+
ASTContext &ctx = decl->getASTContext();
875+
if (!isStdDecl(clangDecl, {"stack"}))
876+
return;
877+
auto valueType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
878+
decl, ctx.getIdentifier("value_type"));
879+
auto sizeType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
880+
decl, ctx.getIdentifier("size_type"));
881+
if (!valueType ||!sizeType)
882+
return;
883+
impl.addSynthesizedTypealias(decl, ctx.Id_Element,
884+
valueType->getUnderlyingType());
885+
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Size"),
886+
sizeType->getUnderlyingType());
887+
888+
ProtocolDecl *cxxInputIteratorProto =
889+
ctx.getProtocol(KnownProtocolKind::UnsafeCxxInputIterator);
890+
if (!cxxInputIteratorProto)
891+
return;
892+
893+
auto rawReferenceType = lookupDirectSingleWithoutExtensions<TypeAliasDecl>(
894+
decl, ctx.getIdentifier("const_reference"));
895+
if (!rawReferenceType)
896+
return;
897+
898+
auto rawReferenceTy = rawReferenceType->getUnderlyingType();
899+
900+
if (!checkConformance(rawReferenceTy, cxxInputIteratorProto))
901+
return;
902+
903+
impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawReference"), rawReferenceTy);
904+
impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxStack});
905+
}
906+
868907
static bool isStdSetType(const clang::CXXRecordDecl *clangDecl) {
869908
return isStdDecl(clangDecl, {"set", "unordered_set", "multiset"});
870909
}

lib/ClangImporter/ClangDerivedConformances.h

+6
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ void conformToCxxSequenceIfNeeded(ClangImporter::Implementation &impl,
4646
NominalTypeDecl *decl,
4747
const clang::CXXRecordDecl *clangDecl);
4848

49+
/// If the decl is an instantiation of C++ `std::stack`,
50+
/// synthesize a conformance to CxxStack, which is defined in the Cxx module.
51+
void conformToCxxStackIfNeeded(ClangImporter::Implementation &impl,
52+
NominalTypeDecl *decl,
53+
const clang::CXXRecordDecl *clangDecl);
54+
4955
/// If the decl is an instantiation of C++ `std::set`, `std::unordered_set` or
5056
/// `std::multiset`, synthesize a conformance to CxxSet, which is defined in the
5157
/// Cxx module.

lib/ClangImporter/ImportDecl.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -3097,6 +3097,7 @@ namespace {
30973097
conformToCxxVectorIfNeeded(Impl, nominalDecl, decl);
30983098
conformToCxxFunctionIfNeeded(Impl, nominalDecl, decl);
30993099
conformToCxxSpanIfNeeded(Impl, nominalDecl, decl);
3100+
conformToCxxStackIfNeeded(Impl, nominalDecl, decl);
31003101
}
31013102
}
31023103

lib/IRGen/GenMeta.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -7133,6 +7133,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
71337133
case KnownProtocolKind::CxxMutableRandomAccessCollection:
71347134
case KnownProtocolKind::CxxSet:
71357135
case KnownProtocolKind::CxxSequence:
7136+
case KnownProtocolKind::CxxStack:
71367137
case KnownProtocolKind::CxxUniqueSet:
71377138
case KnownProtocolKind::CxxVector:
71387139
case KnownProtocolKind::CxxSpan:

stdlib/public/Cxx/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ add_swift_target_library(swiftCxx STATIC NO_LINK_NAME IS_STDLIB IS_SWIFT_ONLY
1515
CxxSet.swift
1616
CxxRandomAccessCollection.swift
1717
CxxSequence.swift
18+
CxxStack.swift
1819
CxxVector.swift
1920
CxxSpan.swift
2021
UnsafeCxxIterators.swift

stdlib/public/Cxx/CxxStack.swift

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
public protocol CxxStack<Element> {
14+
associatedtype Element
15+
associatedtype Size: BinaryInteger
16+
associatedtype RawReference: UnsafeCxxInputIterator
17+
where RawReference.Pointee == Element
18+
19+
func empty() -> Bool
20+
func size() -> Size
21+
22+
func __topUnsafe() -> RawReference
23+
24+
mutating func push(_ element: Element)
25+
mutating func pop()
26+
}
27+
28+
public extension CxxStack {
29+
@inlinable var count: Size { size() }
30+
@inlinable var isEmpty: Bool { empty() }
31+
32+
@inlinable
33+
func top() -> Element {
34+
precondition(!empty(), "stack is empty")
35+
return __topUnsafe().pointee
36+
}
37+
}

test/Interop/Cxx/stdlib/Inputs/module.modulemap

+6
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ module StdStringView {
5252
export *
5353
}
5454

55+
module StdStack {
56+
header "std-stack.h"
57+
requires cplusplus
58+
export *
59+
}
60+
5561
module StdPair {
5662
header "std-pair.h"
5763
requires cplusplus
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#ifndef TEST_INTEROP_CXX_STDLIB_INPUTS_STD_STACK_H
2+
#define TEST_INTEROP_CXX_STDLIB_INPUTS_STD_STACK_H
3+
4+
#include <stack>
5+
6+
using StackOfInt = std::stack<int>;
7+
8+
StackOfInt initStackOfCInt() {
9+
StackOfInt s;
10+
s.push(1);
11+
s.push(2);
12+
s.push(3);
13+
return s;
14+
}
15+
16+
StackOfInt initEmtypeStackOfCInt() {
17+
StackOfInt s;
18+
return s;
19+
}
20+
21+
#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_STACK_H
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-experimental-cxx-interop)
2+
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=swift-6)
3+
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=upcoming-swift)
4+
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=upcoming-swift -Xcc -std=c++14)
5+
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=upcoming-swift -Xcc -std=c++17)
6+
// RUN: %target-run-simple-swift(-I %S/Inputs -cxx-interoperability-mode=upcoming-swift -Xcc -std=c++20)
7+
8+
// Also test this with a bridging header instead of the StdSet module.
9+
// RUN: %empty-directory(%t2)
10+
// RUN: cp %S/Inputs/std-stack.h %t2/std-stack-bridging-header.h
11+
// RUN: %target-run-simple-swift(-D BRIDGING_HEADER -import-objc-header %t2/std-stack-bridging-header.h -Xfrontend -enable-experimental-cxx-interop)
12+
// RUN: %target-run-simple-swift(-D BRIDGING_HEADER -import-objc-header %t2/std-stack-bridging-header.h -cxx-interoperability-mode=swift-6)
13+
// RUN: %target-run-simple-swift(-D BRIDGING_HEADER -import-objc-header %t2/std-stack-bridging-header.h -cxx-interoperability-mode=upcoming-swift)
14+
15+
// REQUIRES: executable_test
16+
//
17+
// Enable this everywhere once we have a solution for modularizing other C++ stdlibs: rdar://87654514
18+
// REQUIRES: OS=macosx || OS=linux-gnu
19+
20+
import StdlibUnittest
21+
#if !BRIDGING_HEADER
22+
import StdStack
23+
#endif
24+
import CxxStdlib
25+
26+
var StdStackTestSuite = TestSuite("StdStack")
27+
28+
StdStackTestSuite.test("stack count") {
29+
let s1 = initStackOfCInt()
30+
expectEqual(s1.count, 3)
31+
32+
let s2 = initEmtypeStackOfCInt()
33+
expectEqual(s2.count, 0)
34+
}
35+
36+
StdStackTestSuite.test("stack isEmpty") {
37+
let s1 = initStackOfCInt()
38+
expectFalse(s1.isEmpty)
39+
40+
let s2 = initEmtypeStackOfCInt()
41+
expectTrue(s2.isEmpty)
42+
}
43+
44+
StdStackTestSuite.test("stack top") {
45+
let s1 = initStackOfCInt()
46+
expectEqual(s1.top(), 3)
47+
}
48+
49+
StdStackTestSuite.test("stack push") {
50+
var s1 = initStackOfCInt()
51+
52+
s1.push(4)
53+
expectEqual(s1.top(), 4)
54+
expectEqual(s1.count, 4)
55+
56+
var s2 = initEmtypeStackOfCInt()
57+
58+
s2.push(4)
59+
expectEqual(s2.top(), 4)
60+
expectEqual(s2.count, 1)
61+
}
62+
63+
StdStackTestSuite.test("stack pop") {
64+
var s1 = initStackOfCInt()
65+
66+
expectEqual(s1.top(), 3)
67+
68+
s1.pop()
69+
expectEqual(s1.top(), 2)
70+
71+
s1.pop()
72+
expectEqual(s1.top(), 1)
73+
74+
s1.pop()
75+
expectTrue(s1.isEmpty)
76+
}
77+
78+
runAllTests()

0 commit comments

Comments
 (0)