Skip to content

Commit 185fb32

Browse files
committed
feat(slang): emit default constructor with FunctionKind attribute
Skip modifier functions during emission, track whether a constructor exists, and emit a default empty constructor with FunctionKind::Constructor when the contract doesn't define one. Map each Slang FunctionKind to the corresponding Sol dialect FunctionKind attribute on sol.func.
1 parent 61d6c78 commit 185fb32

File tree

9 files changed

+81
-4
lines changed

9 files changed

+81
-4
lines changed

solx-mlir/sol_attr_stubs.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ MlirAttribute solxCreateStateMutabilityAttr(MlirContext ctx, uint32_t mutability
3232
return wrap(attr);
3333
}
3434

35+
MlirAttribute solxCreateFunctionKindAttr(MlirContext ctx, uint32_t kind) {
36+
if (kind > 2) abort();
37+
auto *context = unwrap(ctx);
38+
auto attr = mlir::sol::FunctionKindAttr::get(
39+
context, static_cast<mlir::sol::FunctionKind>(kind));
40+
return wrap(attr);
41+
}
42+
3543
MlirAttribute solxCreateEvmVersionAttr(MlirContext ctx, uint32_t version) {
3644
if (version < 11 || version > 13) abort();
3745
auto *context = unwrap(ctx);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//!
2+
//! Sol dialect function kind attribute.
3+
//!
4+
5+
/// Sol dialect function kind.
6+
///
7+
/// Maps to the `FunctionKindAttr` values in the C++ Sol dialect.
8+
/// Regular functions do not carry a kind attribute.
9+
#[repr(u32)]
10+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11+
pub enum FunctionKind {
12+
/// Constructor function.
13+
Constructor = 0,
14+
/// Fallback function.
15+
Fallback = 1,
16+
/// Receive function.
17+
Receive = 2,
18+
}

solx-mlir/src/attributes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
55
pub mod cmp_predicate;
66
pub mod contract_kind;
7+
pub mod function_kind;
78
pub mod state_mutability;

solx-mlir/src/context/builder.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ impl<'context> Builder<'context> {
249249
}
250250

251251
/// Emits a `sol.func` operation with the given name, parameter types,
252-
/// result types, selector, and state mutability.
252+
/// result types, selector, state mutability, and optional function kind.
253253
///
254254
/// Returns the entry block of the function body for appending operations.
255255
///
@@ -263,6 +263,7 @@ impl<'context> Builder<'context> {
263263
result_types: &[Type<'context>],
264264
selector: Option<u32>,
265265
state_mutability: StateMutability,
266+
kind: Option<crate::FunctionKind>,
266267
block: &BlockRef<'context, 'block>,
267268
) -> BlockRef<'context, 'block> {
268269
let function_type = FunctionType::new(self.context, parameter_types, result_types);
@@ -298,6 +299,17 @@ impl<'context> Builder<'context> {
298299
),
299300
];
300301

302+
if let Some(function_kind) = kind {
303+
// SAFETY: `solxCreateFunctionKindAttr` returns a valid MlirAttribute.
304+
let kind_attribute = unsafe {
305+
Attribute::from_raw(crate::ffi::solxCreateFunctionKindAttr(
306+
self.context.to_raw(),
307+
function_kind as u32,
308+
))
309+
};
310+
attributes.push((Identifier::new(self.context, "kind"), kind_attribute));
311+
}
312+
301313
if let Some(selector_value) = selector {
302314
attributes.push((
303315
Identifier::new(self.context, "selector"),

solx-mlir/src/ffi.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ unsafe extern "C" {
7575
mutability: u32,
7676
) -> mlir_sys::MlirAttribute;
7777

78+
/// Creates a `FunctionKindAttr` (0=Constructor, 1=Fallback, 2=Receive).
79+
pub fn solxCreateFunctionKindAttr(context: MlirContext, kind: u32) -> mlir_sys::MlirAttribute;
80+
7881
/// Creates an `EvmVersionAttr`.
7982
pub fn solxCreateEvmVersionAttr(context: MlirContext, version: u32) -> mlir_sys::MlirAttribute;
8083

@@ -104,8 +107,6 @@ unsafe extern "C" {
104107
pub fn block_parent_region<'context, 'block>(
105108
block: &melior::ir::BlockRef<'context, 'block>,
106109
) -> melior::ir::RegionRef<'context, 'block> {
107-
// SAFETY: The block is attached (guaranteed by melior's ownership model).
108-
// `mlirBlockGetParentRegion` returns a non-owning handle to the parent.
109110
// SAFETY: The block is attached (guaranteed by melior's ownership model).
110111
// `mlirBlockGetParentRegion` returns a non-owning handle to the parent.
111112
unsafe {

solx-mlir/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub mod llvm_module;
1616

1717
pub use self::attributes::cmp_predicate::CmpPredicate;
1818
pub use self::attributes::contract_kind::ContractKind;
19+
pub use self::attributes::function_kind::FunctionKind;
1920
pub use self::attributes::state_mutability::StateMutability;
2021
pub use self::context::Context;
2122
pub use self::context::builder::Builder;

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,23 @@ impl<'state, 'context> FunctionEmitter<'state, 'context> {
104104

105105
let state_mutability = Self::map_state_mutability(function);
106106

107+
let mlir_kind = match function.kind() {
108+
FunctionKind::Constructor => Some(solx_mlir::FunctionKind::Constructor),
109+
FunctionKind::Fallback | FunctionKind::Unnamed => {
110+
Some(solx_mlir::FunctionKind::Fallback)
111+
}
112+
FunctionKind::Receive => Some(solx_mlir::FunctionKind::Receive),
113+
FunctionKind::Regular => None,
114+
FunctionKind::Modifier => unreachable!("modifiers are filtered before emission"),
115+
};
116+
107117
let function_entry_block = self.state.builder.emit_sol_func(
108118
&mlir_name,
109119
&mlir_parameter_types,
110120
&result_types,
111121
selector,
112122
state_mutability,
123+
mlir_kind,
113124
contract_body,
114125
);
115126

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use std::rc::Rc;
1010

1111
use slang_solidity::backend::SemanticAnalysis;
1212
use slang_solidity::backend::ir::ast::ContractDefinition;
13+
use slang_solidity::backend::ir::ast::FunctionKind;
1314
use slang_solidity::cst::NodeId;
1415

1516
use solx_mlir::Context;
@@ -68,18 +69,41 @@ impl<'state, 'context> ContractEmitter<'state, 'context> {
6869
.emit_sol_state_var(&format!("slot_{slot}"), *slot, &contract_body);
6970
}
7071

72+
let mut has_constructor = false;
7173
for function in contract.functions() {
74+
match function.kind() {
75+
FunctionKind::Modifier => continue,
76+
FunctionKind::Constructor => has_constructor = true,
77+
_ => {}
78+
}
7279
let emitter = FunctionEmitter::new(&self.semantic, self.state, &storage_layout);
7380
emitter.emit_sol(&function, &contract_body)?;
7481
}
7582

83+
// Emit a default constructor if the contract doesn't define one.
84+
if !has_constructor {
85+
let entry = self.state.builder.emit_sol_func(
86+
"constructor()",
87+
&[],
88+
&[],
89+
None,
90+
solx_mlir::StateMutability::NonPayable,
91+
Some(solx_mlir::FunctionKind::Constructor),
92+
&contract_body,
93+
);
94+
self.state.builder.emit_sol_return(&[], &entry);
95+
}
96+
7697
Ok(())
7798
}
7899

79100
/// Pre-registers all function signatures for call resolution before bodies
80101
/// are emitted.
81102
fn pre_register_functions(&mut self, contract: &ContractDefinition) {
82103
for function in contract.functions() {
104+
if matches!(function.kind(), FunctionKind::Modifier) {
105+
continue;
106+
}
83107
let name = FunctionEmitter::mlir_base_name(&function);
84108
let mlir_name = FunctionEmitter::mlir_function_name(&function);
85109
let parameter_count = function.parameters().len();

solx-slang/src/ast/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ impl<'state, 'context> AstEmitter<'state, 'context> {
4646
/// # Errors
4747
///
4848
/// Returns an error if code generation encounters unsupported constructs.
49-
/// Returns `Some(contract_name)` if a contract was emitted, `None` otherwise.
49+
/// Returns `Some((contract_name, method_identifiers))` if a contract was
50+
/// emitted, `None` otherwise.
5051
pub fn emit(
5152
&mut self,
5253
unit: &SourceUnit,

0 commit comments

Comments
 (0)