Skip to content

Commit

Permalink
Fix error cascade when undefined type used as formal argument (#1076)
Browse files Browse the repository at this point in the history
Bugs fixed:
- Formal arguments of undefined type can case a cascade of spurious errors (closes #1075)

Technical:
- Bump compiler tool to latest
- Bump integration test runner tool to latest
- Bump runtime library to latest
- Replace instances of deprecated `| .has(...)` with `| .any(...)`
  • Loading branch information
degory authored Feb 20, 2024
1 parent be3aef5 commit 8c9df61
Show file tree
Hide file tree
Showing 28 changed files with 92 additions and 31 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.1",
"version": "1.3.2",
"commands": [
"ghul-test"
]
},
"ghul.compiler": {
"version": "0.7.8",
"version": "0.7.9",
"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.7.9-alpha.71</Version>
<Version>0.7.9-alpha.74</Version>
<NoWarn>$(NoWarn);NU1507</NoWarn>
</PropertyGroup>

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.1" />
<PackageVersion Include="ghul.runtime" Version="1.3.2" />

<!-- 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
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test.ghul: 3,26..3,40: error: symbol not found: undefined_type
test.ghul: 8,26..8,52: error: symbol not found: a_different_undefined_type
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"compiler": "dotnet ../../../publish/ghul.dll",
"source": [
"."
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--type-check
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Test is
class A is
some_method(arg: undefined_type) is
si
si

class B is
some_method(arg: a_different_undefined_type) is
si
si
si
Empty file.
10 changes: 5 additions & 5 deletions src/logging/diagnostics_store.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ namespace Logging is
_diagnostics: LIST[DIAGNOSTIC_MESSAGE];

is_poisoned: bool;
any_errors: bool => _diagnostics | .has(d => d.severity == DIAGNOSTIC_SEVERITY.ERROR);
any_warnings: bool => _diagnostics | .has(d => d.severity == DIAGNOSTIC_SEVERITY.WARN);
any_errors: bool => _diagnostics | .any(d => d.severity == DIAGNOSTIC_SEVERITY.ERROR);
any_warnings: bool => _diagnostics | .any(d => d.severity == DIAGNOSTIC_SEVERITY.WARN);

count: int => _diagnostics.count;
diagnostics: List[DIAGNOSTIC_MESSAGE] => _diagnostics;
Expand Down Expand Up @@ -217,9 +217,9 @@ namespace Logging is

class DIAGNOSTICS_STATE is
count: int => _diagnostics_by_source_path.values | .reduce(0, (r, d) => r + d.count);
is_poisoned: bool => _diagnostics_by_source_path.values | .has(d => d.is_poisoned);
any_errors: bool => _diagnostics_by_source_path.values | .has(d => d.any_errors);
any_warnings: bool => _diagnostics_by_source_path.values | .has(d => d.any_warnings);
is_poisoned: bool => _diagnostics_by_source_path.values | .any(d => d.is_poisoned);
any_errors: bool => _diagnostics_by_source_path.values | .any(d => d.any_errors);
any_warnings: bool => _diagnostics_by_source_path.values | .any(d => d.any_warnings);

_diagnostics_by_source_path: MutableMap[string, DIAGNOSTICS_LIST];

Expand Down
4 changes: 2 additions & 2 deletions src/semantic/dotnet/symbol_factory.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ namespace Semantic.DotNet is
type.is_generic_type /\
type.contains_generic_parameters /\
type.get_generic_arguments() |
.has(t =>
.any(t =>
t.is_generic_type_parameter /\
!are_generic_parameter_constraints_safe(t)
)
Expand All @@ -204,7 +204,7 @@ namespace Semantic.DotNet is
si

check_constraints_for_method(owner: Symbols.Symbol, symbol: Symbols.Scoped, generic_arguments: Collections.Iterable[TYPE]) is
if generic_arguments | .has(t => !are_generic_parameter_constraints_safe(t)) then
if generic_arguments | .any(t => !are_generic_parameter_constraints_safe(t)) then
symbol.is_unsafe_constraints = true;
fi
si
Expand Down
6 changes: 3 additions & 3 deletions src/semantic/overload_resolver.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ namespace Semantic is
fi

// FIXME: this could just as well be applied to any parameters of generic type, not just anon functions
let needs_second_call = want_infer /\ arguments | .has(a => a? /\ a.is_function_with_any_implicit_argument_types);
let needs_second_call = want_infer /\ arguments | .any(a => a? /\ a.is_function_with_any_implicit_argument_types);

let is_ambiguous = false;

Expand Down Expand Up @@ -180,7 +180,7 @@ namespace Semantic is
if is_ambiguous then
let non_object_matches =
ambiguous_matches |
.filter(f => !f.arguments | .has(a => a.is_object));
.filter(f => !f.arguments | .any(a => a.is_object));

let count = non_object_matches | .count();

Expand Down Expand Up @@ -216,7 +216,7 @@ namespace Semantic is
fi

if
arguments | .has(a => a.is_error \/ (!want_infer /\ a.is_any))
arguments | .any(a => a.is_error \/ (!want_infer /\ a.is_any))
then
return null;
fi
Expand Down
17 changes: 16 additions & 1 deletion src/semantic/symbols/function.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ namespace Semantic.Symbols is
use Types.Type;

class Function: ScopedWithEnclosingScope, Types.Typed is
_arguments: Collections.List[Type];

_seen_entrypoint: bool static;

_declaring_arguments: bool;
Expand All @@ -36,7 +38,20 @@ namespace Semantic.Symbols is

type: Type public;
return_type: Type public;
arguments: Collections.List[Type] public;
arguments: Collections.List[Type] public => _arguments,
= value is
// trust that any source of null arguments has already reported an error
// and replace the nulls with errors
if value.count > 0 /\ value | .any(a => !a?) then
let from = new System.Diagnostics.StackTrace();

IoC.CONTAINER.instance.logger.poison(location, "function at " + location + " has at least one null argument");

value = value | .map(a => if a? then a else new Types.ERROR() fi) .collect();
fi

_arguments = value;
si

generic_arguments: Collections.List[Type] public;
generic_argument_names: Collections.List[string] public;
Expand Down
2 changes: 2 additions & 0 deletions src/semantic/symbols/method_override_class.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ namespace Semantic is

init(arguments: List[Type]) is
assert arguments? else "override class arguments list is null";
assert arguments | .all(a => a?) else "override class arguments list contains null elements";

self.arguments = arguments;
si

Expand Down
2 changes: 1 addition & 1 deletion src/semantic/symbols/symbol_inheritance_resolver.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ namespace Semantic is
if
seen_multiple_concrete /\
overridees.iterable |
.has(function => function.is_instance /\ !function.is_reflected)
.any(function => function.is_instance /\ !function.is_reflected)
then
let concretes =
overridees.iterable |
Expand Down
4 changes: 2 additions & 2 deletions src/semantic/types/generic.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ namespace Semantic.Types is

arguments: Collections.List[Type] => symbol.arguments;

is_error: bool => arguments | .has(a => a.is_error);
is_error: bool => arguments | .any(a => a.is_error);

// should be
// is_wild: bool => arguments | .has(a => a.is_wild)
// is_wild: bool => arguments | .any(a => a.is_wild)
// but the following shaves about 10% off compiler build time (nearly 10% at time of change)
is_wild: bool is
if _is_wild == 0b then
Expand Down
4 changes: 2 additions & 2 deletions src/syntax/parsers/context.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ namespace Syntax.Parsers is
if is_end_of_file then
error(start::location, message + ": expected " + Lexical.TOKEN_NAMES[current_token]);
return;
elif tokens | .has(t => t == current_token) then
elif tokens | .any(t => t == current_token) then
error(start::location, message + ": expected " + Lexical.TOKEN_NAMES[current_token]);
next_token();
return;
Expand Down Expand Up @@ -154,7 +154,7 @@ namespace Syntax.Parsers is
si

expect_token(tokens: Collections.List[Lexical.TOKEN], message: string) -> bool is
if !tokens | .has(t => t == current_token) then
if !tokens | .any(t => t == current_token) then
error(location, string.format("{0}: expected {1} but found {2}", [message, Lexical.TOKEN_NAMES[tokens], current_token_name]: object));

return false;
Expand Down
2 changes: 1 addition & 1 deletion src/syntax/parsers/definitions/member.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ namespace Syntax.Parsers.Definitions is
context.mark();
context.next_token();

if property_tokens | .has(t => t == context.current.token) then
if property_tokens | .any(t => t == context.current.token) then
context.backtrack();
return property_parser.parse(context);
elif
Expand Down
4 changes: 2 additions & 2 deletions src/syntax/parsers/statements/list.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ namespace Syntax.Parsers.Statements is
catch e: Exception
end = context.current.location;

while !terminators | .has(t => t == context.current_token) do
while !terminators | .any(t => t == context.current_token) do
end = context.current.location;

context.next_token();
Expand All @@ -66,6 +66,6 @@ namespace Syntax.Parsers.Statements is
return new Trees.Statements.LIST(start::end, statements);
si

at_terminator(context: CONTEXT) -> bool => terminators | .has(t => t == context.current_token);
at_terminator(context: CONTEXT) -> bool => terminators | .any(t => t == context.current_token);
si
si
2 changes: 1 addition & 1 deletion src/syntax/parsers/statements/node.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ namespace Syntax.Parsers.Statements is
let expression: Trees.Expressions.Expression;
if
context.current.token != Lexical.TOKEN.SEMICOLON /\
primary_expression_tokens | .has(t => t == context.current.token)
primary_expression_tokens | .any(t => t == context.current.token)
then
expression = expression_parser.parse(context);
end = expression.location;
Expand Down
4 changes: 2 additions & 2 deletions src/syntax/process/compile_expressions.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ namespace Syntax.Process is
function.body.walk(self);
fi

if closure.return_type.is_inferred /\ ! closure.arguments | .has(a => a.is_error) then
if closure.return_type.is_inferred /\ ! closure.arguments | .any(a => a.is_error) then
closure.set_return_type(_innate_symbol_lookup.get_void_type());
fi

Expand Down Expand Up @@ -2133,7 +2133,7 @@ namespace Syntax.Process is
return;
fi

if !`if.branches | .has(b => b? /\ !b.condition?) then
if !`if.branches | .any(b => b? /\ !b.condition?) 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 Down
2 changes: 1 addition & 1 deletion src/syntax/process/resolve_explicit_variable_types.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ namespace Syntax.Process is
for argument in function.arguments do
let type = argument.type_expression.type;

types.add(type);
types.add(if type? then type else new Semantic.Types.ERROR() fi);

names.add(argument.name.name);
od
Expand Down
2 changes: 1 addition & 1 deletion src/syntax/process/resolve_uses.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ namespace Syntax.Process is
elif used_symbol.is_function_group then
let fg = cast Semantic.Symbols.FUNCTION_GROUP(used_symbol);

if !fg.functions | .has(f => !f.is_instance) then
if !fg.functions | .any(f => !f.is_instance) then
if fg.functions.count == 1 then
_logger.error(u.`use.location, "cannot use instance function");
else
Expand Down
2 changes: 1 addition & 1 deletion src/syntax/trees/type_expressions/function.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Syntax.Trees.TypeExpressions is
arguments: LIST;

is_void: bool =>
arguments | .has(a => a? /\ a.is_void);
arguments | .any(a => a? /\ a.is_void);

init(location: LOCATION, arguments: LIST, result: TypeExpression) is
super.init(location);
Expand Down
2 changes: 1 addition & 1 deletion src/syntax/trees/type_expressions/generic.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Syntax.Trees.TypeExpressions is
arguments: TypeExpressions.LIST;

is_void: bool =>
arguments | .has(e => e? /\ e.is_void);
arguments | .any(e => e? /\ e.is_void);

init(location: LOCATION, name: Identifiers.Identifier, arguments: TypeExpressions.LIST) is
super.init(location, name);
Expand Down
2 changes: 1 addition & 1 deletion src/syntax/trees/type_expressions/tuple.ghul
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Syntax.Trees.TypeExpressions is
elements: LIST;

is_void: bool =>
elements | .has(e => e? /\ e.is_void);
elements | .any(e => e? /\ e.is_void);

init(location: LOCATION, elements: LIST) is
super.init(location);
Expand Down

0 comments on commit 8c9df61

Please sign in to comment.