Extend TTypeSymbol with support for two-way type conversion
The current type conversion scheme, using TTypeSymbol.IsCompatible
and TConvExpr.WrapWithConvCast
, is only able to convert a value if the target type knows about the source type.
Consider the following example. WORD
is a custom type derived from TBaseIntegerSymbol
, implemented by the class TWinApiTypeIntegerSymbol<WORD>
:
var i: integer := 1; // TBaseIntegerSymbol
// TWinApiTypeIntegerSymbol knows how to convert from TBaseIntegerSymbol
var w: WORD := i;
// TBaseIntegerSymbol doesn't know how to convert from TWinApiTypeIntegerSymbol
// Syntax Error: Incompatible types: Cannot assign "WORD" to "Integer"
var n: integer := w;
Because of this design it appears to be impossible to extend the type system with custom types that support implicit conversion. I'm aware that it is possible to work around the limitation with variants or explicit casts, but it's a bit hard to explain to the users why they need to cast value types that are clearly compatible.
Proposal
Extend TTypeSymbol with support for reverse type compatibility check.
type
TTypeSymbol = class(...)
...
function IsCompatible(typSym : TTypeSymbol) : Boolean; virtual; deprecated 'Use CanConvertFrom';
function CanConvertTo(typSym : TTypeSymbol) : Boolean; virtual;
function CanConvertFrom(typSym : TTypeSymbol) : Boolean; virtual;
...
end;
function TTypeSymbol.CanConvertTo(typSym : TTypeSymbol) : Boolean;
begin
Result := False;
end;
function TTypeSymbol.CanConvertFrom(typSym : TTypeSymbol) : Boolean; virtual;
begin
Result := IsCompatible(typSym); // backward compatible
end;
Decouple type conversion from TConvExpr
Instead of having the type conversion rules hard coded into TConvExpr
I propose the logic be delegated to the individual type classes.
type
TTypeSymbol = class(...)
...
function ConvertTo(typSym : TTypeSymbol) : TTypedExpr; virtual;
function ConvertFrom(typSym : TTypeSymbol) : TTypedExpr; virtual;
...
end;
See also: MSDN: Type Converters for Value Translation
Example (script): ###
var a: TypeA;
var b: TypeB;
a := b;
Usage (pseudo code):
if (a.CanConvertFrom(b)) then
a.ConvertFrom(b)
else
if (b.CanConvertTo(a)) then
b.ConvertTo(b)
else
error;
Although I "like" the architecture of the above proposal a much more lean solution might be to simply add support for implicit type conversion operators. This would also have the added benefit of being able to add implicit type conversion between existing types.
Comments (2)
-
repo owner -
repo owner Commit some early support for implicit operator.
More testing likely needed :)
- Log in to comment
Yes, the whole conversion framework needs to be redone to support implicit casts, the current mechanism was more an incremental tweak over the previous architecture.
However rather than handling it at the TTypeSymbol level, I was planning to handle it at the operator level, because once you allow custom implicit cast operators, you introduce scoping and precedence issues, ie. the conversion no longer just depends on the types to/from, but on the scope, and all casters in scope.
There are already some tidbits ready, in TOperators class (RegisterCaster/FindCaster), what's missing:
Also the IsCompatible method was not meant not denote the ability to convert, but that one type is compatible with another without conversion (in your case, the Integer -> Word types would not be compatible, because you will probably want a range checking, which is a minimalistic conversion).
However IsCompatible would be affected by implicit casts, if only to check that "implicitly compatible types" (fi. a type and an alias of that type) do not have a custom implicit caster that mean a conversion is now required.