Skip to content

Commit d9cd7e7

Browse files
Merge pull request swiftlang#35457 from varungandhi-apple/vg-cleanup-rep-matching
[docs] Tidy and document permissible conversions based on calling conventions
2 parents 8d217f3 + 29430d6 commit d9cd7e7

File tree

5 files changed

+96
-47
lines changed

5 files changed

+96
-47
lines changed

docs/DynamicCasting.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ Casting from a function type F1 to a function type F2 will succeed iff the follo
299299
* Corresponding arguments have identical types
300300
* The return types are identical
301301
* If F1 is a throwing function type, then F2 must be a throwing function type. If F1 is not throwing, then F2 may be a throwing or non-throwing function type.
302+
* F1 and F2 have the same calling convention.
302303

303304
Note that it is _not_ sufficient for argument and return types to be castable; they must actually be identical.
304305

include/swift/AST/ExtInfo.h

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,45 @@ enum class SILFunctionTypeRepresentation : uint8_t {
169169
Closure,
170170
};
171171

172+
/// Returns true if the function with this convention doesn't carry a context.
173+
constexpr bool
174+
isThinRepresentation(FunctionTypeRepresentation rep) {
175+
switch (rep) {
176+
case FunctionTypeRepresentation::Swift:
177+
case FunctionTypeRepresentation::Block:
178+
return false;
179+
case FunctionTypeRepresentation::Thin:
180+
case FunctionTypeRepresentation::CFunctionPointer:
181+
return true;
182+
}
183+
llvm_unreachable("Unhandled FunctionTypeRepresentation in switch.");
184+
}
185+
186+
/// Returns true if the function with this convention doesn't carry a context.
187+
constexpr bool
188+
isThinRepresentation(SILFunctionTypeRepresentation rep) {
189+
switch (rep) {
190+
case SILFunctionTypeRepresentation::Thick:
191+
case SILFunctionTypeRepresentation::Block:
192+
return false;
193+
case SILFunctionTypeRepresentation::Thin:
194+
case SILFunctionTypeRepresentation::Method:
195+
case SILFunctionTypeRepresentation::ObjCMethod:
196+
case SILFunctionTypeRepresentation::WitnessMethod:
197+
case SILFunctionTypeRepresentation::CFunctionPointer:
198+
case SILFunctionTypeRepresentation::Closure:
199+
return true;
200+
}
201+
llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch.");
202+
}
203+
204+
/// Returns true if the function with this convention carries a context.
205+
template <typename Repr>
206+
constexpr bool
207+
isThickRepresentation(Repr repr) {
208+
return !isThinRepresentation(repr);
209+
}
210+
172211
constexpr SILFunctionTypeRepresentation
173212
convertRepresentation(FunctionTypeRepresentation rep) {
174213
switch (rep) {
@@ -350,19 +389,7 @@ class ASTExtInfoBuilder {
350389

351390
/// True if the function representation carries context.
352391
constexpr bool hasContext() const {
353-
switch (getSILRepresentation()) {
354-
case SILFunctionTypeRepresentation::Thick:
355-
case SILFunctionTypeRepresentation::Block:
356-
return true;
357-
case SILFunctionTypeRepresentation::Thin:
358-
case SILFunctionTypeRepresentation::Method:
359-
case SILFunctionTypeRepresentation::ObjCMethod:
360-
case SILFunctionTypeRepresentation::WitnessMethod:
361-
case SILFunctionTypeRepresentation::CFunctionPointer:
362-
case SILFunctionTypeRepresentation::Closure:
363-
return false;
364-
}
365-
llvm_unreachable("Unhandled SILFunctionTypeRepresentation in switch.");
392+
return isThickRepresentation(getSILRepresentation());
366393
}
367394

368395
// Note that we don't have setters. That is by design, use

lib/SILGen/SILGenExpr.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,6 +1590,10 @@ ManagedValue emitCFunctionPointer(SILGenFunction &SGF,
15901590
// C function pointers cannot capture anything from their context.
15911591
auto captures = SGF.SGM.Types.getLoweredLocalCaptures(constant);
15921592

1593+
// Catch cases like:
1594+
// func g(_ : @convention(c) () -> ()) {}
1595+
// func q() { let z = 0; func r() { print(z) }; g(r); } // error
1596+
// (See also: [NOTE: diagnose-swift-to-c-convention-change])
15931597
if (!captures.getCaptures().empty() ||
15941598
captures.hasGenericParamCaptures() ||
15951599
captures.hasDynamicSelfCapture() ||

lib/Sema/CSApply.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6013,6 +6013,11 @@ maybeDiagnoseUnsupportedFunctionConversion(ConstraintSystem &cs, Expr *expr,
60136013
if (auto closure = dyn_cast<ClosureExpr>(semanticExpr))
60146014
return;
60156015

6016+
// Diagnose cases like:
6017+
// func f() { print(w) }; func g(_ : @convention(c) () -> ()) {}
6018+
// let k = f; g(k) // error
6019+
// func m() { let x = 0; g({ print(x) }) } // error
6020+
// (See also: [NOTE: diagnose-swift-to-c-convention-change])
60166021
de.diagnose(expr->getLoc(),
60176022
diag::invalid_c_function_pointer_conversion_expr);
60186023
}

lib/Sema/CSSimplify.cpp

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1535,31 +1535,29 @@ ConstraintSystem::matchTupleTypes(TupleType *tuple1, TupleType *tuple2,
15351535
return getTypeMatchSuccess();
15361536
}
15371537

1538-
// Determine whether conversion is allowed between two function types
1539-
// based on their representations.
1538+
/// Check where a representation is a subtype of another.
1539+
///
1540+
/// The subtype relationship is defined as:
1541+
/// 1. any representation R is a sub-type of itself.
1542+
/// 2. a thin representation is a subtype of any other representation.
1543+
/// 3. a thick representation is a subtype of any other thick representation.
1544+
///
1545+
/// For example, since `@convention(c)` is a thin representation, and
1546+
/// `@convention(swift)` is a thick representation,
1547+
/// `@convention(c) (A) -> B` is a sub-type of `(A) -> B`.
1548+
///
1549+
/// NOTE: Unlike typical subtyping relationships, this is not anti-symmetric.
1550+
/// For example, @convention(c) and @convention(thin) are subtypes of each other
1551+
/// but not equal.
15401552
static bool
1541-
isConversionAllowedBetween(FunctionTypeRepresentation rep1,
1542-
FunctionTypeRepresentation rep2) {
1543-
auto isThin = [](FunctionTypeRepresentation rep) {
1544-
return rep == FunctionTypeRepresentation::CFunctionPointer ||
1545-
rep == FunctionTypeRepresentation::Thin;
1546-
};
1547-
1548-
// Allowing "thin" (c, thin) to "thin" conventions
1549-
if (isThin(rep1) && isThin(rep2))
1550-
return true;
1551-
1552-
// Allowing all to "thick" (swift, block) conventions
1553-
// "thin" (c, thin) to "thick" or "thick" to "thick"
1554-
if (rep2 == FunctionTypeRepresentation::Swift ||
1555-
rep2 == FunctionTypeRepresentation::Block)
1556-
return true;
1557-
1558-
return rep1 == rep2;
1553+
isSubtypeOf(FunctionTypeRepresentation potentialSubRepr,
1554+
FunctionTypeRepresentation potentialSuperRepr) {
1555+
return (potentialSubRepr == potentialSuperRepr)
1556+
|| isThinRepresentation(potentialSubRepr)
1557+
|| isThickRepresentation(potentialSuperRepr);
15591558
}
15601559

1561-
// Returns 'false' (i.e. no error) if it is legal to match functions with the
1562-
// corresponding function type representations and the given match kind.
1560+
/// Returns true if `constraint rep1 rep2` is satisfied.
15631561
static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1,
15641562
FunctionTypeRepresentation rep2,
15651563
ConstraintKind kind,
@@ -1569,23 +1567,37 @@ static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1,
15691567
case ConstraintKind::BindParam:
15701568
case ConstraintKind::BindToPointerType:
15711569
case ConstraintKind::Equal:
1572-
return rep1 != rep2;
1570+
return rep1 == rep2;
15731571

15741572
case ConstraintKind::Subtype: {
15751573
auto last = locator.last();
15761574
if (!(last && last->is<LocatorPathElt::FunctionArgument>()))
1577-
return false;
1578-
1579-
// Inverting the result because matchFunctionRepresentations
1580-
// returns false in conversions are allowed.
1581-
return !isConversionAllowedBetween(rep1, rep2);
1575+
return true;
1576+
1577+
return isSubtypeOf(rep1, rep2);
15821578
}
15831579

1584-
case ConstraintKind::OpaqueUnderlyingType:
1580+
1581+
// [NOTE: diagnose-swift-to-c-convention-change]: @convention(swift) ->
1582+
// @convention(c) conversions are permitted only in certain cases.
1583+
//
1584+
// var w = 3; func f() { print(w) }; func g(_ : @convention(c) () -> ()) {}
1585+
// g(f); // OK
1586+
// let h = f as @convention(c) () -> (); g(h) // OK
1587+
// let k = f; g(k) // error
1588+
// func m() { let x = 0; g({ print(x) }) } // error
1589+
// func n() { let y = 0; func p() { }; g(p); } // OK
1590+
// func q() { let z = 0; func r() { print(z) }; g(r); } // error
1591+
//
1592+
// Since checking for disallowed cases requires access to captures,
1593+
// it is simpler to defer diagnosing (to CSApply/SILGen) and return true here.
15851594
case ConstraintKind::Conversion:
1586-
case ConstraintKind::BridgingConversion:
15871595
case ConstraintKind::ArgumentConversion:
15881596
case ConstraintKind::OperatorArgumentConversion:
1597+
return true;
1598+
1599+
case ConstraintKind::OpaqueUnderlyingType:
1600+
case ConstraintKind::BridgingConversion:
15891601
case ConstraintKind::ApplicableFunction:
15901602
case ConstraintKind::DynamicCallableApplicableFunction:
15911603
case ConstraintKind::BindOverload:
@@ -1609,7 +1621,7 @@ static bool matchFunctionRepresentations(FunctionTypeRepresentation rep1,
16091621
case ConstraintKind::OneWayEqual:
16101622
case ConstraintKind::OneWayBindParam:
16111623
case ConstraintKind::DefaultClosureType:
1612-
return false;
1624+
return true;
16131625
}
16141626

16151627
llvm_unreachable("Unhandled ConstraintKind in switch.");
@@ -1902,9 +1914,9 @@ ConstraintSystem::matchFunctionTypes(FunctionType *func1, FunctionType *func2,
19021914
return getTypeMatchFailure(locator);
19031915
}
19041916

1905-
if (matchFunctionRepresentations(func1->getExtInfo().getRepresentation(),
1906-
func2->getExtInfo().getRepresentation(),
1907-
kind, locator)) {
1917+
if (!matchFunctionRepresentations(func1->getExtInfo().getRepresentation(),
1918+
func2->getExtInfo().getRepresentation(),
1919+
kind, locator)) {
19081920
return getTypeMatchFailure(locator);
19091921
}
19101922

0 commit comments

Comments
 (0)