Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

Improve inlining output for anonymous functions

+239 -123
+3 -12
compiler-core/src/erlang/snapshots/gleam_core__erlang__tests__integration_test1_5.snap
··· 21 21 -spec go() -> integer(). 22 22 go() -> 23 23 _pipe = 1, 24 - _pipe@1 = begin 25 - _capture = _pipe, 26 - add(_capture, 1) 27 - end, 28 - _pipe@2 = begin 29 - _capture@1 = _pipe@1, 30 - add(2, _capture@1) 31 - end, 32 - begin 33 - _capture@2 = _pipe@2, 34 - add(_capture@2, 3) 35 - end. 24 + _pipe@1 = add(_pipe, 1), 25 + _pipe@2 = add(2, _pipe@1), 26 + add(_pipe@2, 3).
+11
compiler-core/src/erlang/tests/inlining.rs
··· 267 267 " 268 268 ); 269 269 } 270 + 271 + #[test] 272 + fn blocks_get_preserved_when_needed2() { 273 + assert_erl!( 274 + " 275 + pub fn main() { 276 + fn(x) { 1 + x }(2) * 3 277 + } 278 + " 279 + ); 280 + }
+21
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__blocks_get_preserved_when_needed2.snap
··· 1 + --- 2 + source: compiler-core/src/erlang/tests/inlining.rs 3 + expression: "\npub fn main() {\n fn(x) { 1 + x }(2) * 3\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn main() { 8 + fn(x) { 1 + x }(2) * 3 9 + } 10 + 11 + 12 + ----- COMPILED ERLANG 13 + -module(my@mod). 14 + -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). 15 + -define(FILEPATH, "project/test/my/mod.gleam"). 16 + -export([main/0]). 17 + 18 + -file("project/test/my/mod.gleam", 2). 19 + -spec main() -> integer(). 20 + main() -> 21 + (1 + 2) * 3.
+1 -5
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__inline_anonymous_function_call.snap
··· 18 18 -file("project/test/my/mod.gleam", 2). 19 19 -spec main() -> {integer(), boolean()}. 20 20 main() -> 21 - begin 22 - A = 42, 23 - B = false, 24 - {A, B} 25 - end. 21 + {42, false}.
+1 -4
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__inline_anonymous_function_in_pipe.snap
··· 19 19 -spec main() -> integer(). 20 20 main() -> 21 21 _pipe = 1, 22 - _pipe@1 = begin 23 - X = _pipe, 24 - X + 1 25 - end, 22 + _pipe@1 = _pipe + 1, 26 23 begin 27 24 Y = _pipe@1, 28 25 Y * Y
+1 -4
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__inline_function_capture_in_pipe.snap
··· 26 26 -spec main() -> integer(). 27 27 main() -> 28 28 _pipe = 1, 29 - begin 30 - _capture = _pipe, 31 - add(4, _capture) 32 - end. 29 + add(4, _pipe).
+2 -2
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__inline_function_which_calls_other_function.snap
··· 20 20 -file("project/test/my/mod.gleam", 4). 21 21 -spec main() -> {ok, any()} | {error, integer()}. 22 22 main() -> 23 - ((case {ok, 10} of 23 + case {ok, 10} of 24 24 {ok, Value} -> 25 25 {error, Value}; 26 26 27 27 {error, Error} -> 28 28 {error, Error} 29 - end)). 29 + end.
+2 -2
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__inline_function_with_use.snap
··· 21 21 -file("project/test/my/mod.gleam", 4). 22 22 -spec divide(integer(), integer()) -> integer(). 23 23 divide(A, B) -> 24 - (case B =:= 0 of 24 + case B =:= 0 of 25 25 true -> 26 26 0; 27 27 ··· 30 30 0 -> 0; 31 31 Gleam@denominator -> A div Gleam@denominator 32 32 end 33 - end). 33 + end.
+2 -2
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__inline_function_with_use_and_anonymous.snap
··· 21 21 -file("project/test/my/mod.gleam", 4). 22 22 -spec divide(integer(), integer()) -> integer(). 23 23 divide(A, B) -> 24 - (case B =:= 0 of 24 + case B =:= 0 of 25 25 true -> 26 26 erlang:error(#{gleam_error => panic, 27 27 message => <<"Cannot divide by 0"/utf8>>, ··· 35 35 0 -> 0; 36 36 Gleam@denominator -> A div Gleam@denominator 37 37 end 38 - end). 38 + end.
+2 -2
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__inline_function_with_use_becomes_tail_recursive.snap
··· 22 22 -file("project/test/my/mod.gleam", 4). 23 23 -spec count(integer(), integer()) -> integer(). 24 24 count(From, To) -> 25 - (case From >= To of 25 + case From >= To of 26 26 true -> 27 27 From; 28 28 29 29 false -> 30 30 echo(From, 6), 31 31 count(From + 1, To) 32 - end). 32 + end. 33 33 34 34 % ...omitted code from `templates/echo.erl`...
+2 -2
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__inline_higher_order_function.snap
··· 27 27 -file("project/test/my/mod.gleam", 4). 28 28 -spec main() -> {ok, integer()} | {error, any()}. 29 29 main() -> 30 - (case {ok, 10} of 30 + case {ok, 10} of 31 31 {ok, Value} -> 32 32 {ok, double(Value)}; 33 33 34 34 {error, Error} -> 35 35 {error, Error} 36 - end). 36 + end.
+3 -4
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__inline_higher_order_function_anonymous.snap
··· 22 22 -file("project/test/my/mod.gleam", 4). 23 23 -spec main() -> {ok, integer()} | {error, any()}. 24 24 main() -> 25 - (case {ok, 10} of 25 + case {ok, 10} of 26 26 {ok, Value} -> 27 - Value@1 = Value, 28 - {ok, (Value@1 + 2) * 4}; 27 + {ok, (Value + 2) * 4}; 29 28 30 29 {error, Error} -> 31 30 {error, Error} 32 - end). 31 + end.
+3 -4
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__inline_higher_order_function_with_capture.snap
··· 44 44 -file("project/test/my/mod.gleam", 4). 45 45 -spec main() -> {ok, integer()} | {error, nil}. 46 46 main() -> 47 - (case {ok, 10} of 47 + case {ok, 10} of 48 48 {ok, Value} -> 49 - _capture = Value, 50 - divide(_capture, 2); 49 + divide(Value, 2); 51 50 52 51 {error, Error} -> 53 52 {error, Error} 54 - end). 53 + end.
+1 -4
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__inlining__inlining_works_through_blocks.snap
··· 18 18 -file("project/test/my/mod.gleam", 2). 19 19 -spec main() -> {ok, integer()} | {error, any()}. 20 20 main() -> 21 - begin 22 - X = 41, 23 - {ok, X + 1} 24 - end. 21 + {ok, 41 + 1}.
+7 -10
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__pipes__pipe_in_call.snap
··· 32 32 -spec main() -> integer(). 33 33 main() -> 34 34 _pipe = 123, 35 - begin 36 - _capture = _pipe, 37 - two( 38 - begin 39 - _pipe@1 = 1, 40 - two(_pipe@1, 2) 41 - end, 42 - _capture 43 - ) 44 - end. 35 + two( 36 + begin 37 + _pipe@1 = 1, 38 + two(_pipe@1, 2) 39 + end, 40 + _pipe@1 41 + ).
+1 -4
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__variables__shadow_and_call.snap
··· 18 18 -file("project/test/my/mod.gleam", 2). 19 19 -spec main(K) -> K. 20 20 main(X) -> 21 - begin 22 - X@1 = X, 23 - X@1 24 - end. 21 + X.
+1 -4
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__variables__shadow_pipe.snap
··· 20 20 -spec main(K) -> K. 21 21 main(X) -> 22 22 _pipe = X, 23 - begin 24 - X@1 = _pipe, 25 - X@1 26 - end. 23 + _pipe.
+138 -11
compiler-core/src/inline.rs
··· 118 118 STDLIB_PACKAGE_NAME, 119 119 analyse::Inferred, 120 120 ast::{ 121 - ArgNames, Assert, Assignment, AssignmentKind, BitArrayOption, BitArraySegment, CallArg, 122 - Definition, FunctionLiteralKind, PipelineAssignmentKind, Publicity, SrcSpan, Statement, 123 - TypedArg, TypedAssert, TypedAssignment, TypedClause, TypedDefinition, TypedExpr, 121 + self, ArgNames, Assert, Assignment, AssignmentKind, BitArrayOption, BitArraySegment, 122 + CallArg, Definition, FunctionLiteralKind, PipelineAssignmentKind, Publicity, SrcSpan, 123 + Statement, TypedArg, TypedAssert, TypedAssignment, TypedClause, TypedDefinition, TypedExpr, 124 124 TypedExprBitArraySegment, TypedFunction, TypedModule, TypedPattern, 125 - TypedPipelineAssignment, TypedStatement, TypedUse, 125 + TypedPipelineAssignment, TypedStatement, TypedUse, visit::Visit, 126 126 }, 127 127 exhaustiveness::CompiledCase, 128 128 type_::{ 129 129 self, Deprecation, ModuleInterface, ModuleValueConstructor, PRELUDE_MODULE_NAME, 130 - PatternConstructor, Type, TypedCallArg, ValueConstructorVariant, collapse_links, 130 + PatternConstructor, Type, TypedCallArg, ValueConstructor, ValueConstructorVariant, 131 + collapse_links, 131 132 error::VariableOrigin, 132 133 expression::{Implementations, Purity}, 133 134 }, ··· 666 667 body, 667 668 .. 668 669 } => { 669 - return self.inline_anonymous_function_call(&parameters, arguments, body, &[]); 670 + let inlinable_parameters = find_inlinable_parameters(&parameters, &body); 671 + return self.inline_anonymous_function_call( 672 + &parameters, 673 + arguments, 674 + body, 675 + &inlinable_parameters, 676 + ); 670 677 } 671 678 TypedExpr::Int { .. } 672 679 | TypedExpr::Float { .. } ··· 760 767 761 768 self.inline_variables = inline_variables; 762 769 763 - TypedExpr::Block { 770 + // We try to expand this block, so a function which is inlined as a 771 + // single expression does not get wrapped unnecessarily 772 + expand_block(TypedExpr::Block { 764 773 location: BLANK_LOCATION, 765 774 statements: statements 766 775 .try_into() 767 776 .expect("Type checking ensures there is at least one statement"), 768 - } 777 + }) 769 778 } 770 779 771 780 fn pipeline( ··· 910 919 } 911 920 } 912 921 922 + fn find_inlinable_parameters(parameters: &[TypedArg], body: &[TypedStatement]) -> Vec<EcoString> { 923 + let mut parameter_map = HashMap::new(); 924 + for parameter in parameters { 925 + let (name, location) = match &parameter.names { 926 + ArgNames::Discard { .. } | ArgNames::LabelledDiscard { .. } => continue, 927 + ArgNames::Named { name, location } 928 + | ArgNames::NamedLabelled { 929 + name, 930 + name_location: location, 931 + .. 932 + } => (name, location), 933 + }; 934 + _ = parameter_map.insert((name.clone(), *location), false); 935 + } 936 + 937 + let mut finder = FindInlinableParameters { 938 + parameters: parameter_map, 939 + }; 940 + for statement in body { 941 + finder.visit_typed_statement(statement); 942 + } 943 + 944 + // Inlinable parameters are those that are used exactly once. Any parameters 945 + // used more than once will be removed from this map and so will not be 946 + // considered inlinable. 947 + finder 948 + .parameters 949 + .into_iter() 950 + .filter_map(|((name, _), used)| used.then_some(name)) 951 + .collect() 952 + } 953 + 954 + /// A struct for finding the inlinable parameters of an anonymous function. Since 955 + /// we want to inline all anonymous functions, not just a subset of them like we 956 + /// do with regular functions, this must be implemented slightly differently, but 957 + /// it means we can take advantage of the AST visitor, since we don't need to 958 + /// transform the anonymous function into an intermediate representation. 959 + struct FindInlinableParameters { 960 + parameters: HashMap<(EcoString, SrcSpan), bool>, 961 + } 962 + 963 + impl FindInlinableParameters { 964 + fn register_reference(&mut self, name: &EcoString, location: SrcSpan) { 965 + let key = (name.clone(), location); 966 + match self.parameters.get_mut(&key) { 967 + Some(true) => _ = self.parameters.remove(&key), 968 + Some(used @ false) => { 969 + *used = true; 970 + } 971 + None => {} 972 + } 973 + } 974 + } 975 + 976 + impl<'ast> Visit<'ast> for FindInlinableParameters { 977 + fn visit_typed_expr_var( 978 + &mut self, 979 + _location: &'ast SrcSpan, 980 + constructor: &'ast ValueConstructor, 981 + name: &'ast EcoString, 982 + ) { 983 + match constructor.variant { 984 + ValueConstructorVariant::LocalVariable { location, .. } => { 985 + self.register_reference(name, location); 986 + } 987 + _ => {} 988 + } 989 + } 990 + 991 + fn visit_typed_clause_guard_var( 992 + &mut self, 993 + _location: &'ast SrcSpan, 994 + name: &'ast EcoString, 995 + _type_: &'ast Arc<Type>, 996 + location: &'ast SrcSpan, 997 + ) { 998 + self.register_reference(name, *location); 999 + } 1000 + 1001 + fn visit_typed_pattern_var_usage( 1002 + &mut self, 1003 + _location: &'ast SrcSpan, 1004 + name: &'ast EcoString, 1005 + constructor: &'ast Option<ValueConstructor>, 1006 + _type_: &'ast Arc<Type>, 1007 + ) { 1008 + let variant = match constructor { 1009 + Some(constructor) => &constructor.variant, 1010 + None => return, 1011 + }; 1012 + match variant { 1013 + ValueConstructorVariant::LocalVariable { location, .. } => { 1014 + self.register_reference(name, *location); 1015 + } 1016 + _ => {} 1017 + } 1018 + } 1019 + 1020 + fn visit_typed_call_arg(&mut self, arg: &'ast TypedCallArg) { 1021 + if let TypedExpr::Var { 1022 + name, constructor, .. 1023 + } = &arg.value 1024 + { 1025 + match &constructor.variant { 1026 + ValueConstructorVariant::LocalVariable { location, .. } 1027 + if arg.uses_label_shorthand() => 1028 + { 1029 + self.register_reference(name, *location); 1030 + return; 1031 + } 1032 + _ => {} 1033 + } 1034 + } 1035 + 1036 + ast::visit::visit_typed_call_arg(self, arg); 1037 + } 1038 + } 1039 + 913 1040 /// Removes any blocks which are acting as brackets (they hold a single expression) 914 1041 fn expand_block(expression: TypedExpr) -> TypedExpr { 915 1042 match expression { ··· 1198 1325 1199 1326 fn value_constructor( 1200 1327 &mut self, 1201 - constructor: &type_::ValueConstructor, 1328 + constructor: &ValueConstructor, 1202 1329 ) -> Option<InlinableValueConstructor> { 1203 1330 match &constructor.variant { 1204 1331 ValueConstructorVariant::LocalVariable { .. } => { ··· 1479 1606 } 1480 1607 1481 1608 impl InlinableValueConstructor { 1482 - fn to_value_constructor(&self, type_: Arc<Type>) -> type_::ValueConstructor { 1609 + fn to_value_constructor(&self, type_: Arc<Type>) -> ValueConstructor { 1483 1610 let variant = match self { 1484 1611 InlinableValueConstructor::LocalVariable => ValueConstructorVariant::LocalVariable { 1485 1612 location: BLANK_LOCATION, ··· 1510 1637 documentation: None, 1511 1638 }, 1512 1639 }; 1513 - type_::ValueConstructor { 1640 + ValueConstructor { 1514 1641 publicity: Publicity::Private, 1515 1642 deprecation: Deprecation::NotDeprecated, 1516 1643 variant,
+11
compiler-core/src/javascript/tests/inlining.rs
··· 267 267 " 268 268 ); 269 269 } 270 + 271 + #[test] 272 + fn blocks_get_preserved_when_needed2() { 273 + assert_js!( 274 + " 275 + pub fn main() { 276 + fn(x) { 1 + x }(2) * 3 277 + } 278 + " 279 + ); 280 + }
+2 -12
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__blocks__blocks_returning_use.snap
··· 19 19 20 20 ----- COMPILED JAVASCRIPT 21 21 export function b() { 22 - let _block; 23 - { 24 - let cb = (a) => { return a; }; 25 - _block = cb(1); 26 - } 27 - _block; 28 - let _block$1; 29 - { 30 - let cb = (b) => { return b; }; 31 - _block$1 = cb(2); 32 - } 33 - _block$1; 22 + 1; 23 + 2; 34 24 return 3; 35 25 }
+1 -4
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__functions__calling_fn_literal.snap
··· 10 10 11 11 ----- COMPILED JAVASCRIPT 12 12 export function main() { 13 - { 14 - let x = 1; 15 - return x; 16 - } 13 + return 1; 17 14 }
+1 -4
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__functions__function_literals_get_properly_wrapped_1.snap
··· 10 10 11 11 ----- COMPILED JAVASCRIPT 12 12 export function main() { 13 - { 14 - let n = 10; 15 - return n + 1; 16 - } 13 + return 10 + 1; 17 14 }
+1 -4
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__functions__function_literals_get_properly_wrapped_2.snap
··· 10 10 11 11 ----- COMPILED JAVASCRIPT 12 12 export function main() { 13 - { 14 - let n = 10; 15 - return n + 1; 16 - } 13 + return 10 + 1; 17 14 }
+15
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__inlining__blocks_get_preserved_when_needed2.snap
··· 1 + --- 2 + source: compiler-core/src/javascript/tests/inlining.rs 3 + expression: "\npub fn main() {\n fn(x) { 1 + x }(2) * 3\n}\n" 4 + --- 5 + ----- SOURCE CODE 6 + 7 + pub fn main() { 8 + fn(x) { 1 + x }(2) * 3 9 + } 10 + 11 + 12 + ----- COMPILED JAVASCRIPT 13 + export function main() { 14 + return (1 + 2) * 3; 15 + }
+1 -5
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__inlining__inline_anonymous_function_call.snap
··· 11 11 12 12 ----- COMPILED JAVASCRIPT 13 13 export function main() { 14 - { 15 - let a = 42; 16 - let b = false; 17 - return [a, b]; 18 - } 14 + return [42, false]; 19 15 }
+1 -6
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__inlining__inline_anonymous_function_in_pipe.snap
··· 12 12 ----- COMPILED JAVASCRIPT 13 13 export function main() { 14 14 let _pipe = 1; 15 - let _block; 16 - { 17 - let x = _pipe; 18 - _block = x + 1; 19 - } 20 - let _pipe$1 = _block; 15 + let _pipe$1 = _pipe + 1; 21 16 { 22 17 let y = _pipe$1; 23 18 return y * y;
+1 -4
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__inlining__inline_function_capture_in_pipe.snap
··· 18 18 19 19 export function main() { 20 20 let _pipe = 1; 21 - { 22 - let _capture = _pipe; 23 - return add(4, _capture); 24 - } 21 + return add(4, _pipe); 25 22 }
+1 -2
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__inlining__inline_higher_order_function_anonymous.snap
··· 21 21 let $ = new Ok(10); 22 22 if ($ instanceof Ok) { 23 23 let value = $[0]; 24 - let value$1 = value; 25 - return new Ok((value$1 + 2) * 4); 24 + return new Ok((value + 2) * 4); 26 25 } else { 27 26 let error = $[0]; 28 27 return new Error(error);
+1 -2
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__inlining__inline_higher_order_function_with_capture.snap
··· 35 35 let $ = new Ok(10); 36 36 if ($ instanceof Ok) { 37 37 let value = $[0]; 38 - let _capture = value; 39 - return divide(_capture, 2); 38 + return divide(value, 2); 40 39 } else { 41 40 let error = $[0]; 42 41 return new Error(error);
+1 -4
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__inlining__inlining_works_through_blocks.snap
··· 13 13 import { Ok } from "../gleam.mjs"; 14 14 15 15 export function main() { 16 - { 17 - let x = 41; 18 - return new Ok(x + 1); 19 - } 16 + return new Ok(41 + 1); 20 17 }