Skip to content

Commit 4ab5e3a

Browse files
committed
feat(slang): cast function call arguments to match parameter types
Register declared parameter types in function signatures and resolve overloads by exact type match instead of arity. Cast arguments using TypeConversion to correctly handle bool (cmp != 0) and address (truncate to ui160 + address_cast). Extract resolve_function_types to deduplicate Slang-to-MLIR type resolution.
1 parent a26fbcd commit 4ab5e3a

6 files changed

Lines changed: 66 additions & 72 deletions

File tree

solx-mlir/src/context/function.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use melior::ir::Type;
99
pub struct Function<'context> {
1010
/// The mangled MLIR function name.
1111
pub mlir_name: String,
12-
/// Number of parameters.
13-
pub parameter_count: usize,
12+
/// Parameter types (MLIR-interned, exact types from the function signature).
13+
pub parameter_types: Vec<Type<'context>>,
1414
/// Return types (MLIR-interned, exact types from the function signature).
1515
pub return_types: Vec<Type<'context>>,
1616
}
@@ -19,12 +19,12 @@ impl<'context> Function<'context> {
1919
/// Creates a new function metadata entry.
2020
pub fn new(
2121
mlir_name: String,
22-
parameter_count: usize,
22+
parameter_types: Vec<Type<'context>>,
2323
return_types: Vec<Type<'context>>,
2424
) -> Self {
2525
Self {
2626
mlir_name,
27-
parameter_count,
27+
parameter_types,
2828
return_types,
2929
}
3030
}

solx-mlir/src/context/mod.rs

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -171,52 +171,47 @@ impl<'context> Context<'context> {
171171
/// Registers a function signature for call resolution.
172172
pub fn register_function_signature(
173173
&mut self,
174-
bare_name: &str,
174+
base_name: &str,
175175
mlir_name: String,
176-
parameter_count: usize,
176+
parameter_types: Vec<Type<'context>>,
177177
return_types: Vec<Type<'context>>,
178178
) {
179179
self.function_signatures
180-
.entry(bare_name.to_owned())
180+
.entry(base_name.to_owned())
181181
.or_default()
182-
.push(Function::new(mlir_name, parameter_count, return_types));
182+
.push(Function::new(mlir_name, parameter_types, return_types));
183183
}
184184

185185
/// Resolves a function call by bare name and argument count.
186186
///
187-
/// Returns the mangled MLIR name and the declared return types.
187+
/// Returns the mangled MLIR name, declared parameter types, and return
188+
/// types.
188189
///
189190
/// # Errors
190191
///
191192
/// Returns an error if the function is undefined or the call is ambiguous.
192193
pub fn resolve_function(
193194
&self,
194-
bare_name: &str,
195-
argument_count: usize,
196-
) -> anyhow::Result<(&str, &[Type<'context>])> {
195+
base_name: &str,
196+
argument_types: &[Type<'context>],
197+
) -> anyhow::Result<(&str, &[Type<'context>], &[Type<'context>])> {
197198
let signatures = self
198199
.function_signatures
199-
.get(bare_name)
200-
.ok_or_else(|| anyhow::anyhow!("undefined function: {bare_name}"))?;
201-
// TODO: resolve overloads by parameter types, not just arity
202-
let matches: Vec<_> = signatures
200+
.get(base_name)
201+
.ok_or_else(|| anyhow::anyhow!("undefined function: {base_name}"))?;
202+
let matched = signatures
203203
.iter()
204-
.filter(|signature| signature.parameter_count == argument_count)
205-
.collect();
206-
match matches.len() {
207-
0 => anyhow::bail!("no overload of '{bare_name}' takes {argument_count} arguments"),
208-
1 => Ok((matches[0].mlir_name.as_str(), &matches[0].return_types)),
209-
_ => {
210-
let overloads: Vec<&str> = matches
211-
.iter()
212-
.map(|signature| signature.mlir_name.as_str())
213-
.collect();
214-
anyhow::bail!(
215-
"ambiguous call to '{bare_name}' with {argument_count} arguments: {}",
216-
overloads.join(", ")
204+
.find(|signature| signature.parameter_types == argument_types)
205+
.ok_or_else(|| {
206+
anyhow::anyhow!(
207+
"no matching overload of '{base_name}' for the given argument types"
217208
)
218-
}
219-
}
209+
})?;
210+
Ok((
211+
matched.mlir_name.as_str(),
212+
&matched.parameter_types,
213+
&matched.return_types,
214+
))
220215
}
221216

222217
// ==== Phase 3: Sol pass pipeline ====

solx-slang/src/ast/contract/function/expression/call/mod.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod type_conversion;
77
use melior::ir::BlockLike;
88
use melior::ir::BlockRef;
99
use melior::ir::Value;
10+
use melior::ir::ValueLike;
1011
use slang_solidity::backend::built_ins::BuiltIn;
1112
use slang_solidity::backend::ir::ast::ArgumentsDeclaration;
1213
use slang_solidity::backend::ir::ast::Expression;
@@ -138,10 +139,19 @@ impl<'emitter, 'state, 'context, 'block> CallEmitter<'emitter, 'state, 'context,
138139
current_block = next_block;
139140
}
140141

141-
let (mlir_name, return_types) = self
142+
let argument_types: Vec<melior::ir::Type<'context>> =
143+
argument_values.iter().map(|value| value.r#type()).collect();
144+
let (mlir_name, parameter_types, return_types) = self
142145
.expression_emitter
143146
.state
144-
.resolve_function(&callee_name, argument_values.len())?;
147+
.resolve_function(&callee_name, &argument_types)?;
148+
149+
// Cast arguments to match the callee's declared parameter types.
150+
let builder = &self.expression_emitter.state.builder;
151+
for (value, &param_type) in argument_values.iter_mut().zip(parameter_types) {
152+
let conversion = TypeConversion::from_target_type(param_type, builder);
153+
*value = conversion.emit(*value, builder, &current_block);
154+
}
145155

146156
if return_types.is_empty() {
147157
self.expression_emitter.state.builder.emit_sol_call(

solx-slang/src/ast/contract/function/expression/call/type_conversion.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use melior::ir::Type;
66
use melior::ir::ValueLike;
77
use melior::ir::r#type::IntegerType;
8+
use slang_solidity::backend::ir::ast::FunctionDefinition;
9+
use slang_solidity::backend::ir::ast::Parameter;
810
use slang_solidity::backend::ir::ast::StateVariableDefinition;
911
use slang_solidity::backend::ir::ast::Type as SlangType;
1012

@@ -72,6 +74,27 @@ impl<'context> TypeConversion<'context> {
7274
Ok(Self::resolve_slang_type(&slang_type, builder))
7375
}
7476

77+
/// Resolves a function's parameter and return types from Slang AST to MLIR.
78+
pub fn resolve_function_types(
79+
function: &FunctionDefinition,
80+
builder: &solx_mlir::Builder<'context>,
81+
) -> (Vec<Type<'context>>, Vec<Type<'context>>) {
82+
let resolve = |parameter: Parameter| {
83+
Self::resolve_slang_type(
84+
&parameter
85+
.get_type()
86+
.expect("parameter type resolved by semantic analysis"),
87+
builder,
88+
)
89+
};
90+
let parameter_types = function.parameters().iter().map(&resolve).collect();
91+
let return_types = function
92+
.returns()
93+
.map(|returns| returns.iter().map(&resolve).collect())
94+
.unwrap_or_default();
95+
(parameter_types, return_types)
96+
}
97+
7598
/// Emits the conversion, returning the cast value.
7699
pub fn emit<'block>(
77100
self,

solx-slang/src/ast/contract/function/mod.rs

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -74,29 +74,8 @@ impl<'state, 'context> FunctionEmitter<'state, 'context> {
7474
let parameters = function.parameters();
7575
let mlir_name = Self::mlir_function_name(function);
7676

77-
let mlir_parameter_types: Vec<Type<'context>> = parameters
78-
.iter()
79-
.map(|param| {
80-
TypeConversion::resolve_slang_type(
81-
&param.get_type().expect("parameter type binding resolved"),
82-
&self.state.builder,
83-
)
84-
})
85-
.collect();
86-
let result_types: Vec<Type<'context>> = function
87-
.returns()
88-
.map(|returns| {
89-
returns
90-
.iter()
91-
.map(|param| {
92-
TypeConversion::resolve_slang_type(
93-
&param.get_type().expect("return type binding resolved"),
94-
&self.state.builder,
95-
)
96-
})
97-
.collect()
98-
})
99-
.unwrap_or_default();
77+
let (mlir_parameter_types, result_types) =
78+
TypeConversion::resolve_function_types(function, &self.state.builder);
10079

10180
let selector = function.compute_selector();
10281

solx-slang/src/ast/contract/mod.rs

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -117,24 +117,11 @@ impl<'state, 'context> ContractEmitter<'state, 'context> {
117117
}
118118
let name = FunctionEmitter::mlir_base_name(&function);
119119
let mlir_name = FunctionEmitter::mlir_function_name(&function);
120-
let parameter_count = function.parameters().len();
121-
let return_types: Vec<melior::ir::Type<'_>> = function
122-
.returns()
123-
.map(|returns| {
124-
returns
125-
.iter()
126-
.map(|param| {
127-
TypeConversion::resolve_slang_type(
128-
&param.get_type().expect("return type binding resolved"),
129-
&self.state.builder,
130-
)
131-
})
132-
.collect()
133-
})
134-
.unwrap_or_default();
120+
let (parameter_types, return_types) =
121+
TypeConversion::resolve_function_types(&function, &self.state.builder);
135122

136123
self.state
137-
.register_function_signature(&name, mlir_name, parameter_count, return_types);
124+
.register_function_signature(&name, mlir_name, parameter_types, return_types);
138125
}
139126
}
140127

0 commit comments

Comments
 (0)