Skip to content

[cxx-interop] Add conversion to Bool for types that define operator bool() #68578

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 5, 2023
Merged
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
1 change: 1 addition & 0 deletions include/swift/AST/KnownProtocols.def
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ PROTOCOL(DistributedTargetInvocationDecoder)
PROTOCOL(DistributedTargetInvocationResultHandler)

// C++ Standard Library Overlay:
PROTOCOL(CxxConvertibleToBool)
PROTOCOL(CxxConvertibleToCollection)
PROTOCOL(CxxDictionary)
PROTOCOL(CxxOptional)
Expand Down
1 change: 1 addition & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1131,6 +1131,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
case KnownProtocolKind::DistributedTargetInvocationResultHandler:
M = getLoadedModule(Id_Distributed);
break;
case KnownProtocolKind::CxxConvertibleToBool:
case KnownProtocolKind::CxxConvertibleToCollection:
case KnownProtocolKind::CxxDictionary:
case KnownProtocolKind::CxxPair:
Expand Down
33 changes: 33 additions & 0 deletions lib/ClangImporter/ClangDerivedConformances.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,39 @@ void swift::conformToCxxIteratorIfNeeded(
decl, {KnownProtocolKind::UnsafeCxxRandomAccessIterator});
}

void swift::conformToCxxConvertibleToBoolIfNeeded(
ClangImporter::Implementation &impl, swift::NominalTypeDecl *decl,
const clang::CXXRecordDecl *clangDecl) {
PrettyStackTraceDecl trace("conforming to CxxConvertibleToBool", decl);

assert(decl);
assert(clangDecl);
ASTContext &ctx = decl->getASTContext();

auto conversionId = ctx.getIdentifier("__convertToBool");
auto conversions = lookupDirectWithoutExtensions(decl, conversionId);

// Find a non-mutating overload of `__convertToBool`.
FuncDecl *conversion = nullptr;
for (auto c : conversions) {
auto candidate = dyn_cast<FuncDecl>(c);
if (!candidate || candidate->isMutating())
continue;
if (conversion)
// Overload ambiguity?
return;
conversion = candidate;
}
if (!conversion)
return;
auto conversionTy = conversion->getResultInterfaceType();
if (!conversionTy->isBool())
return;

impl.addSynthesizedProtocolAttrs(decl,
{KnownProtocolKind::CxxConvertibleToBool});
}

void swift::conformToCxxOptionalIfNeeded(
ClangImporter::Implementation &impl, NominalTypeDecl *decl,
const clang::CXXRecordDecl *clangDecl) {
Expand Down
6 changes: 6 additions & 0 deletions lib/ClangImporter/ClangDerivedConformances.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ void conformToCxxIteratorIfNeeded(ClangImporter::Implementation &impl,
NominalTypeDecl *decl,
const clang::CXXRecordDecl *clangDecl);

/// If the decl defines `operator bool()`, synthesize a conformance to the
/// CxxConvertibleToBool protocol, which is defined in the Cxx module.
void conformToCxxConvertibleToBoolIfNeeded(
ClangImporter::Implementation &impl, NominalTypeDecl *decl,
const clang::CXXRecordDecl *clangDecl);

/// If the decl is an instantiation of C++ `std::optional`, synthesize a
/// conformance to CxxOptional protocol, which is defined in the Cxx module.
void conformToCxxOptionalIfNeeded(ClangImporter::Implementation &impl,
Expand Down
1 change: 1 addition & 0 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2818,6 +2818,7 @@ namespace {
auto nominalDecl = cast<NominalTypeDecl>(result);
conformToCxxIteratorIfNeeded(Impl, nominalDecl, decl);
conformToCxxSequenceIfNeeded(Impl, nominalDecl, decl);
conformToCxxConvertibleToBoolIfNeeded(Impl, nominalDecl, decl);
conformToCxxSetIfNeeded(Impl, nominalDecl, decl);
conformToCxxDictionaryIfNeeded(Impl, nominalDecl, decl);
conformToCxxPairIfNeeded(Impl, nominalDecl, decl);
Expand Down
1 change: 1 addition & 0 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6519,6 +6519,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
case KnownProtocolKind::DistributedTargetInvocationEncoder:
case KnownProtocolKind::DistributedTargetInvocationDecoder:
case KnownProtocolKind::DistributedTargetInvocationResultHandler:
case KnownProtocolKind::CxxConvertibleToBool:
case KnownProtocolKind::CxxConvertibleToCollection:
case KnownProtocolKind::CxxDictionary:
case KnownProtocolKind::CxxPair:
Expand Down
1 change: 1 addition & 0 deletions stdlib/public/Cxx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ if(SWIFT_STDLIB_SUPPORT_BACK_DEPLOYMENT)
endif()

add_swift_target_library(swiftCxx STATIC NO_LINK_NAME IS_STDLIB IS_SWIFT_ONLY IS_FRAGILE
CxxConvertibleToBool.swift
CxxConvertibleToCollection.swift
CxxDictionary.swift
CxxPair.swift
Expand Down
26 changes: 26 additions & 0 deletions stdlib/public/Cxx/CxxConvertibleToBool.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

/// A C++ type that can be converted to a Boolean value.
///
/// Any C++ type that defines `operator bool()` conforms to this protocol.
public protocol CxxConvertibleToBool {
/// Do not implement this function manually in Swift.
func __convertToBool() -> Bool
}

extension Bool {
@inlinable
public init<B: CxxConvertibleToBool>(fromCxx convertible: __shared B) {
self = convertible.__convertToBool()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// CHECK: mutating func callAsFunction(_ x: Int32, _ y: Int32) -> Int32
// CHECK: }

// CHECK: struct LoadableBoolWrapper {
// CHECK: struct LoadableBoolWrapper
// CHECK: prefix static func ! (lhs: inout LoadableBoolWrapper) -> LoadableBoolWrapper
// CHECK: func __convertToBool() -> Bool
// CHECK: }
Expand Down
18 changes: 18 additions & 0 deletions test/Interop/Cxx/stdlib/overlay/Inputs/convertible-to-bool.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
struct BoolBox {
bool value;

operator bool() const { return value; }
};

struct NonConstBoolBox {
bool value;

operator bool() { return value; }
};

struct DualOverloadBoolBox {
bool value;

operator bool() const { return value; }
operator bool() { return value; }
};
5 changes: 5 additions & 0 deletions test/Interop/Cxx/stdlib/overlay/Inputs/module.modulemap
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
module ConvertibleToBool {
header "convertible-to-bool.h"
requires cplusplus
}

module CustomSequence {
header "custom-iterator.h" // TODO: extract into another module
header "custom-sequence.h"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// RUN: %target-swift-ide-test -print-module -module-to-print=ConvertibleToBool -source-filename=x -I %S/Inputs -enable-experimental-cxx-interop | %FileCheck %s

// CHECK: struct BoolBox : CxxConvertibleToBool {
// CHECK: }

// CHECK: struct NonConstBoolBox {
// CHECK: }

// CHECK: struct DualOverloadBoolBox : CxxConvertibleToBool {
// CHECK: }
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// RUN: %target-typecheck-verify-swift -I %S/Inputs -enable-experimental-cxx-interop

import ConvertibleToBool

let _ = Bool(fromCxx: BoolBox())
let _ = Bool(fromCxx: NonConstBoolBox()) // expected-error {{initializer 'init(fromCxx:)' requires that 'NonConstBoolBox' conform to 'CxxConvertibleToBool'}}
let _ = Bool(fromCxx: DualOverloadBoolBox())
26 changes: 26 additions & 0 deletions test/Interop/Cxx/stdlib/overlay/convertible-to-bool.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-experimental-cxx-interop)

// REQUIRES: executable_test

import StdlibUnittest
import ConvertibleToBool

var CxxConvertibleToBoolTestSuite = TestSuite("CxxConvertibleToBool")

CxxConvertibleToBoolTestSuite.test("BoolBox as CxxConvertibleToBool") {
let b1 = BoolBox(value: true)
expectTrue(Bool(fromCxx: b1))

let b2 = BoolBox(value: false)
expectFalse(Bool(fromCxx: b2))
}

CxxConvertibleToBoolTestSuite.test("DualOverloadBoolBox as CxxConvertibleToBool") {
let b1 = DualOverloadBoolBox(value: true)
expectTrue(Bool(fromCxx: b1))

let b2 = DualOverloadBoolBox(value: false)
expectFalse(Bool(fromCxx: b2))
}

runAllTests()