Skip to content

Commit

Permalink
Casting improvements (#1179)
Browse files Browse the repository at this point in the history
Enhancements:
- Consistently fold nulls and type mismatches to default values when casting to value types
- Consistently fold nulls and type mismatches to default values when casting to or from generic argument types
  • Loading branch information
degory committed May 30, 2024
1 parent a055b96 commit cda02e6
Show file tree
Hide file tree
Showing 26 changed files with 467 additions and 94 deletions.
4 changes: 2 additions & 2 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
]
},
"ghul.test": {
"version": "1.3.3",
"version": "1.3.5",
"commands": [
"ghul-test"
]
},
"ghul.compiler": {
"version": "0.8.46",
"version": "0.8.47",
"commands": [
"ghul-compiler"
]
Expand Down
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>0.8.47-alpha.5</Version>
<Version>0.8.48-alpha.26</Version>
<NoWarn>$(NoWarn);NU1507</NoWarn>
</PropertyGroup>
<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<ItemGroup>
<!-- ghūl runtime -->
<PackageVersion Include="ghul.runtime" Version="1.3.3" />
<PackageVersion Include="ghul.runtime" Version="1.3.5" />

<!-- ghūl compiler dependencies -->
<PackageVersion Include="System.Reflection.MetadataLoadContext" Version="8.0.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Run test",
"command": "dotnet ghul-test \"${workspaceFolder}\"",
"type": "shell",
"group": {
"kind": "test",
"isDefault": true
}
},
{
"label": "Capture test expectation",
"command": "../../../tasks/capture.sh \"${workspaceFolder}\"",
"type": "shell",
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"compiler": "dotnet ../../../publish/ghul.dll"
}
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
cts object null
cts array 1 2 3
cts 123
cts object o hello
cts object p
cts thing
cti object null 0
cti array 1 2 3 0
cti 'hello' 0
cti object o 0
cti object p 123
cti thing 0
ctt object null THING #0 default
ctt array 1 2 3 THING #0 default
ctt 'hello' THING #0 default
ctt object o THING #0 default
ctt object p THING #0 default
ctt thing THING #2 initialized
cfo string object null
cfo string array 1 2 3
cfo string 123
cfo string object o hello
cfo string object p
cfo string thing
cfo int object null 0
cfo int array 1 2 3 0
cfo int 'hello' 0
cfo int object o 0
cfo int object p 123
cfo int thing 0
cfo THING object null THING #0 default
cfo THING array 1 2 3 THING #0 default
cfo THING 'hello' THING #0 default
cfo THING object o THING #0 default
cfo THING object p THING #0 default
cfo THING thing THING #5 initialized
cfs object hello hello
cfs int hello 0
cfs THING hello THING #0 default
79 changes: 79 additions & 0 deletions integration-tests/execution/cast-generic-argument-type/test.ghul
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use IO.Std.write_line;

entry() is
let n: object = null;
let a = [1, 2, 3];

let o: object = "hello";
let p: object = 123;

write_line("cts object null {cast_to_string(n)}");
write_line("cts array 1 2 3 {cast_to_string(a)}");
write_line("cts 123 {cast_to_string(123)}");
write_line("cts object o {cast_to_string(o)}");
write_line("cts object p {cast_to_string(p)}");
write_line("cts thing {cast_to_string(THING())}");

write_line("cti object null {cast_to_int(n)}");
write_line("cti array 1 2 3 {cast_to_int(a)}");
write_line("cti 'hello' {cast_to_int("hello")}");
write_line("cti object o {cast_to_int(o)}");
write_line("cti object p {cast_to_int(p)}");
write_line("cti thing {cast_to_int(THING())}");

write_line("ctt object null {cast_to_thing(n)}");
write_line("ctt array 1 2 3 {cast_to_thing(a)}");
write_line("ctt 'hello' {cast_to_thing("hello")}");
write_line("ctt object o {cast_to_thing(o)}");
write_line("ctt object p {cast_to_thing(p)}");
write_line("ctt thing {cast_to_thing(THING())}");

write_line("cfo string object null {cast_from_object[string](null)}");
write_line("cfo string array 1 2 3 {cast_from_object[string](a)}");
write_line("cfo string 123 {cast_from_object[string](123)}");
write_line("cfo string object o {cast_from_object[string](o)}");
write_line("cfo string object p {cast_from_object[string](p)}");
write_line("cfo string thing {cast_from_object[string](THING())}");

write_line("cfo int object null {cast_from_object[int](null)}");
write_line("cfo int array 1 2 3 {cast_from_object[int](a)}");
write_line("cfo int 'hello' {cast_from_object[int]("hello")}");
write_line("cfo int object o {cast_from_object[int](o)}");
write_line("cfo int object p {cast_from_object[int](p)}");
write_line("cfo int thing {cast_from_object[int](THING())}");

write_line("cfo THING object null {cast_from_object[THING](null)}");
write_line("cfo THING array 1 2 3 {cast_from_object[THING](a)}");
write_line("cfo THING 'hello' {cast_from_object[THING]("hello")}");
write_line("cfo THING object o {cast_from_object[THING](o)}");
write_line("cfo THING object p {cast_from_object[THING](p)}");
write_line("cfo THING thing {cast_from_object[THING](THING())}");

// this is maybe not very useful, but is orthogonal
write_line("cfs object hello {cast_from_string[object]("hello")}");
write_line("cfs int hello {cast_from_string[int]("hello")}");
write_line("cfs THING hello {cast_from_string[THING]("hello")}");
si

struct THING is
_not_default: bool;
_next: int static;

value: int;

init() is
value = _next;
_next = _next + 1;
_not_default = true;
si

to_string() -> string =>
"THING #{value} {if _not_default then "initialized" else "default" fi}";
si

cast_to_string[T](value: T) -> string => cast string(value);
cast_to_int[T](value: T) -> int => cast int(value);
cast_to_thing[T](value: T) -> THING => cast THING(value);

cast_from_object[T](value: object) -> T => cast T(value);
cast_from_string[T](value: string) -> T => cast T(value);
Empty file.
5 changes: 1 addition & 4 deletions integration-tests/il/convert-object-to-struct/ghul.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
{
"compiler": "dotnet ../../../publish/ghul.dll",
"source": [
"."
]
"compiler": "dotnet ../../../publish/ghul.dll"
}
16 changes: 13 additions & 3 deletions integration-tests/il/convert-object-to-struct/il.expected
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
.locals init (class ['System.Runtime']'System'.'Object' '.cast.0')
ldloc 'o.1'
unbox.any class ['System.Runtime']'System'.'Object'
box valuetype 'convert_object_to_struct__test'.'STRUCT_THINGER'
stloc 't.2'
isinst valuetype 'convert_object_to_struct__test'.'STRUCT_THINGER'
stloc '.cast.0'
ldloc '.cast.0'
brfalse L_0
ldloc '.cast.0'
unbox.any valuetype 'convert_object_to_struct__test'.'STRUCT_THINGER'
br L_1
L_0:
.locals init (valuetype 'convert_object_to_struct__test'.'STRUCT_THINGER' '.default.1')
ldloc '.default.1'
L_1:
stloc 't.2'
5 changes: 2 additions & 3 deletions integration-tests/il/convert-object-to-struct/test.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ struct STRUCT_THINGER: GetAThing is
to_string() -> string => name;
si

test_convert_of_object_to_class() is
test_convert_of_object_to_struct() is
let s = STRUCT_THINGER("hello");

let o: object;
let t: GetAThing;
let t: STRUCT_THINGER;

o = s;

Expand All @@ -36,4 +36,3 @@ test_convert_of_object_to_class() is
write_line("result is: " + t.get_a_thing());
si


15 changes: 0 additions & 15 deletions integration-tests/il/convert-object-to-struct/test.ghulproj

This file was deleted.

13 changes: 13 additions & 0 deletions src/ioc/container.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ namespace IoC is
innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup;
function_caller: Semantic.FUNCTION_CALLER;
overload_resolver: Semantic.OVERLOAD_RESOLVER;
type_caster: Semantic.TYPE_CASTER;

symbol_use_locations: Semantic.SYMBOL_USE_LOCATIONS;
symbol_definition_locations: Semantic.SYMBOL_DEFINITION_LOCATIONS;
Expand Down Expand Up @@ -448,6 +449,16 @@ namespace IoC is

overload_resolver = Semantic.OVERLOAD_RESOLVER(logger);

type_caster =
Semantic.TYPE_CASTER(
brancher,
block_stack,
value_boxer,
value_converter,
innate_symbol_lookup,
logger
);

symbol_use_locations = Semantic.SYMBOL_USE_LOCATIONS();

symbol_definition_locations = Semantic.SYMBOL_DEFINITION_LOCATIONS(symbol_use_locations);
Expand Down Expand Up @@ -499,6 +510,7 @@ namespace IoC is
symbol_loader,
innate_symbol_lookup,
function_caller,
type_caster,
overload_resolver,
symbol_use_locations,
ir_context,
Expand Down Expand Up @@ -531,6 +543,7 @@ namespace IoC is
symbol_loader,
innate_symbol_lookup,
function_caller,
type_caster,
overload_resolver,
symbol_use_locations,
ir_context,
Expand Down
6 changes: 3 additions & 3 deletions src/ir/block_context.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ namespace IR is
is_in_block: bool;
current_block: Values.BLOCK;

enter_block();
enter_block(type: Semantic.Types.Type);
enter_block(block: Values.BLOCK);
enter_block() -> IR.Values.BLOCK;
enter_block(type: Semantic.Types.Type) -> IR.Values.BLOCK;
enter_block(block: Values.BLOCK) -> IR.Values.BLOCK;

leave_block();

Expand Down
16 changes: 11 additions & 5 deletions src/ir/block_stack.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,22 @@ namespace IR is
return _blocks.peek();
si

enter_block(block: Values.BLOCK) is
enter_block(block: Values.BLOCK) -> Values.BLOCK is
_blocks.push(block);

return block;
si

enter_block(type: Semantic.Types.Type) is
enter_block(Values.BLOCK(type));
enter_block(type: Semantic.Types.Type) -> Values.BLOCK is
let result = Values.BLOCK(type);
enter_block(result);
return result;
si

enter_block() is
enter_block(Values.BLOCK());
enter_block() -> Values.BLOCK is
let result = Values.BLOCK();
enter_block(result);
return result;
si

leave_block() is
Expand Down
10 changes: 7 additions & 3 deletions src/ir/temp.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ namespace IR is
self.prefix = prefix;
self.suffix = suffix;

self.id = _next_id;
_next_id = _next_id + 1;

self.id = get_next_id();
self.type = type;

declare();
Expand All @@ -51,6 +49,12 @@ namespace IR is
_next_id = 0;
si

get_next_id() -> int static is
let result = _next_id;
_next_id = _next_id + 1;
return result;
si

declare() is
let buffer = System.Text.StringBuilder();

Expand Down
39 changes: 39 additions & 0 deletions src/ir/values/default.ghul
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
namespace IR.Values is
use Semantic.Types.Type;

class DEFAULT: Value is
has_type: bool => type?;
type: Type;
is_value_type: bool => type.is_value_type;
is_value_tuple: bool => type.is_value_tuple;

has_address: bool => true;

init(
type: Type
) is
super.init();

assert type? else "type is null";

self.type = type;
si

gen(context: IR.CONTEXT) is
let id = TEMP.get_next_id();

context.write_line(".locals init ({type.get_il_type()} '.default.{id}')");
context.write_line("ldloc '.default.{id}'");
si

gen_address(context: IR.CONTEXT) is
let id = TEMP.get_next_id();

context.write_line(".locals init ({type.get_il_type()} '.default.{id}')");
context.write_line("ldloca '.default.{id}'");
si

to_string() -> string =>
"default:[{type}]()";
si
si
1 change: 1 addition & 0 deletions src/ir/values/type_wrapper.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ namespace IR.Values is
class TYPE_WRAPPER: Value is
has_type: bool => type?;
has_address: bool => value.has_address;
is_lightweight_pure: bool => value.is_lightweight_pure;
type: Type;
value: Value;

Expand Down
Loading

0 comments on commit cda02e6

Please sign in to comment.