⭐️ A friendly language for building type-safe, scalable systems!

Reuse variables for record updates

+15 -5
compiler-core/src/ast/typed.rs
··· 164 164 RecordUpdate { 165 165 location: SrcSpan, 166 166 type_: Arc<Type>, 167 - record: Box<TypedAssignment>, 167 + /// If the record is an expression that is not a variable we will need to assign to a 168 + /// variable so it can be referred multiple times. 169 + record_assignment: Option<Box<TypedAssignment>>, 168 170 constructor: Box<Self>, 169 171 args: Vec<CallArg<Self>>, 170 172 }, ··· 371 373 .or_else(|| self.self_if_contains_location(byte_index)), 372 374 373 375 Self::RecordUpdate { 374 - record, 376 + record_assignment: record, 375 377 constructor, 376 378 args, 377 379 .. ··· 379 381 .iter() 380 382 .filter(|arg| arg.implicit.is_none()) 381 383 .find_map(|arg| arg.find_node(byte_index, constructor, args)) 382 - .or_else(|| record.find_node(byte_index)) 384 + .or_else(|| record.as_ref().and_then(|r| r.find_node(byte_index))) 383 385 .or_else(|| self.self_if_contains_location(byte_index)), 384 386 } 385 387 } ··· 509 511 .iter() 510 512 .find_map(|arg| arg.value.find_statement(byte_index)), 511 513 512 - Self::RecordUpdate { record, args, .. } => args 514 + Self::RecordUpdate { 515 + record_assignment: record, 516 + args, 517 + .. 518 + } => args 513 519 .iter() 514 520 .filter(|arg| arg.implicit.is_none()) 515 521 .find_map(|arg| arg.find_statement(byte_index)) 516 - .or_else(|| record.value.find_statement(byte_index)), 522 + .or_else(|| { 523 + record 524 + .as_ref() 525 + .and_then(|r| r.value.find_statement(byte_index)) 526 + }), 517 527 } 518 528 } 519 529
+6 -4
compiler-core/src/ast/visit.rs
··· 299 299 &mut self, 300 300 location: &'ast SrcSpan, 301 301 type_: &'ast Arc<Type>, 302 - record: &'ast TypedAssignment, 302 + record: &'ast Option<Box<TypedAssignment>>, 303 303 constructor: &'ast TypedExpr, 304 304 args: &'ast [TypedCallArg], 305 305 ) { ··· 843 843 TypedExpr::RecordUpdate { 844 844 location, 845 845 type_, 846 - record, 846 + record_assignment: record, 847 847 constructor, 848 848 args, 849 849 } => v.visit_typed_expr_record_update(location, type_, record, constructor, args), ··· 1133 1133 v: &mut V, 1134 1134 _location: &'a SrcSpan, 1135 1135 _type: &'a Arc<Type>, 1136 - record: &'a TypedAssignment, 1136 + record: &'a Option<Box<TypedAssignment>>, 1137 1137 constructor: &'a TypedExpr, 1138 1138 args: &'a [TypedCallArg], 1139 1139 ) where 1140 1140 V: Visit<'a> + ?Sized, 1141 1141 { 1142 1142 v.visit_typed_expr(constructor); 1143 - v.visit_typed_assignment(record); 1143 + if let Some(record) = record { 1144 + v.visit_typed_assignment(record); 1145 + } 1144 1146 for arg in args { 1145 1147 v.visit_typed_call_arg(arg); 1146 1148 }
+11 -8
compiler-core/src/erlang.rs
··· 1948 1948 } 1949 1949 1950 1950 fn record_update<'a>( 1951 - record: &'a TypedAssignment, 1951 + record: &'a Option<Box<TypedAssignment>>, 1952 1952 constructor: &'a TypedExpr, 1953 1953 args: &'a [TypedCallArg], 1954 1954 env: &mut Env<'a>, 1955 1955 ) -> Document<'a> { 1956 1956 let vars = env.current_scope_vars.clone(); 1957 1957 1958 - let document = docvec![ 1959 - assignment(record, env, Position::NotTail), 1960 - ",", 1961 - line(), 1962 - call(constructor, args, env) 1963 - ]; 1958 + let document = match record.as_ref() { 1959 + Some(record) => docvec![ 1960 + assignment(record, env, Position::NotTail), 1961 + ",", 1962 + line(), 1963 + call(constructor, args, env) 1964 + ], 1965 + None => call(constructor, args, env), 1966 + }; 1964 1967 1965 1968 env.current_scope_vars = vars; 1966 1969 ··· 2155 2158 TypedExpr::RecordAccess { record, index, .. } => tuple_index(record, index + 1, env), 2156 2159 2157 2160 TypedExpr::RecordUpdate { 2158 - record, 2161 + record_assignment: record, 2159 2162 constructor, 2160 2163 args, 2161 2164 ..
+1 -2
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__echo__record_update_printed_by_echo_is_wrapped_in_begin_end_block.snap
··· 29 29 Wobble = {wobble, 1, <<"wobble"/utf8>>}, 30 30 echo( 31 31 begin 32 - _record = Wobble, 33 - {wobble, 1, erlang:element(3, _record)} 32 + {wobble, 1, erlang:element(3, Wobble)} 34 33 end, 35 34 6 36 35 ).
+1 -2
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__pipes__pipe_in_record_update.snap
··· 34 34 -file("project/test/my/mod.gleam", 9). 35 35 -spec main(x()) -> x(). 36 36 main(X) -> 37 - _record = X, 38 37 {x, 39 38 begin 40 39 _pipe = 1, 41 40 id(_pipe) 42 41 end, 43 - erlang:element(3, _record)}. 42 + erlang:element(3, X)}.
+4 -5
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__records__nested_record_update.snap
··· 33 33 -spec main() -> wibble(). 34 34 main() -> 35 35 Base = {wibble, 1, {wobble, 2, 3}, 4}, 36 - _record = Base, 37 36 {wibble, 38 - erlang:element(2, _record), 37 + erlang:element(2, Base), 39 38 begin 40 - _record@1 = erlang:element(3, Base), 41 - {wobble, erlang:element(2, _record@1), 5} 39 + _record = erlang:element(3, Base), 40 + {wobble, erlang:element(2, _record), 5} 42 41 end, 43 - erlang:element(4, _record)}. 42 + erlang:element(4, Base)}.
+2 -3
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__records__nested_record_update_with_blocks.snap
··· 33 33 -file("project/test/my/mod.gleam", 5). 34 34 -spec main(a()) -> a(). 35 35 main(A) -> 36 - _record = A, 37 36 {a, 38 37 begin 39 - _record@1 = erlang:element(2, A), 38 + _record = erlang:element(2, A), 40 39 {b, 41 40 begin 42 - _record@2 = erlang:element(2, erlang:element(2, A)), 41 + _record@1 = erlang:element(2, erlang:element(2, A)), 43 42 {c, 0} 44 43 end} 45 44 end}.
+1 -2
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__records__record_updates.snap
··· 29 29 main() -> 30 30 P = {person, <<"Quinn"/utf8>>, 27}, 31 31 New_p = begin 32 - _record = P, 33 - {person, erlang:element(2, _record), 28} 32 + {person, erlang:element(2, P), 28} 34 33 end, 35 34 New_p.
+1 -2
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__records__record_updates1.snap
··· 29 29 main() -> 30 30 P = {person, <<"Quinn"/utf8>>, 27}, 31 31 New_p = begin 32 - _record = P, 33 - {person, erlang:element(2, _record), erlang:element(3, P) + 1} 32 + {person, erlang:element(2, P), erlang:element(3, P) + 1} 34 33 end, 35 34 New_p.
-1
compiler-core/src/erlang/tests/snapshots/gleam_core__erlang__tests__records__record_updates2.snap
··· 29 29 main() -> 30 30 P = {person, <<"Quinn"/utf8>>, 27}, 31 31 New_p = begin 32 - _record = P, 33 32 {person, <<"Riley"/utf8>>, 28} 34 33 end, 35 34 New_p.
+10 -7
compiler-core/src/javascript/expression.rs
··· 312 312 313 313 TypedExpr::RecordAccess { record, label, .. } => self.record_access(record, label), 314 314 TypedExpr::RecordUpdate { 315 - record, 315 + record_assignment: record, 316 316 constructor, 317 317 args, 318 318 .. ··· 1443 1443 1444 1444 fn record_update( 1445 1445 &mut self, 1446 - record: &'a TypedAssignment, 1446 + record: &'a Option<Box<TypedAssignment>>, 1447 1447 constructor: &'a TypedExpr, 1448 1448 args: &'a [TypedCallArg], 1449 1449 ) -> Output<'a> { 1450 - Ok(docvec![ 1451 - self.not_in_tail_position(None, |this| this.assignment(record))?, 1452 - line(), 1453 - self.call(constructor, args)?, 1454 - ]) 1450 + match record.as_ref() { 1451 + Some(record) => Ok(docvec![ 1452 + self.not_in_tail_position(None, |this| this.assignment(record))?, 1453 + line(), 1454 + self.call(constructor, args)?, 1455 + ]), 1456 + None => self.call(constructor, args), 1457 + } 1455 1458 } 1456 1459 1457 1460 fn tuple_index(&mut self, tuple: &'a TypedExpr, index: u64) -> Output<'a> {
+2 -4
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__case__record_update_in_pipeline_in_case_clause.snap
··· 44 44 let $ = x.wibble; 45 45 if ($ === 1) { 46 46 let _block; 47 - let _record = x; 48 - _block = new Wibble(4, _record.wobble); 47 + _block = new Wibble(4, x.wobble); 49 48 let _pipe = _block; 50 49 return identity(_pipe); 51 50 } else { 52 51 let $1 = x.wobble; 53 52 if ($1 === 3) { 54 53 let _block; 55 - let _record = x; 56 - _block = new Wibble(_record.wibble, 10); 54 + _block = new Wibble(x.wibble, 10); 57 55 let _pipe = _block; 58 56 return identity(_pipe); 59 57 } else {
+6 -11
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__custom_type_with_named_fields.snap
··· 73 73 } 74 74 75 75 export function update(cat) { 76 - let _record = cat; 77 - new Cat("Sid", _record.cuteness) 78 - let _record$1 = cat; 79 - new Cat( 80 - "Bartholemew Wonder Puss the Fourth !!!!!!!!!!!!!!!!", 81 - _record$1.cuteness, 82 - ) 83 - let _record$2 = new_cat(); 84 - new Cat("Molly", _record$2.cuteness) 76 + new Cat("Sid", cat.cuteness) 77 + new Cat("Bartholemew Wonder Puss the Fourth !!!!!!!!!!!!!!!!", cat.cuteness) 78 + let _record = new_cat(); 79 + new Cat("Molly", _record.cuteness) 85 80 let box = new Box(cat); 86 - let _record$3 = box.occupant; 87 - return new Cat(_record$3.name, box.occupant.cuteness + 1); 81 + let _record$1 = box.occupant; 82 + return new Cat(_record$1.name, box.occupant.cuteness + 1); 88 83 } 89 84 90 85 export const felix = /* @__PURE__ */ new Cat("Felix", 12);
+2 -1
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__record_with_field_named_constructor.snap
··· 1 1 --- 2 2 source: compiler-core/src/javascript/tests/custom_types.rs 3 + assertion_line: 605 3 4 expression: "\npub type Thing {\n Thing(constructor: Nil)\n}\n\npub fn main() {\n let a = Thing(constructor: Nil)\n let b = Thing(..a, constructor: Nil)\n b.constructor\n}\n" 5 + snapshot_kind: text 4 6 --- 5 7 ----- SOURCE CODE 6 8 ··· 28 30 export function main() { 29 31 let a = new Thing(undefined); 30 32 let _block; 31 - let _record = a; 32 33 _block = new Thing(undefined); 33 34 let b = _block; 34 35 return b.constructor$;
+2 -1
compiler-core/src/javascript/tests/snapshots/gleam_core__javascript__tests__custom_types__record_with_field_named_then.snap
··· 1 1 --- 2 2 source: compiler-core/src/javascript/tests/custom_types.rs 3 + assertion_line: 622 3 4 expression: "\npub type Thing {\n Thing(then: Nil)\n}\n\npub fn main() {\n let a = Thing(then: Nil)\n let b = Thing(..a, then: Nil)\n b.then\n}\n" 5 + snapshot_kind: text 4 6 --- 5 7 ----- SOURCE CODE 6 8 ··· 28 30 export function main() { 29 31 let a = new Thing(undefined); 30 32 let _block; 31 - let _record = a; 32 33 _block = new Thing(undefined); 33 34 let b = _block; 34 35 return b.then$;
+30 -25
compiler-core/src/type_/expression.rs
··· 3037 3037 let record_location = record.location(); 3038 3038 let record_type = record.type_(); 3039 3039 3040 - // We create an Assignment for the old record expression and will use a Var expression 3041 - // to refer back to it while constructing the arguments. 3042 - let record_assignment = Assignment { 3043 - location: record_location, 3044 - pattern: Pattern::Variable { 3040 + let (record_var, record_assignment) = if record.is_var() { 3041 + (record, None) 3042 + } else { 3043 + // We create an Assignment for the old record expression and will use a Var expression 3044 + // to refer back to it while constructing the arguments. 3045 + let record_assignment = Assignment { 3045 3046 location: record_location, 3046 - name: RECORD_UPDATE_VARIABLE.into(), 3047 - type_: record_type.clone(), 3048 - origin: VariableOrigin::generated(), 3049 - }, 3050 - annotation: None, 3051 - compiled_case: CompiledCase::failure(), 3052 - kind: AssignmentKind::Generated, 3053 - value: record, 3054 - }; 3055 - 3056 - let record_var = TypedExpr::Var { 3057 - location: record_location, 3058 - constructor: ValueConstructor { 3059 - publicity: Publicity::Private, 3060 - deprecation: Deprecation::NotDeprecated, 3061 - type_: record_type, 3062 - variant: ValueConstructorVariant::LocalVariable { 3047 + pattern: Pattern::Variable { 3063 3048 location: record_location, 3049 + name: RECORD_UPDATE_VARIABLE.into(), 3050 + type_: record_type.clone(), 3064 3051 origin: VariableOrigin::generated(), 3065 3052 }, 3066 - }, 3067 - name: RECORD_UPDATE_VARIABLE.into(), 3053 + annotation: None, 3054 + compiled_case: CompiledCase::failure(), 3055 + kind: AssignmentKind::Generated, 3056 + value: record, 3057 + }; 3058 + 3059 + let record_var = TypedExpr::Var { 3060 + location: record_location, 3061 + constructor: ValueConstructor { 3062 + publicity: Publicity::Private, 3063 + deprecation: Deprecation::NotDeprecated, 3064 + type_: record_type, 3065 + variant: ValueConstructorVariant::LocalVariable { 3066 + location: record_location, 3067 + origin: VariableOrigin::generated(), 3068 + }, 3069 + }, 3070 + name: RECORD_UPDATE_VARIABLE.into(), 3071 + }; 3072 + (record_var, Some(Box::new(record_assignment))) 3068 3073 }; 3069 3074 3070 3075 // infer the fields of the variant we want to update ··· 3076 3081 Ok(TypedExpr::RecordUpdate { 3077 3082 location, 3078 3083 type_: variant.retn, 3079 - record: Box::new(record_assignment), 3084 + record_assignment, 3080 3085 constructor: Box::new(typed_constructor), 3081 3086 args, 3082 3087 })