visitAsExpression method
Object?
visitAsExpression(
- 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()}");
}