Skip to content

Commit 54a46a7

Browse files
authored
feat(slang): emit sol.this for ThisKeyword expressions (#369)
Adds a `ThisKeyword` match arm in the expression emitter so Solidity `this` lowers to `sol.this` with the enclosing contract's `!sol.contract<...>` type. Mirrors the reference pattern in `solx-solidity/libsolidity/codegen/mlir/SolidityToMLIR.cpp:611-616`. The contract type is stashed on `Context::current_contract_type` by the contract emitter, scoped per-function, so the expression layer reads it directly instead of threading the contract name through every emitter. Foundational for later work on external calls (`this.f()`), `address(this).balance`, `payable(this)`, and related patterns — does not flip any test to PASSED on its own because the tests that exercise `this` also require features still not yet supported (external calls, inline assembly). Regression checked: identical pass/fail counts against `tests/solidity/simple` with and without this change. `payable` is derived from the contract's `receive` / payable `fallback` functions, matching `ContractType(*currContract).isPayable()` in the reference compiler.
1 parent 41934fd commit 54a46a7

7 files changed

Lines changed: 93 additions & 4 deletions

File tree

solx-mlir/sol_attr_stubs.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
/*
2-
* C wrappers for Sol dialect attribute creation.
2+
* C wrappers for Sol dialect attribute and type creation.
33
*
44
* The Sol dialect's C API (mlir-c/Dialect/Sol.h) does not expose
5-
* ContractKindAttr or StateMutabilityAttr constructors. These thin
6-
* wrappers call the generated C++ get() methods via extern "C" linkage
7-
* so Rust can create these attributes through FFI.
5+
* constructors for several attributes (e.g. ContractKindAttr,
6+
* StateMutabilityAttr) or types (e.g. PointerType, AddressType,
7+
* ContractType). These thin wrappers call the generated C++ get()
8+
* methods via extern "C" linkage so Rust can create them through FFI.
89
*/
910

1011
#include "mlir/Dialect/Sol/Sol.h"
@@ -61,4 +62,11 @@ MlirType solxCreateAddressType(MlirContext ctx, bool payable) {
6162
return wrap(mlir::sol::AddressType::get(context, payable));
6263
}
6364

65+
MlirType solxCreateContractType(MlirContext ctx, const char *name_ptr,
66+
size_t name_len, bool payable) {
67+
auto *context = unwrap(ctx);
68+
llvm::StringRef name(name_ptr, name_len);
69+
return wrap(mlir::sol::ContractType::get(context, name, payable));
70+
}
71+
6472
} /* extern "C" */

solx-mlir/src/context/builder/type_factory.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,20 @@ impl<'context> TypeFactory<'context> {
104104
))
105105
}
106106
}
107+
108+
/// Creates a `sol::ContractType` for the named contract with the given payability.
109+
pub fn contract(&self, name: &str, payable: bool) -> Type<'context> {
110+
let name_bytes = name.as_bytes();
111+
// SAFETY: `solxCreateContractType` returns a valid MlirType from the
112+
// C++ Sol dialect. The context pointer and the name byte range are
113+
// valid for the duration of the call.
114+
unsafe {
115+
Type::from_raw(crate::ffi::solxCreateContractType(
116+
self.context.to_raw(),
117+
name_bytes.as_ptr() as *const std::ffi::c_char,
118+
name_bytes.len(),
119+
payable,
120+
))
121+
}
122+
}
107123
}

solx-mlir/src/context/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ pub struct Context<'context> {
4343
pub builder: Builder<'context>,
4444
/// All function signatures for call resolution (bare name -> overloads).
4545
pub function_signatures: HashMap<String, Vec<Function<'context>>>,
46+
/// The MLIR type of the contract currently being emitted, used to type
47+
/// `this` expressions. Frontends set this before emitting function bodies.
48+
pub current_contract_type: Option<Type<'context>>,
4649
}
4750

4851
impl<'context> Context<'context> {
@@ -161,6 +164,7 @@ impl<'context> Context<'context> {
161164
module,
162165
function_signatures: HashMap::new(),
163166
builder: Builder::new(context),
167+
current_contract_type: None,
164168
}
165169
}
166170

solx-mlir/src/ffi.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ unsafe extern "C" {
9696
/// Creates a `sol::AddressType` with the given payability.
9797
pub fn solxCreateAddressType(context: MlirContext, payable: bool) -> mlir_sys::MlirType;
9898

99+
/// Creates a `sol::ContractType` for a contract with the given name and payability.
100+
pub fn solxCreateContractType(
101+
context: MlirContext,
102+
name_ptr: *const std::ffi::c_char,
103+
name_len: usize,
104+
payable: bool,
105+
) -> mlir_sys::MlirType;
106+
99107
// ---- MLIR core (not in mlir-sys) ----
100108

101109
/// Returns the region that owns the given block.

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub mod storage;
1212
use std::collections::HashMap;
1313
use std::rc::Rc;
1414

15+
use melior::ir::BlockLike;
1516
use melior::ir::BlockRef;
1617
use melior::ir::Type;
1718
use melior::ir::Value;
@@ -25,6 +26,7 @@ use slang_solidity::cst::NodeId;
2526
use solx_mlir::CmpPredicate;
2627
use solx_mlir::Context;
2728
use solx_mlir::Environment;
29+
use solx_mlir::ods::sol::ThisOperation;
2830

2931
use self::call::type_conversion::TypeConversion;
3032

@@ -134,6 +136,24 @@ impl<'state, 'context, 'block> ExpressionEmitter<'state, 'context, 'block> {
134136
let value = self.state.builder.emit_arith_constant_bool(false, &block);
135137
Ok((Some(value), block))
136138
}
139+
Expression::ThisKeyword => {
140+
let contract_type = self
141+
.state
142+
.current_contract_type
143+
.ok_or_else(|| anyhow::anyhow!("sol.this emitted outside a contract"))?;
144+
let operation = ThisOperation::builder(
145+
self.state.builder.context,
146+
self.state.builder.unknown_location,
147+
)
148+
.addr(contract_type)
149+
.build();
150+
let value = block
151+
.append_operation(operation.into())
152+
.result(0)
153+
.expect("sol.this always produces one result")
154+
.into();
155+
Ok((Some(value), block))
156+
}
137157
Expression::Identifier(identifier) => {
138158
let name = identifier.name();
139159
match identifier.resolve_to_definition() {

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use std::rc::Rc;
1111
use slang_solidity::backend::SemanticAnalysis;
1212
use slang_solidity::backend::ir::ast::ContractDefinition;
1313
use slang_solidity::backend::ir::ast::FunctionKind;
14+
use slang_solidity::backend::ir::ast::FunctionMutability;
1415
use slang_solidity::cst::NodeId;
1516

1617
use solx_mlir::Context;
@@ -54,6 +55,13 @@ impl<'state, 'context> ContractEmitter<'state, 'context> {
5455
self.pre_register_functions(contract);
5556
let storage_layout = Self::compute_storage_layout(contract, file_identifier);
5657

58+
let payable = contract.functions().iter().any(|function| {
59+
matches!(function.kind(), FunctionKind::Receive)
60+
|| (matches!(function.kind(), FunctionKind::Fallback)
61+
&& matches!(function.mutability(), FunctionMutability::Payable))
62+
});
63+
let contract_type = self.state.builder.types.contract(&contract_name, payable);
64+
5765
// Emit sol.contract and functions.
5866
let module_body = self.state.module.body();
5967
let contract_body = self.state.builder.emit_sol_contract(
@@ -77,8 +85,10 @@ impl<'state, 'context> ContractEmitter<'state, 'context> {
7785
FunctionKind::Constructor => has_constructor = true,
7886
_ => {}
7987
}
88+
self.state.current_contract_type = Some(contract_type);
8089
let emitter = FunctionEmitter::new(&self.semantic, self.state, &storage_layout);
8190
emitter.emit_sol(&function, &contract_body)?;
91+
self.state.current_contract_type = None;
8292
}
8393

8494
// Emit a default constructor if the contract doesn't define one.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//! { "cases": [ {
2+
//! "name": "main",
3+
//! "inputs": [
4+
//! {
5+
//! "method": "main",
6+
//! "calldata": [
7+
//! ]
8+
//! }
9+
//! ],
10+
//! "expected": [
11+
//! "1"
12+
//! ]
13+
//! } ] }
14+
15+
// SPDX-License-Identifier: MIT
16+
17+
pragma solidity >=0.8.0;
18+
19+
contract Test {
20+
function main() public view returns (bool) {
21+
return address(this) == address(this);
22+
}
23+
}

0 commit comments

Comments
 (0)