neo_solidity/ir/expressions/dispatch/
calls.rs1fn try_lower_expression_calls(
2 expr: &Expression,
3 ctx: &mut LoweringContext,
4 instructions: &mut Vec<Instruction>,
5) -> Option<bool> {
6 match expr {
7 Expression::FunctionCallBlock(_, call, block) => {
8 let _ = (call, block);
9 ctx.record_error_with_suggestion(
10 "function call options (`{...}`) are not supported; Neo N3 requires explicit NEP-17 transfers (`NativeCalls.gasTransfer` / `NativeCalls.neoTransfer`) + `onNEP17Payment`",
11 "replace {value: x} with an explicit NativeCalls.gasTransfer() or NativeCalls.neoTransfer() before the call",
12 );
13 Some(false)
14 }
15 Expression::NamedFunctionCall(_, func, args) => {
16 if let Some(result) =
18 try_lower_struct_constructor_named_call(func.as_ref(), args, ctx, instructions)
19 {
20 return Some(result);
21 }
22
23 if let Some(result) =
25 try_lower_named_function_call(func.as_ref(), args, ctx, instructions)
26 {
27 return Some(result);
28 }
29
30 ctx.record_error_with_suggestion(
31 "named argument calls are not supported for this callee",
32 "use positional arguments instead: f(arg1, arg2) rather than f({x: arg1, y: arg2})",
33 );
34 Some(false)
35 }
36 Expression::FunctionCall(_, func, args) => {
37 Some(lower_function_call_expression(func.as_ref(), args, ctx, instructions))
38 }
39 Expression::New(_, expr) => {
40 Some(lower_new_expression(expr.as_ref(), ctx, instructions))
41 }
42 Expression::Type(_, ty) => {
43 push_default_for_type(ty, instructions);
44 Some(true)
45 }
46 Expression::Parenthesis(_, inner) => Some(lower_expression(inner, ctx, instructions)),
47 Expression::MemberAccess(_, inner, member) => Some(lower_member_access_expression(
48 expr,
49 inner.as_ref(),
50 member,
51 ctx,
52 instructions,
53 )),
54 _ => None,
55 }
56}
57
58fn lower_new_expression(
59 expr: &Expression,
60 ctx: &mut LoweringContext,
61 instructions: &mut Vec<Instruction>,
62) -> bool {
63 match expr {
64 Expression::FunctionCall(_, func, args) => {
65 if matches!(
67 func.as_ref(),
68 Expression::Type(_, PtType::DynamicBytes | PtType::String)
69 ) {
70 if args.len() != 1 {
71 ctx.record_error_with_suggestion(
72 "new bytes/string expects exactly one length argument",
73 "usage: new bytes(length) or new string(length)",
74 );
75 for arg in args {
76 if lower_expression(arg, ctx, instructions) {
77 instructions.push(Instruction::Drop(ValueType::Any));
78 }
79 }
80 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
81 BigInt::zero(),
82 )));
83 } else if !lower_expression(&args[0], ctx, instructions) {
84 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
85 BigInt::zero(),
86 )));
87 }
88
89 instructions.push(Instruction::NewBuffer);
90 return true;
91 }
92 if let Expression::ArraySubscript(_, array_type_expr, index) = func.as_ref() {
94 if let Some(fixed_len_expr) = index.as_ref() {
95 if !args.is_empty() {
96 ctx.record_error("new fixed-size array constructor does not accept runtime arguments");
97 for arg in args {
98 if lower_expression(arg, ctx, instructions) {
99 instructions.push(Instruction::Drop(ValueType::Any));
100 }
101 }
102 }
103
104 return lower_new_array_allocation(
105 array_type_expr.as_ref(),
106 fixed_len_expr.as_ref(),
107 ctx,
108 instructions,
109 );
110 }
111
112 if args.len() != 1 {
113 ctx.record_error("new array expects exactly one length argument");
114 for arg in args {
115 if lower_expression(arg, ctx, instructions) {
116 instructions.push(Instruction::Drop(ValueType::Any));
117 }
118 }
119
120 let zero = Expression::NumberLiteral(
121 Default::default(),
122 "0".to_string(),
123 "".to_string(),
124 None,
125 );
126 return lower_new_array_allocation(
127 array_type_expr.as_ref(),
128 &zero,
129 ctx,
130 instructions,
131 );
132 }
133
134 return lower_new_array_allocation(
135 array_type_expr.as_ref(),
136 &args[0],
137 ctx,
138 instructions,
139 );
140 }
141
142 if let Expression::Variable(identifier) = func.as_ref() {
144 if ctx.is_contract_type_name(&identifier.name) {
145 ctx.record_error(format!(
146 "contract creation via `new {}` is not supported on Neo N3; use ContractManagement.deploy from an admin/entry contract instead",
147 identifier.name
148 ));
149 for arg in args {
150 if lower_expression(arg, ctx, instructions) {
151 instructions.push(Instruction::Drop(ValueType::Any));
152 }
153 }
154 instructions.push(Instruction::PushLiteral(LiteralValue::Address(vec![
155 0u8;
156 20
157 ])));
158 return true;
159 }
160 }
161
162 ctx.record_error_with_suggestion(
163 "unsupported `new` expression",
164 "Neo N3 supports `new bytes(n)`, `new string(n)`, `new T[](n)`, and `new T[N]`; use ContractManagement for contract deployment",
165 );
166 for arg in args {
167 if lower_expression(arg, ctx, instructions) {
168 instructions.push(Instruction::Drop(ValueType::Any));
169 }
170 }
171 instructions.push(Instruction::PushLiteral(LiteralValue::Null));
172 true
173 }
174 Expression::ArraySubscript(_, array_type_expr, Some(length_expr)) => {
175 lower_new_array_allocation(array_type_expr.as_ref(), length_expr.as_ref(), ctx, instructions)
176 }
177 Expression::FunctionCallBlock(_, _, _) => {
178 ctx.record_error_with_suggestion(
179 "function call blocks on `new` are not supported",
180 "Neo N3 does not support value transfers via call options; use explicit NEP-17 transfers",
181 );
182 instructions.push(Instruction::PushLiteral(LiteralValue::Null));
183 true
184 }
185 _ => {
186 ctx.record_error_with_suggestion(
187 "unsupported `new` expression",
188 "Neo N3 supports `new bytes(n)`, `new string(n)`, `new T[](n)`, and `new T[N]`",
189 );
190 instructions.push(Instruction::PushLiteral(LiteralValue::Null));
191 true
192 }
193 }
194}
195
196fn lower_new_array_allocation(
197 array_type_expr: &Expression,
198 length_expr: &Expression,
199 ctx: &mut LoweringContext,
200 instructions: &mut Vec<Instruction>,
201) -> bool {
202 let Some(element_type) = infer_type_from_expression(array_type_expr, ctx) else {
203 ctx.record_error(format!(
204 "unable to infer element type for new array allocation (`new {}`)",
205 array_type_expr
206 ));
207 if lower_expression(length_expr, ctx, instructions) {
208 instructions.push(Instruction::Drop(ValueType::Any));
209 }
210 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
211 BigInt::zero(),
212 )));
213 instructions.push(Instruction::NewArray {
214 element_type: ValueType::Any,
215 });
216 return true;
217 };
218
219 let tmp_id = ctx.next_label();
220 let len_local = ctx.allocate_local(format!("__new_array_len_{tmp_id}"), None);
221 if !lower_expression(length_expr, ctx, instructions) {
222 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
223 BigInt::zero(),
224 )));
225 }
226 instructions.push(Instruction::StoreLocal(len_local));
227
228 let array_type = ValueType::Array(Box::new(element_type.clone()));
229 let array_local = ctx.allocate_local(format!("__new_array_{tmp_id}"), Some(array_type));
230
231 instructions.push(Instruction::LoadLocal(len_local));
232 instructions.push(Instruction::NewArray {
233 element_type: element_type.clone(),
234 });
235 instructions.push(Instruction::StoreLocal(array_local));
236
237 let idx_local = ctx.allocate_local(format!("__new_array_idx_{tmp_id}"), None);
240 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
241 BigInt::zero(),
242 )));
243 instructions.push(Instruction::StoreLocal(idx_local));
244
245 let loop_label = ctx.next_label();
246 let end_label = ctx.next_label();
247
248 instructions.push(Instruction::Label(loop_label));
249 instructions.push(Instruction::LoadLocal(idx_local));
250 instructions.push(Instruction::LoadLocal(len_local));
251 instructions.push(Instruction::BinaryOp(BinaryOperator::Lt));
252 instructions.push(Instruction::JumpIf { target: end_label });
253
254 instructions.push(Instruction::LoadLocal(array_local));
255 instructions.push(Instruction::LoadLocal(idx_local));
256 push_default_for_value_type(&element_type, ctx, instructions);
257 instructions.push(Instruction::ArraySet);
258
259 instructions.push(Instruction::LoadLocal(idx_local));
260 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::one())));
261 instructions.push(Instruction::BinaryOp(BinaryOperator::Add));
262 instructions.push(Instruction::StoreLocal(idx_local));
263
264 instructions.push(Instruction::Jump { target: loop_label });
265 instructions.push(Instruction::Label(end_label));
266
267 instructions.push(Instruction::LoadLocal(array_local));
268 true
269}
270
271fn try_lower_named_function_call(
277 func: &Expression,
278 named_args: &[solang_parser::pt::NamedArgument],
279 ctx: &mut LoweringContext,
280 instructions: &mut Vec<Instruction>,
281) -> Option<bool> {
282 let Expression::Variable(identifier) = func else {
283 return None;
284 };
285
286 let param_names: Vec<String> =
290 ctx.get_function_param_names(&identifier.name, named_args.len())?.to_vec();
291
292 let name_to_index: HashMap<&str, usize> = param_names
294 .iter()
295 .enumerate()
296 .map(|(i, name)| (name.as_str(), i))
297 .collect();
298
299 let mut positional: Vec<Option<&Expression>> = vec![None; named_args.len()];
301 let mut has_error = false;
302
303 for arg in named_args {
304 if let Some(&index) = name_to_index.get(arg.name.name.as_str()) {
305 if positional[index].is_some() {
306 ctx.record_error(format!(
307 "duplicate named argument '{}' in call to '{}'",
308 arg.name.name, identifier.name
309 ));
310 has_error = true;
311 } else {
312 positional[index] = Some(&arg.expr);
313 }
314 } else {
315 ctx.record_error(format!(
316 "unknown parameter '{}' in call to '{}'; expected one of: {}",
317 arg.name.name,
318 identifier.name,
319 param_names.join(", ")
320 ));
321 has_error = true;
322 }
323 }
324
325 if has_error {
326 for arg in named_args {
328 if lower_expression(&arg.expr, ctx, instructions) {
329 instructions.push(Instruction::Drop(ValueType::Any));
330 }
331 }
332 return Some(false);
333 }
334
335 let ordered_exprs: Vec<&Expression> = positional
337 .into_iter()
338 .map(|opt| opt.expect("all positions filled"))
339 .collect();
340
341 let mut success = true;
343 for expr in &ordered_exprs {
344 if !lower_expression(expr, ctx, instructions) {
345 success = false;
346 }
347 }
348
349 if success {
350 if let Some(neo_name) = ctx.neo_function_name(&identifier.name, named_args.len()) {
351 instructions.push(Instruction::CallFunction {
352 name: neo_name,
353 arg_count: named_args.len(),
354 });
355 } else {
356 ctx.record_error(format!(
357 "no overload of '{}' with {} argument(s)",
358 identifier.name, named_args.len()
359 ));
360 success = false;
361 }
362 }
363
364 if ctx.is_void_function(&identifier.name) {
365 return Some(false);
366 }
367 Some(success)
368}