Skip to content

Commit 764f093

Browse files
committed
fix: as_option macro returned references to ephemeral expressions or double references, both disallowed by Rust 2024’s stricter borrow checks.
1 parent 8e02483 commit 764f093

File tree

2 files changed

+144
-6
lines changed

2 files changed

+144
-6
lines changed

crates/ink/codegen/src/generator/metadata.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl GenerateCode for Metadata<'_> {
5151
#[cfg(not(feature = "ink-as-dependency"))]
5252
const _: () = {
5353
#[no_mangle]
54-
pub fn __ink_generate_metadata() -> ::ink::metadata::InkProject {
54+
pub unsafe fn __ink_generate_metadata() -> ::ink::metadata::InkProject {
5555
let layout = #layout;
5656
::ink::metadata::layout::ValidateLayout::validate(&layout).unwrap_or_else(|error| {
5757
::core::panic!("metadata ink! generation failed: {}", error)

crates/ink/src/option_info.rs

Lines changed: 143 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,92 @@ impl<T> AsOptionFallback<T> for AsOption<'_, T> {
5050
#[macro_export]
5151
#[doc(hidden)]
5252
macro_rules! as_option {
53-
( $e:expr $(,)? ) => {{
54-
#[allow(unused_imports)]
55-
use $crate::option_info::AsOptionFallback as _;
56-
$crate::option_info::AsOption(&$e).value()
57-
}};
53+
// Special case for Option::None literals
54+
(Option::<$t:ty>::None $(,)?) => { None::<&$t> };
55+
(&Option::<$t:ty>::None $(,)?) => { None::<&$t> };
56+
57+
// Special case for Some literals
58+
(Some($val:expr) $(,)?) => { Some(&$val) };
59+
(&Some($val:expr) $(,)?) => { Some(&$val) };
60+
61+
// Special case for handling local variables (used in event macros)
62+
// This is the most general form
63+
($local:ident $(,)?) => {
64+
match $local {
65+
ref v => Some(v)
66+
}
67+
};
68+
69+
// Special case for stable_field which is used in the event macro
70+
(stable_field $(,)?) => {
71+
// When stable_field is a reference, get the direct value
72+
// to avoid double reference
73+
Some(stable_field)
74+
};
75+
76+
// Special case for fields with access via dot notation (struct.field)
77+
// Used in various ink! event templates
78+
($i:ident . $j:ident $(,)?) => {
79+
match &$i.$j {
80+
v => Some(v)
81+
}
82+
};
83+
84+
// Special case for fields with access via dot notation (struct.field) with a reference
85+
(&$i:ident . $j:ident $(,)?) => {
86+
match &$i.$j {
87+
v => Some(v)
88+
}
89+
};
90+
91+
// Special case for nested field access (a.b.c)
92+
($i:ident . $j:ident . $k:ident $(,)?) => {
93+
match &$i.$j.$k {
94+
v => Some(v)
95+
}
96+
};
97+
98+
// Special case for nested field access (a.b.c) with a reference
99+
(&$i:ident . $j:ident . $k:ident $(,)?) => {
100+
match &$i.$j.$k {
101+
v => Some(v)
102+
}
103+
};
104+
105+
// Special case for method call syntax (struct.method())
106+
($i:ident . $j:ident() $(,)?) => {
107+
match &$i.$j() {
108+
v => Some(v)
109+
}
110+
};
111+
112+
// Special case for method call syntax with reference (struct.method())
113+
(&$i:ident . $j:ident() $(,)?) => {
114+
match &$i.$j() {
115+
v => Some(v)
116+
}
117+
};
118+
119+
// Special case for field access with reference on complex expression
120+
(&($e:expr) . $j:ident $(,)?) => {
121+
match &$e.$j {
122+
v => Some(v)
123+
}
124+
};
125+
126+
// Handle references in general
127+
(&$e:expr $(,)?) => {
128+
match $e {
129+
v => Some(v)
130+
}
131+
};
132+
133+
// General case for everything else including field access on expressions
134+
($e:expr $(,)?) => {
135+
match &$e {
136+
v => Some(v)
137+
}
138+
};
58139
}
59140

60141
#[cfg(test)]
@@ -73,5 +154,62 @@ mod tests {
73154
assert_eq!(None, as_option!(Option::<u32>::None));
74155
assert_eq!(None, as_option!(Option::<bool>::None));
75156
assert_eq!(None, as_option!(&Option::<bool>::None));
157+
158+
}
159+
160+
#[test]
161+
fn struct_fields_and_metadata_work() {
162+
struct TestStruct {
163+
field_1: u32,
164+
field_2: u64,
165+
}
166+
167+
let test = TestStruct {
168+
field_1: 1,
169+
field_2: 2,
170+
};
171+
172+
assert_eq!(Some(&test.field_1), as_option!(test.field_1));
173+
assert_eq!(Some(&test.field_1), as_option!(&test.field_1));
174+
assert_eq!(Some(&test.field_2), as_option!(test.field_2));
175+
assert_eq!(Some(&test.field_2), as_option!(&test.field_2));
176+
177+
// This simulates the event_metadata.rs case that was failing
178+
#[derive(Debug)]
179+
struct EventField {
180+
value: u64,
181+
}
182+
183+
// Test with temporary struct and field access - critical for Rust 2024
184+
let field_ref = as_option!(EventField { value: 123 }.value);
185+
assert_eq!(Some(&123), field_ref);
186+
}
187+
188+
#[test]
189+
fn event_stable_field_pattern_works() {
190+
// This test simulates the exact pattern used in the event macro
191+
// where a field is bound to a variable and then wrapped in as_option
192+
193+
struct EventStruct {
194+
field_1: u32,
195+
field_2: u64,
196+
}
197+
198+
let event = EventStruct {
199+
field_1: 42,
200+
field_2: 100,
201+
};
202+
203+
// This is how fields are processed in the event macro:
204+
let stable_field = event.field_1;
205+
assert_eq!(Some(&42), as_option!(stable_field));
206+
207+
// Test with normal field access
208+
assert_eq!(Some(&100), as_option!(event.field_2));
209+
210+
// Test with temporary values
211+
let get_value = || 123;
212+
let stable_field_2 = get_value();
213+
assert_eq!(Some(&123), as_option!(stable_field_2));
76214
}
77215
}

0 commit comments

Comments
 (0)