neo_solidity/ir/expressions/calls/
struct_constructors.rs1fn resolve_struct_type_by_name(ctx: &LoweringContext<'_>, name: &str) -> Option<ValueType> {
2 ctx.defined_struct_types
3 .iter()
4 .chain(ctx.state_types.iter())
5 .chain(ctx.param_types.iter())
6 .chain(ctx.return_types.iter())
7 .chain(ctx.local_types.values())
8 .find_map(|ty| find_named_struct_type(ty, name))
9}
10
11fn try_lower_struct_constructor_call(
12 func: &Expression,
13 args: &[Expression],
14 ctx: &mut LoweringContext,
15 instructions: &mut Vec<Instruction>,
16) -> Option<bool> {
17 let Expression::Variable(identifier) = func else {
18 return None;
19 };
20
21 let struct_type = resolve_struct_type_by_name(ctx, &identifier.name)?;
22 let ValueType::Struct { fields, .. } = &struct_type else {
23 return None;
24 };
25
26 Some(lower_struct_constructor_positional(
27 &identifier.name,
28 &struct_type,
29 fields,
30 args,
31 ctx,
32 instructions,
33 ))
34}
35
36fn try_lower_struct_constructor_named_call(
37 func: &Expression,
38 args: &[solang_parser::pt::NamedArgument],
39 ctx: &mut LoweringContext,
40 instructions: &mut Vec<Instruction>,
41) -> Option<bool> {
42 let Expression::Variable(identifier) = func else {
43 return None;
44 };
45
46 let struct_type = resolve_struct_type_by_name(ctx, &identifier.name)?;
47 let ValueType::Struct { fields, .. } = &struct_type else {
48 return None;
49 };
50
51 Some(lower_struct_constructor_named(
52 &identifier.name,
53 &struct_type,
54 fields,
55 args,
56 ctx,
57 instructions,
58 ))
59}
60
61fn lower_struct_constructor_positional(
62 call_name: &str,
63 struct_type: &ValueType,
64 fields: &[StructField],
65 args: &[Expression],
66 ctx: &mut LoweringContext,
67 instructions: &mut Vec<Instruction>,
68) -> bool {
69 let tmp_id = ctx.next_label();
70 let struct_local = ctx.allocate_local(format!("__struct_ctor_{tmp_id}"), Some(struct_type.clone()));
71
72 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(
73 fields.len() as u64,
74 ))));
75 instructions.push(Instruction::NewArray {
76 element_type: ValueType::Any,
77 });
78 instructions.push(Instruction::StoreLocal(struct_local));
79
80 let mut success = true;
81
82 for (index, field) in fields.iter().enumerate() {
83 instructions.push(Instruction::LoadLocal(struct_local));
84 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(
85 index as u64,
86 ))));
87 if let Some(arg) = args.get(index) {
88 if !lower_expression(arg, ctx, instructions) {
89 push_default_for_value_type(&field.ty, ctx, instructions);
90 success = false;
91 }
92 } else {
93 push_default_for_value_type(&field.ty, ctx, instructions);
94 }
95 instructions.push(Instruction::ArraySet);
96 }
97
98 if args.len() > fields.len() {
99 ctx.record_error(format!(
100 "struct constructor '{}' expects {} argument(s), got {}",
101 call_name,
102 fields.len(),
103 args.len()
104 ));
105 for arg in &args[fields.len()..] {
106 if lower_expression(arg, ctx, instructions) {
107 instructions.push(Instruction::Drop(ValueType::Any));
108 }
109 }
110 success = false;
111 }
112
113 instructions.push(Instruction::LoadLocal(struct_local));
114 success
115}
116
117fn lower_struct_constructor_named(
118 call_name: &str,
119 struct_type: &ValueType,
120 fields: &[StructField],
121 args: &[solang_parser::pt::NamedArgument],
122 ctx: &mut LoweringContext,
123 instructions: &mut Vec<Instruction>,
124) -> bool {
125 let tmp_id = ctx.next_label();
126 let struct_local = ctx.allocate_local(format!("__struct_ctor_{tmp_id}"), Some(struct_type.clone()));
127
128 let mut field_indexes = HashMap::with_capacity(fields.len());
129 for (index, field) in fields.iter().enumerate() {
130 field_indexes.insert(field.name.clone(), index);
131 }
132
133 let mut arg_locals: Vec<Option<usize>> = vec![None; fields.len()];
134 let mut success = true;
135
136 for arg in args {
137 let Some(&field_index) = field_indexes.get(&arg.name.name) else {
138 ctx.record_error(format!(
139 "unknown field '{}' for struct '{}'",
140 arg.name.name, call_name
141 ));
142 if lower_expression(&arg.expr, ctx, instructions) {
143 instructions.push(Instruction::Drop(ValueType::Any));
144 }
145 success = false;
146 continue;
147 };
148
149 if arg_locals[field_index].is_some() {
150 ctx.record_error(format!(
151 "duplicate field '{}' for struct '{}'",
152 arg.name.name, call_name
153 ));
154 if lower_expression(&arg.expr, ctx, instructions) {
155 instructions.push(Instruction::Drop(ValueType::Any));
156 }
157 success = false;
158 continue;
159 }
160
161 let field_ty = &fields[field_index].ty;
162 let arg_local = ctx.allocate_local(
163 format!("__struct_arg_{tmp_id}_{field_index}"),
164 Some(field_ty.clone()),
165 );
166
167 if !lower_expression(&arg.expr, ctx, instructions) {
168 push_default_for_value_type(field_ty, ctx, instructions);
169 success = false;
170 }
171
172 instructions.push(Instruction::StoreLocal(arg_local));
173 arg_locals[field_index] = Some(arg_local);
174 }
175
176 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(
177 fields.len() as u64,
178 ))));
179 instructions.push(Instruction::NewArray {
180 element_type: ValueType::Any,
181 });
182 instructions.push(Instruction::StoreLocal(struct_local));
183
184 for (index, field) in fields.iter().enumerate() {
185 instructions.push(Instruction::LoadLocal(struct_local));
186 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(
187 index as u64,
188 ))));
189 if let Some(arg_local) = arg_locals[index] {
190 instructions.push(Instruction::LoadLocal(arg_local));
191 } else {
192 push_default_for_value_type(&field.ty, ctx, instructions);
193 }
194 instructions.push(Instruction::ArraySet);
195 }
196
197 instructions.push(Instruction::LoadLocal(struct_local));
198 success
199}