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

Allow void if expressions #1164

Merged
merged 1 commit into from
Apr 10, 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.36",
"version": "0.8.37",
"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.37-alpha.131</Version>
<Version>0.8.38-alpha.30</Version>
<NoWarn>$(NoWarn);NU1507</NoWarn>
</PropertyGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
namespace Test is
use Std = IO.Std;
use Std = IO.Std;

class Main is
entry() static is


Main().test();
si

init() is
si
class Main is
entry() static is


test() is
Std.write_line("map 123 to string: " + map_int_to_string(123, i => "'" + i + "'"));

return;
si
Main().test();
si

map_int_to_string(i: int, f: int -> string) -> string static => f(i);
init() is
si
si


test() is
Std.write_line("map 123 to string: " + map_int_to_string(123, i => "'" + i + "'"));

return;
si

map_int_to_string(i: int, f: int -> string) -> string static => f(i);
si
4 changes: 2 additions & 2 deletions src/ir/values/block.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ namespace IR.Values is
use TypeTyped = Semantic.Types.Typed;
use Semantic.Types.Type;

use Logging;

class BLOCK: Value, TypeTyped is
_values: Collections.MutableList[Value];

Expand All @@ -14,6 +12,8 @@ namespace IR.Values is
is_closed: bool;
is_emitted: bool;

is_consumable: bool => true;

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

Expand Down
1 change: 1 addition & 0 deletions src/syntax/parsers/statements/node.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ namespace Syntax.Parsers.Statements is
Lexical.TOKEN.SUPER,
Lexical.TOKEN.REC,
Lexical.TOKEN.IF,
Lexical.TOKEN.LET,
Lexical.TOKEN.OPERATOR
];

Expand Down
78 changes: 31 additions & 47 deletions src/syntax/process/compile_expressions.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -646,8 +646,7 @@ namespace Syntax.Process is

if
!function.return_type? \/
function.return_type.is_any \/
function.return_type =~ _innate_symbol_lookup.get_void_type()
function.return_type.is_any
then
return false;
fi
Expand Down Expand Up @@ -682,7 +681,7 @@ namespace Syntax.Process is
let void_type = _innate_symbol_lookup.get_void_type();

if function.return_type.is_inferred then
if Value.check_is_consumable(_logger, expression.expression.location, expression.expression.value) then
if Value.check_is_consumable_allow_void(_logger, expression.expression.location, expression.expression.value) then
function.set_return_type(expression.expression.value.type);
elif expression.expression.value? /\ expression.expression.value.type? /\ expression.expression.value.type.is_error then
function.set_return_type(Semantic.Types.ERROR());
Expand Down Expand Up @@ -711,34 +710,26 @@ namespace Syntax.Process is
pre(function: Trees.Expressions.FUNCTION) -> bool is
super.pre(function);

if !function.value? then
function.value = IR.Values.WRAPPER();
fi

let closure = cast Semantic.Symbols.Closure(scope_for(function));

if !closure? then
return true;
fi

closure.map_type_arguments();

return true;
si


visit(function: Trees.Expressions.FUNCTION) is
try
_visit(function);
catch ex: Exception
debug_always("{function.location} caught {ex}");
_logger.poison(function.location, "oops: {ex.message}");
_logger.exception(function.location, ex, "exception walking lambda");
yrt
si

_visit(function: Trees.Expressions.FUNCTION) is
if !function.value? then
function.value = IR.Values.WRAPPER();
fi

let closure = cast Semantic.Symbols.Closure(scope_for(function));

closure.map_type_arguments();

let implied_type: Type;
let implied_argument_types: Collections.List[Type];

Expand Down Expand Up @@ -805,9 +796,6 @@ namespace Syntax.Process is
else
return_type = Semantic.Types.INFERED_RETURN_TYPE();
fi

// FIXME is this needed? it's not referenced after this point
argument_types.add(return_type);

closure.argument_names = argument_names;

Expand Down Expand Up @@ -1297,7 +1285,7 @@ namespace Syntax.Process is
);
si

resolve_constructor(location: LOCATION, right_location: LOCATION, type: Semantic.Types.Type, argument_expressions: Trees.Expressions.LIST) -> Value is
resolve_constructor(location: LOCATION, right_location: LOCATION, type: Semantic.Types.Type, argument_expressions: Trees.Expressions.LIST) -> (Value, Value) is
let result: Value;

let named_type = cast Semantic.Types.NAMED(type);
Expand Down Expand Up @@ -1328,23 +1316,15 @@ namespace Syntax.Process is
od

if !function_group? then
_logger.error(location, "no constructor found init({argument_types|})");
_logger.error(location, "no constructor found init({argument_types|})");

return
DUMMY(
type,
location
);
return (cast Value(DUMMY(type, location)), cast Value(DUMMY(type, location)));
fi

let overload_result = _overload_resolver.resolve(location, function_group, argument_types, false, true);
let overload_result = _overload_resolver.resolve(location, function_group, argument_types, true, true);

if overload_result == null then
return
DUMMY(
type,
location
);
return (cast Value(Load.SYMBOL(null, function_group)), cast Value(DUMMY(type, location)));
fi

let function = overload_result.function;
Expand All @@ -1364,12 +1344,7 @@ namespace Syntax.Process is
_symbol_use_locations.add_symbol_use(right_location, function);
_symbol_use_locations.add_symbol_use(right_location, type_symbol.root_unspecialized_symbol);

return
NEW(
type,
function,
arguments
);
return (cast Value(Load.SYMBOL(null, function_group)), cast Value(NEW(type, function, arguments)));
si

visit(unary: Trees.Expressions.UNARY) is
Expand Down Expand Up @@ -2546,7 +2521,7 @@ namespace Syntax.Process is
fi

if call.function.value.is_type_expression then
call.value = resolve_constructor(call.location, call.right_location, call.function.value.type, call.arguments);
(call.function.value, call.value) = resolve_constructor(call.location, call.right_location, call.function.value.type, call.arguments);

return;
fi
Expand Down Expand Up @@ -2685,7 +2660,7 @@ namespace Syntax.Process is
fi
else
if load_symbol? /\ load_symbol.is_type then
call.value = resolve_constructor(call.location, call.right_location, load_symbol.type, call.arguments);
(call.function.value, call.value) = resolve_constructor(call.location, call.right_location, load_symbol.type, call.arguments);
else
_logger.error(call.function.location, "cannot call value of non-function type {call.function.value.type}");
call.value = DUMMY(Semantic.Types.ERROR(), call.location);
Expand Down Expand Up @@ -2960,7 +2935,10 @@ namespace Syntax.Process is
return;
fi

if !`if.branches | .any(b => b? /\ !b.condition?) then
if
!`if.branches | .any(b => b? /\ !b.condition?) /\
(!`if.constraint? \/ !`if.constraint.is_void)
then
// if the if has no else branch, then don't bother reporting any
// semantic errors:
_logger.error(`if.location, "expected else in if expression");
Expand All @@ -2985,12 +2963,18 @@ namespace Syntax.Process is
continue;
fi

if !branch.value.check_is_consumable(_logger, branch.location) then
continue;
if `if.constraint? then
if !branch.value.check_is_consumable_allow_void(_logger, branch.location) then
continue;
fi
else
if !branch.value.check_is_consumable(_logger, branch.location) then
continue;
fi
fi

if `if.constraint? then
if !`if.constraint.is_assignable_from(branch.value.type) then
if `if.constraint? then
if !`if.constraint.is_void /\ !`if.constraint.is_assignable_from(branch.value.type) then
_logger.error(branch.location, string.format(`if.constraint_error_message, branch.value.type, type));
fi
seen_non_null_values = true;
Expand Down
4 changes: 4 additions & 0 deletions src/syntax/process/generate_il.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -1063,6 +1063,10 @@ namespace Syntax.Process is

if `if.want_value /\ b.body.value? then
add(b.body.value);

if `if.constraint? /\ `if.constraint.is_void /\ !b.body.value.type.is_void then
add("pop");
fi
fi

brancher.branch(end);
Expand Down
35 changes: 18 additions & 17 deletions src/syntax/process/signature_help.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ namespace Syntax.Process is
return;
fi

if !(_results?) then
if
!call.location.contains(_target_line, _target_column) \/
call.function.location.contains(_target_line, _target_column)
if !_results? then
if !call.arguments.location.contains(_target_line, _target_column) /\
(!call.location.contains(_target_line, _target_column) \/
call.function.location.contains(_target_line, _target_column))
then
return;
fi
Expand Down Expand Up @@ -133,7 +133,15 @@ namespace Syntax.Process is
return null;
fi

if call.function.value == null then
let load = cast IR.Values.Load.SYMBOL(call.function.value);

if !load? then
return null;
fi

let function_group = cast Semantic.Symbols.FUNCTION_GROUP(load.symbol);

if !function_group? then
return null;
fi

Expand All @@ -147,20 +155,13 @@ namespace Syntax.Process is
fi
od

if isa IR.Values.Load.SYMBOL(call.function.value) then
let load = cast IR.Values.Load.SYMBOL(call.function.value);

if load.symbol? /\ isa Semantic.Symbols.FUNCTION_GROUP(load.symbol) then
let function_group = cast Semantic.Symbols.FUNCTION_GROUP(load.symbol);
let overload_results = _overload_resolver.find_matches(function_group, argument_types);

if overload_results.results.count == 0 then
return null;
fi
let overload_results = _overload_resolver.find_matches(function_group, argument_types);

return overload_results;
fi
if overload_results.results.count == 0 then
return null;
fi

return overload_results;
si

help_for(`new: Trees.Expressions.NEW) -> Semantic.OVERLOAD_MATCHES_RESULT is
Expand Down
8 changes: 7 additions & 1 deletion src/syntax/trees/expressions/let_in.ghul
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Syntax.Trees.Expressions is
use Source;

class LET_IN: Expression is
variables: Variables.LIST;
expression: Expression;
Expand All @@ -26,6 +26,12 @@ namespace Syntax.Trees.Expressions is
fi
si

try_set_constraint(constraint: Semantic.Types.Type, error_message: string) is
if expression? then
expression.try_set_constraint(constraint, error_message);
fi
si

accept(visitor: Visitor) is
visitor.visit(self);
si
Expand Down
4 changes: 4 additions & 0 deletions src/syntax/trees/statements/if.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ namespace Syntax.Trees.Statements is
try_set_constraint(constraint: Semantic.Types.Type, error_message: string) is
self.constraint = constraint;
self.constraint_error_message = error_message;

for b in branches do
b.body.try_set_constraint(constraint, error_message);
od
si

accept(visitor: Visitor) is
Expand Down
Loading