[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.
This commit is contained in:
Doug Gregor
2016-09-20 17:01:35 -07:00
parent 8da7d7c156
commit 87908c8a1b
2 changed files with 84 additions and 0 deletions

View File

@@ -2497,6 +2497,51 @@ static bool _dynamicCastStructToStruct(OpaqueValue *destination,
return result;
}
static bool _dynamicCastTupleToTuple(OpaqueValue *destination,
OpaqueValue *source,
const TupleTypeMetadata *sourceType,
const TupleTypeMetadata *targetType,
DynamicCastFlags flags) {
assert(sourceType != targetType &&
"Caller should handle exact tuple matches");
// Simple case: number of elements mismatches.
if (sourceType->NumElements != targetType->NumElements)
return _fail(source, sourceType, targetType, flags);
// Check that the elements line up.
const char *sourceLabels = sourceType->Labels;
const char *targetLabels = targetType->Labels;
for (unsigned i = 0, n = sourceType->NumElements; i != n; ++i) {
// Check the label, if there is one.
if (sourceLabels && targetLabels && sourceLabels != targetLabels) {
const char *sourceSpace = strchr(sourceLabels, ' ');
const char *targetSpace = strchr(targetLabels, ' ');
// If both have labels, and the labels mismatch, we fail.
if (sourceSpace && sourceSpace != sourceLabels &&
targetSpace && targetSpace != targetLabels) {
unsigned sourceLen = sourceSpace - sourceLabels;
unsigned targetLen = targetSpace - targetLabels;
if (sourceLen != targetLen ||
strncmp(sourceLabels, targetLabels, sourceLen) != 0)
return _fail(source, sourceType, targetType, flags);
}
sourceLabels = sourceSpace ? sourceSpace + 1 : nullptr;
targetLabels = targetSpace ? targetSpace + 1 : nullptr;
}
// Make sure the types match exactly.
// FIXME: we should dynamically cast the elements themselves.
if (sourceType->getElement(i).Type != targetType->getElement(i).Type)
return _fail(source, sourceType, targetType, flags);
}
return _succeed(destination, source, targetType, flags);
}
/******************************************************************************/
/****************************** Main Entrypoint *******************************/
/******************************************************************************/
@@ -2718,6 +2763,15 @@ bool swift::swift_dynamicCast(OpaqueValue *dest,
targetType, flags);
}
// If both are tuple types, allow the cast to add/remove labels.
if (srcType->getKind() == MetadataKind::Tuple &&
targetType->getKind() == MetadataKind::Tuple) {
return _dynamicCastTupleToTuple(dest, src,
cast<TupleTypeMetadata>(srcType),
cast<TupleTypeMetadata>(targetType),
flags);
}
// Otherwise, we have a failure.
return _fail(src, srcType, targetType, flags);
}