Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type inference improvements #1176

Merged
merged 1 commit into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
]
},
"ghul.compiler": {
"version": "0.8.43",
"version": "0.8.44",
"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.44-alpha.80</Version>
<Version>0.8.45-alpha.5</Version>
<NoWarn>$(NoWarn);NU1507</NoWarn>
</PropertyGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
test.ghul: 14,18..14,24: error: no static overload found for function(Ghul.int, Ghul.single), tried Test.GenericFunctionTypeInference.function[T](a: T, b: T) -> T
test.ghul: 24,18..24,40: error: no static overload found for function(Ghul.int[], Ghul.object[]), tried Test.GenericFunctionTypeInference.function[T](a: T, b: T) -> T
test.ghul: 27,18..27,58: error: no static overload found for function(Ghul.int[], Collections.LIST[Ghul.int]), tried Test.GenericFunctionTypeInference.function[T](a: T, b: T) -> T
test.ghul: 28,18..28,40: error: no static overload found for function(Ghul.int[], Ghul.Pipes.Pipe[Ghul.int]), tried Test.GenericFunctionTypeInference.function[T](a: T, b: T) -> T
test.ghul: 13,14..13,20: error: no static overload found for function(Ghul.int, Ghul.single), tried function[T](a: T, b: T) -> T
test.ghul: 24,14..24,36: error: no static overload found for function(Ghul.int[], System.ISpanFormattable[]), tried function[T](a: T, b: T) -> T
test.ghul: 27,14..27,54: error: no static overload found for function(Ghul.int[], Collections.LIST[Ghul.int]), tried function[T](a: T, b: T) -> T
test.ghul: 28,14..28,36: error: no static overload found for function(Ghul.int[], Ghul.Pipes.Pipe[Ghul.int]), tried function[T](a: T, b: T) -> T
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
namespace Test.GenericFunctionTypeInference is
use Collections;
use Collections;

entry() is
function(1, 2);
function(1.0, 2.0);
entry() is
function(1, 2);

function(1.0, 2.0);

function("a", "b");
function("a", "b");

function(true, false);
function(true, false);

// expect an error
function(1, 2.0);
// expect an error
function(1, 2.0);

function([1234], [5678]);
function([1234], [5678]);


function([1, 2.0], [3, 4.0]);
function([1, 2.0], [3, 4.0]);

function([1, 2.0, "a"], [3, 4.0, "b"]);
function([1, 2.0, "a"], [3, 4.0, "b"]);

// expect an error
function([1, 2, 3], [4, 5, 6.0]);
// expect error
// infers a not very useful interface - LUB needs tuning
function([1, 2, 3], [4, 5, 6.0]);

// expect errors. Note these don't work in C# either:
function([1234, 5678], LIST[int]([91011, 121314]));
function([1, 2, 3], [4, 5, 6] |);
si
// expect errors. Note these don't work in C# either:
function([1234, 5678], LIST[int]([91011, 121314]));
function([1, 2, 3], [4, 5, 6] |);
si

function[T](a: T, b: T) -> T is
return a;
si
function[T](a: T, b: T) -> T is
return a;
si
2 changes: 1 addition & 1 deletion src/semantic/dotnet/symbol_factory.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ namespace Semantic.DotNet is
fi

return t.get_generic_parameter_constraints().count == 0;
si
si

add_members(result: Symbols.Scoped, type: TYPE) is
let properties = MAP[System.Reflection.MethodInfo,PROPERTY_DETAILS]();
Expand Down
6 changes: 1 addition & 5 deletions src/semantic/types/tuple.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ namespace Semantic.Types is
assert args? else "tuple args are null";
assert args | .all(a => a?) else "tuple at least one null argument";

assert !names? \/ names | .all(n => n?) else "tuple at least one null name";

return
if !args? then
"()";
Expand All @@ -27,7 +25,7 @@ namespace Semantic.Types is
si

_get_element_name(names: Collections.List[string], index: int) -> string static =>
if names? /\ index < names.count then
if names? /\ index < names.count /\ names[index]? then
"{names[index]}: "
else
""
Expand All @@ -41,8 +39,6 @@ namespace Semantic.Types is
) is
super.init(Symbols.TUPLE(location, symbol, arguments, names));

assert !names? \/ names | .all(n => n?) else "tuple at least one null name";

self.names = names;
si

Expand Down
1 change: 1 addition & 0 deletions src/semantic/types/type.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ namespace Semantic.Types is
is_function_with_any_implicit_argument_types: bool => false;
is_ref: bool => false; // specifically 'ref', not just a reference type
is_value_tuple: bool => false;
is_unsafe_constraints: bool => symbol.is_unsafe_constraints;

init() is
si
Expand Down
2 changes: 1 addition & 1 deletion src/syntax/process/compile_expressions.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,7 @@ namespace Syntax.Process is
types.add(Semantic.Types.ERROR());

elif Value.check_is_consumable(_logger, v.location, v.value) then
names.add(null);
names.add("`{index}");

if element_type_constraint? then
let type = element_type_constraint;
Expand Down
38 changes: 31 additions & 7 deletions src/syntax/process/least_upper_bound_map.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Syntax.Process is
si

class LEAST_UPPER_BOUND_MAP is
_any_unsafe_constraints: bool;
_mode: LUB_MODE;

types: LIST[Type];
Expand All @@ -28,6 +29,10 @@ namespace Syntax.Process is
add(type: Type) is
types.add(type);

if type.is_unsafe_constraints then
_any_unsafe_constraints = true;
fi

_update_element_names_from(type);
si

Expand Down Expand Up @@ -71,20 +76,33 @@ namespace Syntax.Process is
// case it will return object, which may still be better
// than a fairly random choice of interface type
let best_concrete_type = _try_get_best_concrete();
if best_concrete_type? then
return best_concrete_type;
fi

// this is LUB applied to all elements and all traits
// they implement. It has a tendency to select unhelpful
// traits, and needs tuning somehow to prioritize
// traits that are relevant based on context
let best_trait_type = _try_get_best_trait();
if best_trait_type? then
return best_trait_type;
fi

return null;
// this still might not be enough - we might need to record the
// average depth difference (='specificity') per type, but that
// could be slow
if best_concrete_type? /\ best_trait_type? then
let total_concrete_depth_difference =
types | .reduce(0, (d, t) => d + (t.depth - best_concrete_type.depth));

let total_trait_depth_difference =
types | .reduce(0, (d, t) => d + (t.depth - best_trait_type.depth));

if total_trait_depth_difference < total_concrete_depth_difference then
return best_trait_type
else
return best_concrete_type
fi
elif best_concrete_type? then
return best_concrete_type
else
return best_trait_type
fi
si

_try_get_best_assignable() -> Type is
Expand Down Expand Up @@ -149,6 +167,12 @@ namespace Syntax.Process is

for list in result.values do
for type in list do
if !_any_unsafe_constraints /\ type.is_unsafe_constraints then
// only allow unsafe constraints if some of the element
// types have unsafe constraints:
continue;
fi

if !best? then
best = type;
is_ambiguous = false;
Expand Down
Loading