visitAsExpression method

  1. @override
Object? visitAsExpression(
  1. AsExpression node
)
override

Implementation

@override
Object? visitAsExpression(AsExpression node) {
  final value = node.expression.accept<Object?>(this);
  final typeNode = node.type;
  if (typeNode is NamedType) {
    final typeName = typeNode.name.lexeme;
    // G-DOV2-1 FIX: Handle nullable types (e.g., String?, int?)
    // If the type is nullable (has a '?' suffix), then null is always allowed
    final isNullable = typeNode.question != null;
    if (isNullable && value == null) {
      return value; // Null is valid for any nullable type
    }
    switch (typeName) {
      case 'int':
        if (value is int) return value;
        break;
      case 'double':
        if (value is double) return value;
        // GEN-094: int→double promotion — see tom_d4rt_ast mirror for
        // the full reasoning; in short, D4rt collection literals
        // store int elements even under `<double>[...]`, so an
        // explicit `as double` must handle the same case
        // extractBridgedArg already does via INTER-003.
        if (value is int) return value.toDouble();
        break;
      case 'num':
        if (value is num) return value;
        break;
      case 'String':
        if (value is String) return value;
        break;
      case 'bool':
        if (value is bool) return value;
        break;
      case 'List':
        if (value is List) return value;
        break;
      case 'Null':
        if (value == null) return value;
        break;
      case 'Object':
        // G-DOV2-1 FIX: For Object?, null is valid (handled above)
        // For Object, any non-null value is valid
        if (value != null || isNullable) return value;
        break;
      case 'dynamic':
        return value;
      default:
        // C21: If the value is a native interface-proxy that wraps an
        // InterpretedInstance and the cast target names a class in the
        // wrapped instance's class chain, unwrap to the InterpretedInstance
        // so subsequent property/method access dispatches against the
        // scripted class (which knows about user-defined fields and
        // methods that the bridged proxy alone cannot serve).
        if (value is D4InterpretedProxy) {
          final inner = value.d4rtInstance;
          if (inner is InterpretedInstance &&
              _interpretedClassChainHasName(inner.klass, typeName)) {
            return inner;
          }
        }
        // For custom/interpreted types, we can add logic here
        // For now, we accept all (permissive behavior)
        return value;
    }
  } else if (typeNode is GenericFunctionType ||
      typeNode is RecordTypeAnnotation) {
    // DFUB5: `value as int Function(int)` / `value as (int, String)` — accept
    // when the value's runtime type is compatible with the structural
    // Function/Record type (or when the value's type can't be determined, to
    // stay permissive). Upstream 848f03d.
    final targetType = _resolveTypeAnnotation(typeNode);
    final valueType = environment.getRuntimeType(value);
    if (valueType == null ||
        valueType.isSubtypeOf(targetType, value: value)) {
      return value;
    }
  }
  // GEN-094: Include actual value type for actionable diagnostics.
  final valueDesc = value?.runtimeType.toString() ?? 'Null';
  throw D4rtTypeError(
      "Cast failed with 'as' : value of type $valueDesc cannot be cast to ${typeNode.toSource()}");
}