From 71f72f4861f355c943e4bc93d766e86affd07fab Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Mon, 25 Jun 2018 17:35:39 +0200 Subject: [PATCH 1/6] Add &+ &- &* &** operators parsing Assignment operators are supported for &+= &-= &*= --- spec/compiler/codegen/proc_spec.cr | 4 +-- spec/compiler/lexer/lexer_spec.cr | 7 ++-- spec/compiler/normalize/op_assign_spec.cr | 6 ++-- spec/compiler/parser/parser_spec.cr | 11 ++++--- spec/compiler/semantic/block_spec.cr | 4 +-- src/compiler/crystal/syntax/lexer.cr | 39 ++++++++++++++++++++++- src/compiler/crystal/syntax/parser.cr | 18 +++++------ 7 files changed, 67 insertions(+), 22 deletions(-) diff --git a/spec/compiler/codegen/proc_spec.cr b/spec/compiler/codegen/proc_spec.cr index 77580fec22e0..bee6aa1f099c 100644 --- a/spec/compiler/codegen/proc_spec.cr +++ b/spec/compiler/codegen/proc_spec.cr @@ -600,13 +600,13 @@ describe "Code gen: proc" do )).to_i.should eq(1) end - it "passes proc as &-> to method that yields" do + it "passes proc as &(->expr) to method that yields" do run(%( def foo yield end - foo &->{ 123 } + foo &(->{ 123 }) )).to_i.should eq(123) end diff --git a/spec/compiler/lexer/lexer_spec.cr b/spec/compiler/lexer/lexer_spec.cr index db45c7bf16a8..2f228e6e3d62 100644 --- a/spec/compiler/lexer/lexer_spec.cr +++ b/spec/compiler/lexer/lexer_spec.cr @@ -249,10 +249,12 @@ describe "Lexer" do :"|", :"{", :"}", :"?", :":", :"+=", :"-=", :"*=", :"%=", :"&=", :"|=", :"^=", :"**=", :"<<", :">>", :"%", :"&", :"|", :"^", :"**", :"<<=", :">>=", :"~", :"[]", :"[]=", :"[", :"]", :"::", :"<=>", :"=>", :"||=", - :"&&=", :"===", :";", :"->", :"[]?", :"{%", :"{{", :"%}", :"@[", :"!~"] + :"&&=", :"===", :";", :"->", :"[]?", :"{%", :"{{", :"%}", :"@[", :"!~", + :"&+", :"&-", :"&*", :"&**", :"&+=", :"&-=", :"&*="] it_lexes "!@foo", :"!" it_lexes "+@foo", :"+" it_lexes "-@foo", :"-" + it_lexes "&-@foo", :"&-" it_lexes_const "Foo" it_lexes_instance_var "@foo" it_lexes_class_var "@@foo" @@ -260,7 +262,8 @@ describe "Lexer" do it_lexes_symbols [":foo", ":foo!", ":foo?", ":foo=", ":\"foo\"", ":かたな", ":+", ":-", ":*", ":/", ":==", ":<", ":<=", ":>", ":>=", ":!", ":!=", ":=~", ":!~", ":&", ":|", ":^", ":~", ":**", ":>>", ":<<", ":%", ":[]", ":[]?", ":[]=", ":<=>", ":===", - ] + ":&+", ":&-", ":&*", ":&**"] + it_lexes_global_match_data_index ["$1", "$10", "$1?", "$23?"] it_lexes "$~", :"$~" diff --git a/spec/compiler/normalize/op_assign_spec.cr b/spec/compiler/normalize/op_assign_spec.cr index 7c0056960294..5dfe204c0325 100644 --- a/spec/compiler/normalize/op_assign_spec.cr +++ b/spec/compiler/normalize/op_assign_spec.cr @@ -1,8 +1,10 @@ require "../../spec_helper" describe "Normalize: op assign" do - it "normalizes var +=" do - assert_normalize "a = 1; a += 2", "a = 1\na = a + 2" + ["+", "-", "*", "&+", "&-", "&*"].each do |op| + it "normalizes var +=" do + assert_normalize "a = 1; a #{op}= 2", "a = 1\na = a #{op} 2" + end end it "normalizes var ||=" do diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 2caba5fd3de6..3493e4c838e5 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -132,6 +132,7 @@ module Crystal it_parses "~ 1", Call.new(1.int32, "~") it_parses "1 && 2", And.new(1.int32, 2.int32) it_parses "1 || 2", Or.new(1.int32, 2.int32) + it_parses "&- 1", Call.new(1.int32, "&-") it_parses "1 <=> 2", Call.new(1.int32, "<=>", 2.int32) it_parses "1 !~ 2", Call.new(1.int32, "!~", 2.int32) @@ -419,7 +420,7 @@ module Crystal it_parses "f.x = Foo.new", Call.new("f".call, "x=", [Call.new("Foo".path, "new")] of ASTNode) it_parses "f.x = - 1", Call.new("f".call, "x=", [Call.new(1.int32, "-")] of ASTNode) - ["+", "-", "*", "/", "%", "|", "&", "^", "**", "<<", ">>"].each do |op| + ["+", "-", "*", "/", "%", "|", "&", "^", "**", "<<", ">>", "&+", "&-", "&*"].each do |op| it_parses "f.x #{op}= 2", OpAssign.new(Call.new("f".call, "x"), op, 2.int32) end @@ -430,9 +431,10 @@ module Crystal it_parses "def %(); end;", Def.new("%") it_parses "def /(); end;", Def.new("/") - ["<<", "<", "<=", "==", ">>", ">", ">=", "+", "-", "*", "/", "%", "|", "&", "^", "**", "===", "=~", "!~"].each do |op| + ["<<", "<", "<=", "==", ">>", ">", ">=", "+", "-", "*", "/", "%", "|", "&", "^", "**", "===", "=~", "!~", "&+", "&-", "&*", "&**"].each do |op| it_parses "1 #{op} 2", Call.new(1.int32, op, 2.int32) it_parses "n #{op} 2", Call.new("n".call, op, 2.int32) + it_parses "def #{op}(); end", Def.new(op) end ["bar", "+", "-", "*", "/", "<", "<=", "==", ">", ">=", "%", "|", "&", "^", "**", "===", "=~", "!~"].each do |name| @@ -441,7 +443,7 @@ module Crystal it_parses "foo.#{name}(1, 2)", Call.new("foo".call, name, 1.int32, 2.int32) end - ["+", "-", "*", "/", "%", "|", "&", "^", "**", "<<", ">>"].each do |op| + ["+", "-", "*", "/", "%", "|", "&", "^", "**", "<<", ">>", "&+", "&-", "&*"].each do |op| it_parses "a = 1; a #{op}= 1", [Assign.new("a".var, 1.int32), OpAssign.new("a".var, op, 1.int32)] it_parses "a = 1; a #{op}=\n1", [Assign.new("a".var, 1.int32), OpAssign.new("a".var, op, 1.int32)] it_parses "a.b #{op}=\n1", OpAssign.new(Call.new("a".call, "b"), op, 1.int32) @@ -642,7 +644,8 @@ module Crystal assert_syntax_error "#{keyword} ? 1 : 2", "void value expression" assert_syntax_error "+#{keyword}", "void value expression" - ["<<", "<", "<=", "==", ">>", ">", ">=", "+", "-", "*", "/", "%", "|", "&", "^", "**", "==="].each do |op| + ["<<", "<", "<=", "==", ">>", ">", ">=", "+", "-", "*", "/", "%", "|", + "&", "^", "**", "===", "&+", "&-", "&*", "&**"].each do |op| assert_syntax_error "#{keyword} #{op} 1", "void value expression" end diff --git a/spec/compiler/semantic/block_spec.cr b/spec/compiler/semantic/block_spec.cr index 3ddc08928c11..31d508beb2ad 100644 --- a/spec/compiler/semantic/block_spec.cr +++ b/spec/compiler/semantic/block_spec.cr @@ -914,7 +914,7 @@ describe "Block inference" do )) { types["Moo"].types["Bar"] } end - it "passes &->f" do + it "passes &(->f)" do assert_type(%( def foo end @@ -924,7 +924,7 @@ describe "Block inference" do 1 end - bar &->foo + bar &(->foo) )) { int32 } end diff --git a/src/compiler/crystal/syntax/lexer.cr b/src/compiler/crystal/syntax/lexer.cr index ad3d839d3238..c02bc83641e8 100644 --- a/src/compiler/crystal/syntax/lexer.cr +++ b/src/compiler/crystal/syntax/lexer.cr @@ -458,7 +458,21 @@ module Crystal symbol ">" end when '&' - next_char_and_symbol "&" + case next_char + when '+' + next_char_and_symbol "&+" + when '-' + next_char_and_symbol "&-" + when '*' + case next_char + when '*' + next_char_and_symbol "&**" + else + symbol "&*" + end + else + symbol "&" + end when '|' next_char_and_symbol "|" when '^' @@ -574,6 +588,29 @@ module Crystal end when '=' next_char :"&=" + when '+' + case next_char + when '=' + next_char :"&+=" + else + @token.type = :"&+" + end + when '-' + case next_char + when '=' + next_char :"&-=" + else + @token.type = :"&-" + end + when '*' + case next_char + when '*' + next_char :"&**" + when '=' + next_char :"&*=" + else + @token.type = :"&*" + end else @token.type = :"&" end diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index 3dafc20158c6..c1673bbcaaad 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -380,7 +380,7 @@ module Crystal atomic.doc = doc atomic end - when :"+=", :"-=", :"*=", :"/=", :"%=", :"|=", :"&=", :"^=", :"**=", :"<<=", :">>=", :"||=", :"&&=" + when :"+=", :"-=", :"*=", :"/=", :"%=", :"|=", :"&=", :"^=", :"**=", :"<<=", :">>=", :"||=", :"&&=", :"&+=", :"&-=", :"&*=" unexpected_token unless allow_ops break unless can_be_assigned?(atomic) @@ -502,7 +502,7 @@ module Crystal case @token.type when :SPACE next_token - when :"+", :"-" + when :"+", :"-", :"&+", :"&-" check_void_value left, location method = @token.type.to_s @@ -531,13 +531,13 @@ module Crystal end end - parse_operator :mul_or_div, :pow, "Call.new left, method, [right] of ASTNode, name_column_number: method_column_number", ":\"*\", :\"/\", :\"%\"" - parse_operator :pow, :prefix, "Call.new left, method, [right] of ASTNode, name_column_number: method_column_number", ":\"**\"" + parse_operator :mul_or_div, :pow, "Call.new left, method, [right] of ASTNode, name_column_number: method_column_number", %(:"*", :"/", :"%", :"&*") + parse_operator :pow, :prefix, "Call.new left, method, [right] of ASTNode, name_column_number: method_column_number", %(:"**", :"&**") def parse_prefix column_number = @token.column_number case token_type = @token.type - when :"!", :"+", :"-", :"~" + when :"!", :"+", :"-", :"~", :"&-" location = @token.location next_token_skip_space_or_newline check_void_expression_keyword @@ -552,7 +552,7 @@ module Crystal end end - AtomicWithMethodCheck = [:IDENT, :CONST, :"+", :"-", :"*", :"/", :"%", :"|", :"&", :"^", :"**", :"<<", :"<", :"<=", :"==", :"!=", :"=~", :"!~", :">>", :">", :">=", :"<=>", :"===", :"[]", :"[]=", :"[]?", :"["] + AtomicWithMethodCheck = [:IDENT, :CONST, :"+", :"-", :"*", :"/", :"%", :"|", :"&", :"^", :"**", :"<<", :"<", :"<=", :"==", :"!=", :"=~", :"!~", :">>", :">", :">=", :"<=>", :"===", :"[]", :"[]=", :"[]?", :"[", :"&+", :"&-", :"&*", :"&**"] def parse_atomic_with_method location = @token.location @@ -666,7 +666,7 @@ module Crystal atomic = Call.new(atomic, "#{name}=", [arg] of ASTNode, name_column_number: name_column_number).at(location) next - when :"+=", :"-=", :"*=", :"/=", :"%=", :"|=", :"&=", :"^=", :"**=", :"<<=", :">>=", :"||=", :"&&=" + when :"+=", :"-=", :"*=", :"/=", :"%=", :"|=", :"&=", :"^=", :"**=", :"<<=", :">>=", :"||=", :"&&=", :"&+=", :"&-=", :"&*=" method = @token.type.to_s.byte_slice(0, @token.type.to_s.size - 1) next_token_skip_space_or_newline value = parse_op_assign @@ -3197,8 +3197,8 @@ module Crystal exp end - DefOrMacroCheck1 = [:IDENT, :CONST, :"<<", :"<", :"<=", :"==", :"===", :"!=", :"=~", :"!~", :">>", :">", :">=", :"+", :"-", :"*", :"/", :"!", :"~", :"%", :"&", :"|", :"^", :"**", :"[]", :"[]=", :"<=>", :"[]?"] - DefOrMacroCheck2 = [:"<<", :"<", :"<=", :"==", :"===", :"!=", :"=~", :"!~", :">>", :">", :">=", :"+", :"-", :"*", :"/", :"!", :"~", :"%", :"&", :"|", :"^", :"**", :"[]", :"[]?", :"[]=", :"<=>"] + DefOrMacroCheck1 = [:IDENT, :CONST, :"<<", :"<", :"<=", :"==", :"===", :"!=", :"=~", :"!~", :">>", :">", :">=", :"+", :"-", :"*", :"/", :"!", :"~", :"%", :"&", :"|", :"^", :"**", :"[]", :"[]=", :"<=>", :"[]?", :"&+", :"&-", :"&*", :"&**"] + DefOrMacroCheck2 = [:"<<", :"<", :"<=", :"==", :"===", :"!=", :"=~", :"!~", :">>", :">", :">=", :"+", :"-", :"*", :"/", :"!", :"~", :"%", :"&", :"|", :"^", :"**", :"[]", :"[]?", :"[]=", :"<=>", :"&+", :"&-", :"&*", :"&**"] def parse_def_helper(is_abstract = false) push_def From 12e0e547cee40a4cbcf3ca4d3e8bd413951aa6c3 Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Wed, 4 Jul 2018 12:21:45 +0100 Subject: [PATCH 2/6] Update test description --- spec/compiler/normalize/op_assign_spec.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/compiler/normalize/op_assign_spec.cr b/spec/compiler/normalize/op_assign_spec.cr index 5dfe204c0325..2a91ef841bfc 100644 --- a/spec/compiler/normalize/op_assign_spec.cr +++ b/spec/compiler/normalize/op_assign_spec.cr @@ -2,7 +2,7 @@ require "../../spec_helper" describe "Normalize: op assign" do ["+", "-", "*", "&+", "&-", "&*"].each do |op| - it "normalizes var +=" do + it "normalizes var #{op}=" do assert_normalize "a = 1; a #{op}= 2", "a = 1\na = a #{op} 2" end end From 25cab7c1449c819013b865ce5019290d7fd6cd84 Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Wed, 4 Jul 2018 18:59:19 +0100 Subject: [PATCH 3/6] Add lookahead to parse &->expr as &(->expr) --- spec/compiler/codegen/proc_spec.cr | 4 ++-- spec/compiler/parser/parser_spec.cr | 2 ++ src/compiler/crystal/syntax/lexer.cr | 15 +++++++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/spec/compiler/codegen/proc_spec.cr b/spec/compiler/codegen/proc_spec.cr index bee6aa1f099c..4e08c075cb51 100644 --- a/spec/compiler/codegen/proc_spec.cr +++ b/spec/compiler/codegen/proc_spec.cr @@ -600,13 +600,13 @@ describe "Code gen: proc" do )).to_i.should eq(1) end - it "passes proc as &(->expr) to method that yields" do + it "passes proc as &->expr to method that yields" do run(%( def foo yield end - foo &(->{ 123 }) + foo &->{ 123 } )).to_i.should eq(123) end diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 3493e4c838e5..168b18344183 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -1085,6 +1085,8 @@ module Crystal it_parses "->foo=", ProcPointer.new(nil, "foo=") it_parses "foo = 1; ->foo.foo=", [Assign.new("foo".var, 1.int32), ProcPointer.new("foo".var, "foo=")] + it_parses "foo &->bar", Call.new(nil, "foo", block_arg: ProcPointer.new(nil, "bar")) + it_parses "foo.bar = {} of Int32 => Int32", Call.new("foo".call, "bar=", HashLiteral.new(of: HashLiteral::Entry.new("Int32".path, "Int32".path))) it_parses "alias Foo = Bar", Alias.new("Foo", "Bar".path) diff --git a/src/compiler/crystal/syntax/lexer.cr b/src/compiler/crystal/syntax/lexer.cr index c02bc83641e8..915ab8da7381 100644 --- a/src/compiler/crystal/syntax/lexer.cr +++ b/src/compiler/crystal/syntax/lexer.cr @@ -596,11 +596,18 @@ module Crystal @token.type = :"&+" end when '-' - case next_char - when '=' - next_char :"&-=" + # Check if '>' comes after '&-', making it '&->'. + # We want to parse that like '&(->...)', + # so we only return '&' for now. + if peek_next_char == '>' + @token.type = :"&" else - @token.type = :"&-" + case next_char + when '=' + next_char :"&-=" + else + @token.type = :"&-" + end end when '*' case next_char From ad0a3c85cebfccf94d185422a32edacbde3102e9 Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Wed, 4 Jul 2018 19:02:58 +0100 Subject: [PATCH 4/6] Allow &+ as prefix operator --- spec/compiler/lexer/lexer_spec.cr | 1 + spec/compiler/parser/parser_spec.cr | 1 + src/compiler/crystal/syntax/parser.cr | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/compiler/lexer/lexer_spec.cr b/spec/compiler/lexer/lexer_spec.cr index 2f228e6e3d62..9cb5ece89473 100644 --- a/spec/compiler/lexer/lexer_spec.cr +++ b/spec/compiler/lexer/lexer_spec.cr @@ -254,6 +254,7 @@ describe "Lexer" do it_lexes "!@foo", :"!" it_lexes "+@foo", :"+" it_lexes "-@foo", :"-" + it_lexes "&+@foo", :"&+" it_lexes "&-@foo", :"&-" it_lexes_const "Foo" it_lexes_instance_var "@foo" diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 168b18344183..ad01bc646091 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -133,6 +133,7 @@ module Crystal it_parses "1 && 2", And.new(1.int32, 2.int32) it_parses "1 || 2", Or.new(1.int32, 2.int32) it_parses "&- 1", Call.new(1.int32, "&-") + it_parses "&+ 1", Call.new(1.int32, "&+") it_parses "1 <=> 2", Call.new(1.int32, "<=>", 2.int32) it_parses "1 !~ 2", Call.new(1.int32, "!~", 2.int32) diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index c1673bbcaaad..165bf862bc8b 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -537,7 +537,7 @@ module Crystal def parse_prefix column_number = @token.column_number case token_type = @token.type - when :"!", :"+", :"-", :"~", :"&-" + when :"!", :"+", :"-", :"~", :"&+", :"&-" location = @token.location next_token_skip_space_or_newline check_void_expression_keyword From 96db8fd1ec709a138b4e4531123000fb16a9ab97 Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Wed, 4 Jul 2018 20:05:48 +0100 Subject: [PATCH 5/6] Add formatter specs and rules --- spec/compiler/formatter/formatter_spec.cr | 12 +++++++++++- src/compiler/crystal/tools/formatter.cr | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/spec/compiler/formatter/formatter_spec.cr b/spec/compiler/formatter/formatter_spec.cr index 226365bdd3b1..b5129e29ccd5 100644 --- a/spec/compiler/formatter/formatter_spec.cr +++ b/spec/compiler/formatter/formatter_spec.cr @@ -296,6 +296,7 @@ describe Crystal::Formatter do assert_format "with foo yield bar" assert_format "1 + 2", "1 + 2" + assert_format "1 &+ 2", "1 &+ 2" assert_format "1 > 2", "1 > 2" assert_format "1 * 2", "1 * 2" assert_format "1*2", "1*2" @@ -312,14 +313,22 @@ describe Crystal::Formatter do assert_format "- 1", "-1" assert_format "~ 1", "~1" assert_format "+ 1", "+1" + assert_format "&- 1", "&-1" + assert_format "&+ 1", "&+1" assert_format "a-1", "a - 1" assert_format "a+1", "a + 1" + assert_format "a&-1", "a &- 1" + assert_format "a&+1", "a &+ 1" assert_format "1 + \n2", "1 +\n 2" assert_format "1 + # foo\n2", "1 + # foo\n 2" assert_format "a = 1 + # foo\n2", "a = 1 + # foo\n 2" assert_format "1+2*3", "1 + 2*3" + assert_format "1&+2&*3", "1 &+ 2 &* 3" assert_format "foo(1 + \n2)", "foo(1 +\n 2)" + assert_format "foo(1 &+ \n2)", "foo(1 &+\n 2)" + + assert_format "foo(1 &- 2)" assert_format "foo[]", "foo[]" assert_format "foo[ 1 , 2 ]", "foo[1, 2]" @@ -650,7 +659,8 @@ describe Crystal::Formatter do assert_format "->( x , y ) { x }", "->(x, y) { x }" assert_format "->( x : Int32 , y ) { x }", "->(x : Int32, y) { x }" - {:+, :-, :*, :/, :^, :>>, :<<, :|, :&}.each do |sym| + # TODO remove quotes after 0.26.0 + {:+, :-, :*, :/, :^, :>>, :<<, :|, :&, :"&+", :"&-", :"&*", :"&**"}.each do |sym| assert_format ":#{sym}" end assert_format ":\"foo bar\"" diff --git a/src/compiler/crystal/tools/formatter.cr b/src/compiler/crystal/tools/formatter.cr index 64d964a5a955..68bf3bc11c85 100644 --- a/src/compiler/crystal/tools/formatter.cr +++ b/src/compiler/crystal/tools/formatter.cr @@ -2145,7 +2145,7 @@ module Crystal write_token :"::" if node.global? if obj - {:"!", :"+", :"-", :"~"}.each do |op| + {:"!", :"+", :"-", :"~", :"&+", :"&-"}.each do |op| if node.name == op.to_s && @token.type == op && node.args.empty? write op next_token_skip_space_or_newline From d3b1c0de31e2ac2de2fda715d0a5cee6c0080fca Mon Sep 17 00:00:00 2001 From: "Brian J. Cardiff" Date: Thu, 5 Jul 2018 16:37:46 +0100 Subject: [PATCH 6/6] Rollback changes in semantic/block_spec.cr --- spec/compiler/semantic/block_spec.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/compiler/semantic/block_spec.cr b/spec/compiler/semantic/block_spec.cr index 31d508beb2ad..3ddc08928c11 100644 --- a/spec/compiler/semantic/block_spec.cr +++ b/spec/compiler/semantic/block_spec.cr @@ -914,7 +914,7 @@ describe "Block inference" do )) { types["Moo"].types["Bar"] } end - it "passes &(->f)" do + it "passes &->f" do assert_type(%( def foo end @@ -924,7 +924,7 @@ describe "Block inference" do 1 end - bar &(->foo) + bar &->foo )) { int32 } end