@@ -731,26 +731,49 @@ tryCastToAnyHashable(
731731 // TODO: Implement a fast path for NSString->AnyHashable casts.
732732 // These are incredibly common because an NSDictionary with
733733 // NSString keys is bridged by default to [AnyHashable:Any].
734- // Until this is implemented, fall through to the default case
735- SWIFT_FALLTHROUGH ;
734+ // Until this is implemented, fall through to the general case
735+ break ;
736736#else
737- // If no Obj-C interop, just fall through to the default case.
738- SWIFT_FALLTHROUGH ;
737+ // If no Obj-C interop, just fall through to the general case.
738+ break ;
739739#endif
740740 }
741- default : {
742- auto hashableConformance = reinterpret_cast <const HashableWitnessTable *>(
743- swift_conformsToProtocol (srcType, &HashableProtocolDescriptor));
744- if (hashableConformance) {
745- _swift_convertToAnyHashableIndirect (srcValue, destLocation,
746- srcType, hashableConformance);
747- return DynamicCastResult::SuccessViaCopy;
748- } else {
749- return DynamicCastResult::Failure;
741+ case MetadataKind::Optional: {
742+ // Until SR-9047 fixes the interactions between AnyHashable and Optional, we
743+ // avoid directly injecting Optionals. In particular, this allows
744+ // casts from [String?:String] to [AnyHashable:Any] to work the way people
745+ // expect. Otherwise, without SR-9047, the resulting dictionary can only be
746+ // indexed with an explicit Optional<String>, not a plain String.
747+ // After SR-9047, we can consider dropping this special case entirely.
748+
749+ // !!!! This breaks compatibility with compiler-optimized casts
750+ // (which just inject) and violates the Casting Spec. It just preserves
751+ // the behavior of the older casting code until we can clean things up.
752+ auto srcInnerType = cast<EnumMetadata>(srcType)->getGenericArgs ()[0 ];
753+ unsigned sourceEnumCase = srcInnerType->vw_getEnumTagSinglePayload (
754+ srcValue, /* emptyCases=*/ 1 );
755+ auto nonNil = (sourceEnumCase == 0 );
756+ if (nonNil) {
757+ return DynamicCastResult::Failure; // Our caller will unwrap the optional and try again
750758 }
759+ // Else Optional is nil -- the general case below will inject it
760+ break ;
751761 }
762+ default :
763+ break ;
764+ }
765+
766+
767+ // General case: If it conforms to Hashable, we cast it
768+ auto hashableConformance = reinterpret_cast <const HashableWitnessTable *>(
769+ swift_conformsToProtocol (srcType, &HashableProtocolDescriptor));
770+ if (hashableConformance) {
771+ _swift_convertToAnyHashableIndirect (srcValue, destLocation,
772+ srcType, hashableConformance);
773+ return DynamicCastResult::SuccessViaCopy;
774+ } else {
775+ return DynamicCastResult::Failure;
752776 }
753- return DynamicCastResult::Failure;
754777}
755778
756779static DynamicCastResult
0 commit comments