Skip to content

Commit 84e7f82

Browse files
committed
Reapply "[ConstraintSystem] C++ Interop: Binding a string literal to std.string shouldn't increase the score"
This reverts commit 6852bc9. In addition to the original change, this makes sure that C++ `std::string` and Swift `String` are given distinct score, in order to prevent ambiguity which was causing build failures in some projects. rdar://158439395
1 parent 51c1358 commit 84e7f82

File tree

6 files changed

+88
-4
lines changed

6 files changed

+88
-4
lines changed

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ IDENTIFIER(Any)
3535
IDENTIFIER(ArrayLiteralElement)
3636
IDENTIFIER(asLocalActor)
3737
IDENTIFIER(atIndexedSubscript)
38+
IDENTIFIER(basic_string)
3839
IDENTIFIER_(bridgeToObjectiveC)
3940
IDENTIFIER(buildArray)
4041
IDENTIFIER(buildBlock)

include/swift/AST/Types.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,6 +1112,9 @@ class alignas(1 << TypeAlignInBits) TypeBase
11121112
/// Check if this is a ObjCBool type from the Objective-C module.
11131113
bool isObjCBool();
11141114

1115+
/// Check if this is a std.string type from C++.
1116+
bool isCxxString();
1117+
11151118
/// Check if this is the type Unicode.Scalar from the Swift standard library.
11161119
bool isUnicodeScalar();
11171120

lib/AST/Type.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "swift/AST/Types.h"
4444
#include "swift/Basic/Assertions.h"
4545
#include "swift/Basic/Compiler.h"
46+
#include "clang/AST/DeclCXX.h"
4647
#include "llvm/ADT/APFloat.h"
4748
#include "llvm/ADT/STLExtras.h"
4849
#include "llvm/ADT/SmallPtrSet.h"
@@ -1302,6 +1303,21 @@ bool TypeBase::isObjCBool() {
13021303
return module->getName().is("ObjectiveC") && NTD->getName().is("ObjCBool");
13031304
}
13041305

1306+
bool TypeBase::isCxxString() {
1307+
auto *nominal = getAnyNominal();
1308+
if (!nominal)
1309+
return false;
1310+
1311+
auto *clangDecl =
1312+
dyn_cast_or_null<clang::CXXRecordDecl>(nominal->getClangDecl());
1313+
if (!clangDecl)
1314+
return false;
1315+
1316+
auto &ctx = nominal->getASTContext();
1317+
return clangDecl->isInStdNamespace() && clangDecl->getIdentifier() &&
1318+
ctx.Id_basic_string.is(clangDecl->getName());
1319+
}
1320+
13051321
bool TypeBase::isUnicodeScalar() {
13061322
if (!is<StructType>())
13071323
return false;

lib/Sema/ConstraintSystem.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,24 @@ void ConstraintSystem::assignFixedType(TypeVariableType *typeVar, Type type,
257257

258258
// If the protocol has a default type, check it.
259259
if (auto defaultType = TypeChecker::getDefaultType(literalProtocol, DC)) {
260-
// Check whether the nominal types match. This makes sure that we
261-
// properly handle Array vs. Array<T>.
262-
if (defaultType->getAnyNominal() != type->getAnyNominal()) {
263-
increaseScore(SK_NonDefaultLiteral, locator);
260+
auto isDefaultType = [&defaultType](Type type) {
261+
// Check whether the nominal types match. This makes sure that we
262+
// properly handle Array vs. Array<T>.
263+
return defaultType->getAnyNominal() == type->getAnyNominal();
264+
};
265+
266+
if (!isDefaultType(type)) {
267+
// Treat `std.string` as a default type just like we do
268+
// Swift standard library `String`. This helps to disambiguate
269+
// operator overloads that use `std.string` vs. a custom C++
270+
// type that conforms to `ExpressibleByStringLiteral` as well.
271+
bool isCxxDefaultType =
272+
literalProtocol->isSpecificProtocol(
273+
KnownProtocolKind::ExpressibleByStringLiteral) &&
274+
type->isCxxString();
275+
276+
increaseScore(SK_NonDefaultLiteral, locator,
277+
isCxxDefaultType ? 1 : 2);
264278
}
265279
}
266280

test/Interop/Cxx/stdlib/Inputs/std-string.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,19 @@ struct HasMethodThatReturnsString {
66
};
77

88
inline std::string takesStringWithDefaultArg(std::string s = "abc") { return s; }
9+
10+
struct StringBox {
11+
std::string value;
12+
13+
friend bool operator==(const StringBox &lhs, const std::string &rhs) {
14+
return lhs.value == rhs;
15+
}
16+
17+
friend bool operator==(const std::string &lhs, const StringBox &rhs) {
18+
return rhs == lhs;
19+
}
20+
21+
StringBox operator+(const StringBox &rhs) const {
22+
return {value + rhs.value};
23+
}
24+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %target-swift-emit-silgen -verify -I %S/Inputs -cxx-interoperability-mode=upcoming-swift %s | %FileCheck %s
2+
3+
import CxxStdlib
4+
import StdString
5+
6+
extension StringBox: @retroactive ExpressibleByStringLiteral {
7+
public init(stringLiteral value: String) {
8+
self.value = std.string(value)
9+
}
10+
}
11+
12+
func takesAny(_: Any) {}
13+
14+
// CHECK-LABEL: sil hidden [ossa] @$s4main4testyyF
15+
// CHECK: // function_ref static std{{.*}}basic_string<CChar, std{{.*}}char_traits<CChar>, std{{.*}}allocator<CChar>>.== infix(_:_:)
16+
// CHECK: // function_ref static std{{.*}}basic_string<CChar, std{{.*}}char_traits<CChar>, std{{.*}}allocator<CChar>>.== infix(_:_:)
17+
// CHECK: // function_ref static String.== infix(_:_:)
18+
// CHECK: // function_ref static String.+ infix(_:_:)
19+
// CHECK: // function_ref static StringBox.+ infix(_:_:)
20+
// CHECK-NEXT: %{{.*}} = function_ref @$sSo9StringBoxV1poiyA2B_ABtFZ
21+
// CHECK: // function_ref takesAny(_:)
22+
// CHECK-NEXT: %{{.*}} = function_ref @$s4main8takesAnyyyypF
23+
// CHECK: } // end sil function '$s4main4testyyF'
24+
func test() {
25+
let cxxString: std.string = ""
26+
let _ = cxxString == "def" // Ok
27+
let _ = "def" == cxxString // Ok
28+
let _ = "def" == "hello" // Ok
29+
30+
takesAny("abc" + "def")
31+
takesAny("abc" + "def" + "xyz")
32+
takesAny(StringBox("abc") + "def" + "xyz")
33+
takesAny("abc" + StringBox("def") + "xyz")
34+
}

0 commit comments

Comments
 (0)