Skip to content

Commit 86b123a

Browse files
[Sema] Diagnose type error on Clang type mismatch.
Fixes rdar://71904525.
1 parent f9570b2 commit 86b123a

File tree

4 files changed

+101
-12
lines changed

4 files changed

+101
-12
lines changed

include/swift/Sema/ConstraintSystem.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,17 @@ enum class ConstraintSystemFlags {
13751375
/// CodeCompletionExpr, and should continue in the presence of errors wherever
13761376
/// possible.
13771377
ForCodeCompletion = 0x40,
1378+
1379+
/// Include Clang function types when checking equality for function types.
1380+
///
1381+
/// When LangOptions.UseClangFunctionTypes is set, we can synthesize
1382+
/// different @convention(c) function types with the same parameter and result
1383+
/// types (similarly for blocks). This difference is reflected in the `cType:`
1384+
/// field (@convention(c, cType: ...)). When the cType is different, the types
1385+
/// should be treated as semantically different, as they may have different
1386+
/// calling conventions, say due to Clang attributes such as
1387+
/// `__attribute__((ns_consumed))`.
1388+
UseClangFunctionTypes = 0x80,
13781389
};
13791390

13801391
/// Options that affect the constraint system as a whole.

lib/Sema/CSSimplify.cpp

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,19 +1557,38 @@ isSubtypeOf(FunctionTypeRepresentation potentialSubRepr,
15571557
|| isThickRepresentation(potentialSuperRepr);
15581558
}
15591559

1560-
/// Returns true if `constraint rep1 rep2` is satisfied.
1561-
static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1,
1562-
FunctionTypeRepresentation rep2,
1563-
ConstraintKind kind) {
1560+
/// Returns true if `constraint extInfo1 extInfo2` is satisfied.
1561+
static bool matchFunctionRepresentations(FunctionType::ExtInfo einfo1,
1562+
FunctionType::ExtInfo einfo2,
1563+
ConstraintKind kind,
1564+
ConstraintSystemOptions options) {
1565+
auto rep1 = einfo1.getRepresentation();
1566+
auto rep2 = einfo2.getRepresentation();
1567+
bool clangTypeMismatch =
1568+
(options.contains(ConstraintSystemFlags::UseClangFunctionTypes) &&
1569+
(einfo1.getClangTypeInfo() != einfo2.getClangTypeInfo()));
15641570
switch (kind) {
15651571
case ConstraintKind::Bind:
15661572
case ConstraintKind::BindParam:
15671573
case ConstraintKind::BindToPointerType:
15681574
case ConstraintKind::Equal:
1569-
return rep1 == rep2;
1570-
1575+
return (rep1 == rep2) && !clangTypeMismatch;
1576+
15711577
case ConstraintKind::Subtype: {
1572-
return isSubtypeOf(rep1, rep2);
1578+
// Breakdown of cases:
1579+
// 1. isSubtypeOf(rep1, rep2) == false (hence rep1 != rep2):
1580+
// In this case, this function will return false, indicating that we
1581+
// can't convert. E.g. you can't convert from @convention(swift) to
1582+
// @convention(c).
1583+
// 2. isSubtypeOf(rep1, rep2) == true and rep1 != rep2:
1584+
// In this case, this function will return true, indicating that we
1585+
// can convert, because the Clang type doesn't matter when converting
1586+
// between different representations. E.g. it is okay to convert from
1587+
// @convention(c) (regardless of cType) to @convention(swift).
1588+
// 3. isSubtypeOf(rep1, rep2) == true and rep1 == rep2:
1589+
// In this case, the function returns !clangTypeMismatch, as we forbid
1590+
// conversions between @convention(c) functions with different cTypes.
1591+
return isSubtypeOf(rep1, rep2) && ((rep1 != rep2) || !clangTypeMismatch);
15731592
}
15741593

15751594
// [NOTE: diagnose-swift-to-c-convention-change]: @convention(swift) ->
@@ -1588,6 +1607,19 @@ static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1,
15881607
case ConstraintKind::Conversion:
15891608
case ConstraintKind::ArgumentConversion:
15901609
case ConstraintKind::OperatorArgumentConversion:
1610+
// For now, forbid conversion if representations match but cTypes differ.
1611+
//
1612+
// let f : @convention(c, cType: "id (*)(void) __attribute__((ns_returns_retained))")
1613+
// () -> AnyObject = ...
1614+
// let _ : @convention(c, cType: "id (*)(void)")
1615+
// () -> AnyObject = f // error
1616+
// let g : @convention(c, cType: "void (*)(void *)")
1617+
// (OpaquePointer?) -> () = ...
1618+
// let _ : @convention(c, cType: "void (*)(MyCtx *)")
1619+
// (OpaquePointer?) -> () = g // error
1620+
if ((rep1 == rep2) && clangTypeMismatch) {
1621+
return false;
1622+
}
15911623
return true;
15921624

15931625
case ConstraintKind::OpaqueUnderlyingType:
@@ -1908,9 +1940,8 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
19081940
return getTypeMatchFailure(locator);
19091941
}
19101942

1911-
if (!matchFunctionRepresentations(func1->getExtInfo().getRepresentation(),
1912-
func2->getExtInfo().getRepresentation(),
1913-
kind)) {
1943+
if (!matchFunctionRepresentations(func1->getExtInfo(), func2->getExtInfo(),
1944+
kind, Options)) {
19141945
return getTypeMatchFailure(locator);
19151946
}
19161947

lib/Sema/ConstraintSystem.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ ConstraintSystem::ConstraintSystem(DeclContext *dc,
8686
DC->getParentModule()->isMainModule()) {
8787
Options |= ConstraintSystemFlags::DebugConstraints;
8888
}
89+
if (Context.LangOpts.UseClangFunctionTypes)
90+
Options |= ConstraintSystemFlags::UseClangFunctionTypes;
8991
}
9092

9193
ConstraintSystem::~ConstraintSystem() {
Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,50 @@
1-
// RUN: not --crash %target-typecheck-verify-swift -sdk %clang-importer-sdk -experimental-print-full-convention -use-clang-function-types
1+
// RUN: %target-typecheck-verify-swift -sdk %clang-importer-sdk -experimental-print-full-convention -use-clang-function-types
22

33
import ctypes
44

5-
let _error : (@convention(c, cType: "(void *) (*)(void *)") (Int) -> Int)? = getFunctionPointer_()
5+
// Setting a C function type with the correct cType works.
6+
let f1 : (@convention(c, cType: "size_t (*)(size_t)") (Int) -> Int)? = getFunctionPointer_()
7+
8+
// However, trying to convert between @convention(c) functions
9+
// with differing cTypes doesn't work.
10+
11+
let _ : @convention(c) (Int) -> Int = f1!
12+
// expected-error@-1{{cannot convert value of type '@convention(c, cType: "size_t (*)(size_t)") (Int) -> Int' to specified type '@convention(c) (Int) -> Int'}}
13+
14+
let _ : (@convention(c) (Int) -> Int)? = f1
15+
// expected-error@-1{{cannot convert value of type '(@convention(c, cType: "size_t (*)(size_t)") (Int) -> Int)?' to specified type '(@convention(c) (Int) -> Int)?'}}
16+
17+
let _ : (@convention(c, cType: "void *(*)(void *)") (Int) -> Int)? = f1
18+
// expected-error@-1{{cannot convert value of type '(@convention(c, cType: "size_t (*)(size_t)") (Int) -> Int)?' to specified type '(@convention(c, cType: "void *(*)(void *)") (Int) -> Int)?'}}
19+
20+
21+
// Converting from @convention(c) -> @convention(swift) works
22+
23+
let _ : (Int) -> Int = ({ x in x } as @convention(c) (Int) -> Int)
24+
let _ : (Int) -> Int = ({ x in x } as @convention(c, cType: "size_t (*)(size_t)") (Int) -> Int)
25+
26+
27+
// Converting from @convention(swift) -> @convention(c) doesn't work.
28+
29+
let fs : (Int) -> Int = { x in x }
30+
31+
let _ : @convention(c) (Int) -> Int = fs
32+
// expected-error@-1{{a C function pointer can only be formed from a reference to a 'func' or a literal closure}}
33+
34+
let _ : @convention(c, cType: "size_t (*)(size_t)") (Int) -> Int = fs
35+
// expected-error@-1{{a C function pointer can only be formed from a reference to a 'func' or a literal closure}}
36+
37+
38+
// More complex examples.
39+
40+
let f2 : (@convention(c) ((@convention(c, cType: "size_t (*)(size_t)") (Swift.Int) -> Swift.Int)?) -> (@convention(c, cType: "size_t (*)(size_t)") (Swift.Int) -> Swift.Int)?)? = getHigherOrderFunctionPointer()!
41+
42+
let _ : (@convention(c) ((@convention(c) (Swift.Int) -> Swift.Int)?) -> (@convention(c, cType: "size_t (*)(size_t)") (Swift.Int) -> Swift.Int)?)? = f2!
43+
// expected-error@-1{{cannot convert value of type '@convention(c) ((@convention(c, cType: "size_t (*)(size_t)") (Int) -> Int)?) -> (@convention(c, cType: "size_t (*)(size_t)") (Int) -> Int)?' to specified type '(@convention(c) ((@convention(c) (Int) -> Int)?) -> (@convention(c, cType: "size_t (*)(size_t)") (Int) -> Int)?)?'}}
44+
45+
let _ : (@convention(c) ((@convention(c) (Swift.Int) -> Swift.Int)?) -> (@convention(c) (Swift.Int) -> Swift.Int)?)? = f2!
46+
// expected-error@-1{{cannot convert value of type '@convention(c) ((@convention(c, cType: "size_t (*)(size_t)") (Int) -> Int)?) -> (@convention(c, cType: "size_t (*)(size_t)") (Int) -> Int)?' to specified type '(@convention(c) ((@convention(c) (Int) -> Int)?) -> (@convention(c) (Int) -> Int)?)?'}}
47+
48+
let f3 = getFunctionPointer3
49+
50+
let _ : @convention(c) (UnsafeMutablePointer<ctypes.Dummy>?) -> UnsafeMutablePointer<ctypes.Dummy>? = f3()!

0 commit comments

Comments
 (0)