neo_solidity/ir/expressions/calls/
dispatch.rs1fn lower_function_call_expression(
2 func: &Expression,
3 args: &[Expression],
4 ctx: &mut LoweringContext,
5 instructions: &mut Vec<Instruction>,
6) -> bool {
7 let mut func = func;
8
9 fn is_compile_time_zero(expr: &Expression) -> bool {
10 match expr {
11 Expression::Parenthesis(_, inner) => is_compile_time_zero(inner),
12 Expression::FunctionCall(_, func, args) if args.len() == 1 => {
13 if matches!(
14 func.as_ref(),
15 Expression::Type(_, PtType::Uint(_) | PtType::Int(_))
16 ) {
17 return is_compile_time_zero(&args[0]);
18 }
19 false
20 }
21 _ => matches!(
22 literal_from_expression(expr),
23 Some(LiteralValue::Integer(value)) if value.is_zero()
24 ),
25 }
26 }
27
28 if let Expression::FunctionCallBlock(_, inner_call, block) = func {
33 if let Statement::Args(_, named_args) = block.as_ref() {
34 let mut unsupported = Vec::new();
35 for arg in named_args {
36 match arg.name.name.as_str() {
37 "gas" => {
39 if lower_expression(&arg.expr, ctx, instructions) {
40 instructions.push(Instruction::Drop(ValueType::Any));
41 }
42 }
43 "value" => {
45 if is_compile_time_zero(&arg.expr) {
46 if lower_expression(&arg.expr, ctx, instructions) {
47 instructions.push(Instruction::Drop(ValueType::Any));
48 }
49 } else {
50 unsupported.push("value");
51 }
52 }
53 "salt" => unsupported.push("salt"),
54 other => unsupported.push(other),
55 }
56 }
57
58 if !unsupported.is_empty() {
59 ctx.record_error(format!(
60 "function call options (`{{...}}`) are not supported ({}); Neo N3 requires explicit NEP-17 transfers (`NativeCalls.gasTransfer` / `NativeCalls.neoTransfer`) + `onNEP17Payment`",
61 unsupported.join(", ")
62 ));
63 return false;
64 }
65
66 func = inner_call.as_ref();
67 } else {
68 ctx.record_error(
69 "function call blocks are not supported; only call options (`{...}`) are recognized",
70 );
71 return false;
72 }
73 }
74
75 if let Some(result) = try_lower_storage_array_helpers(func, args, ctx, instructions) {
76 return result;
77 }
78
79 if let Some(result) = try_lower_value_transfer_helpers(func, args, ctx, instructions) {
80 return result;
81 }
82
83 if let Some(result) = try_lower_low_level_address_call(func, args, ctx, instructions) {
84 return result;
85 }
86
87 if let Some(result) = try_lower_type_constructor_call(func, args, ctx, instructions) {
88 return result;
89 }
90
91 if let Some(result) = try_lower_struct_constructor_call(func, args, ctx, instructions) {
92 return result;
93 }
94
95 if let Some(result) = try_lower_builtin_call(func, args, ctx, instructions) {
96 return result;
97 }
98
99 if let Some(result) = try_lower_member_call(func, args, ctx, instructions) {
100 return result;
101 }
102
103 if let Some(result) = try_lower_variable_call(func, args, ctx, instructions) {
104 return result;
105 }
106
107 for arg in args {
108 load_expression(arg, ctx, instructions);
109 instructions.push(Instruction::Drop(ValueType::Any));
110 }
111 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
112 BigInt::zero(),
113 )));
114 true
115}