Skip to content

Commit 87908c8

Browse files
committed
[Runtime] Handle tuple/tuple dynamic casts that add/remove labels.
Introduce narrow support for tuple/tuple dynamic casts that merely add or remove labels, but require the element types to match exactly. This gets us back to allowing the same correct dynamic casts as in Swift 3.0, when labels were completely ignored.
1 parent 8da7d7c commit 87908c8

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

stdlib/public/runtime/Casting.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2497,6 +2497,51 @@ static bool _dynamicCastStructToStruct(OpaqueValue *destination,
24972497
return result;
24982498
}
24992499

2500+
static bool _dynamicCastTupleToTuple(OpaqueValue *destination,
2501+
OpaqueValue *source,
2502+
const TupleTypeMetadata *sourceType,
2503+
const TupleTypeMetadata *targetType,
2504+
DynamicCastFlags flags) {
2505+
assert(sourceType != targetType &&
2506+
"Caller should handle exact tuple matches");
2507+
2508+
2509+
// Simple case: number of elements mismatches.
2510+
if (sourceType->NumElements != targetType->NumElements)
2511+
return _fail(source, sourceType, targetType, flags);
2512+
2513+
// Check that the elements line up.
2514+
const char *sourceLabels = sourceType->Labels;
2515+
const char *targetLabels = targetType->Labels;
2516+
for (unsigned i = 0, n = sourceType->NumElements; i != n; ++i) {
2517+
// Check the label, if there is one.
2518+
if (sourceLabels && targetLabels && sourceLabels != targetLabels) {
2519+
const char *sourceSpace = strchr(sourceLabels, ' ');
2520+
const char *targetSpace = strchr(targetLabels, ' ');
2521+
2522+
// If both have labels, and the labels mismatch, we fail.
2523+
if (sourceSpace && sourceSpace != sourceLabels &&
2524+
targetSpace && targetSpace != targetLabels) {
2525+
unsigned sourceLen = sourceSpace - sourceLabels;
2526+
unsigned targetLen = targetSpace - targetLabels;
2527+
if (sourceLen != targetLen ||
2528+
strncmp(sourceLabels, targetLabels, sourceLen) != 0)
2529+
return _fail(source, sourceType, targetType, flags);
2530+
}
2531+
2532+
sourceLabels = sourceSpace ? sourceSpace + 1 : nullptr;
2533+
targetLabels = targetSpace ? targetSpace + 1 : nullptr;
2534+
}
2535+
2536+
// Make sure the types match exactly.
2537+
// FIXME: we should dynamically cast the elements themselves.
2538+
if (sourceType->getElement(i).Type != targetType->getElement(i).Type)
2539+
return _fail(source, sourceType, targetType, flags);
2540+
}
2541+
2542+
return _succeed(destination, source, targetType, flags);
2543+
}
2544+
25002545
/******************************************************************************/
25012546
/****************************** Main Entrypoint *******************************/
25022547
/******************************************************************************/
@@ -2718,6 +2763,15 @@ bool swift::swift_dynamicCast(OpaqueValue *dest,
27182763
targetType, flags);
27192764
}
27202765

2766+
// If both are tuple types, allow the cast to add/remove labels.
2767+
if (srcType->getKind() == MetadataKind::Tuple &&
2768+
targetType->getKind() == MetadataKind::Tuple) {
2769+
return _dynamicCastTupleToTuple(dest, src,
2770+
cast<TupleTypeMetadata>(srcType),
2771+
cast<TupleTypeMetadata>(targetType),
2772+
flags);
2773+
}
2774+
27212775
// Otherwise, we have a failure.
27222776
return _fail(src, srcType, targetType, flags);
27232777
}

test/Interpreter/tuple_casts.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %target-run-simple-swift | %FileCheck %s
2+
// RUN: %target-build-swift -O %s -o %t/a.out.optimized
3+
// RUN: %target-run %t/a.out.optimized | %FileCheck %s
4+
// REQUIRES: executable_test
5+
6+
func anyToIntPoint(_ x: Any) -> (x: Int, y: Int) {
7+
return x as! (x: Int, y: Int)
8+
}
9+
10+
func anyToIntPointOpt(_ x: Any) -> (x: Int, y: Int)? {
11+
return x as? (x: Int, y: Int)
12+
}
13+
14+
func anyToInt2(_ x: Any) -> (Int, Int) {
15+
return x as! (Int, Int)
16+
}
17+
18+
// Labels can be added/removed.
19+
print("Label add/remove") // CHECK: Label add/remove
20+
print(anyToIntPoint((x: 1, y: 2))) // CHECK-NEXT: (1, 2)
21+
print(anyToIntPoint((3, 4))) // CHECK-NEXT: (3, 4)
22+
print(anyToIntPoint((x: 5, 6))) // CHECK-NEXT: (5, 6)
23+
print(anyToInt2((1, 2))) // CHECK-NEXT: (1, 2)
24+
print(anyToInt2((x: 3, y: 4))) // CHECK-NEXT: (3, 4)
25+
print(anyToInt2((x: 5, 6))) // CHECK-NEXT: (5, 6)
26+
27+
// Labels cannot be wrong.
28+
print("Wrong labels") // CHECK: Wrong labels
29+
print(anyToIntPointOpt((x: 1, z: 2))) // CHECK-NEXT: nil
30+
print(anyToIntPointOpt((x: 1, y: 2))) // CHECK-NEXT: Optional((1, 2))

0 commit comments

Comments
 (0)