Skip to content

Commit 874b8e2

Browse files
committed
Fix some minor issues.
- Make it explicit that reject must be non-zero. - Fix the Default of Reject instance to be non-zero. - Remove the need for an allocator in generated code (format! uses an allocator)
1 parent 467aa3b commit 874b8e2

File tree

4 files changed

+99
-89
lines changed

4 files changed

+99
-89
lines changed

concordium-std-derive/src/lib.rs

Lines changed: 81 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,6 @@ fn init_worker(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream>
181181
let rust_export_fn_name = format_ident!("export_{}", fn_name);
182182
let wasm_export_fn_name = format!("init_{}", contract_name);
183183
let amount_ident = format_ident!("amount");
184-
let exceeded_error_code_limit =
185-
format!("Error code should not exceed {} (i32::MAX).", i32::MAX);
186184

187185
// Accumulate a list of required arguments, if the function contains a
188186
// different number of arguments, than elements in this vector, then the
@@ -198,15 +196,18 @@ fn init_worker(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream>
198196
#[export_name = #wasm_export_fn_name]
199197
pub extern "C" fn #rust_export_fn_name(#amount_ident: Amount) -> i32 {
200198
use concordium_std::{trap, ExternContext, InitContextExtern, ContractState};
201-
use concordium_std::convert::TryFrom;
202199
#setup_fn_optional_args
203200
let ctx = ExternContext::<InitContextExtern>::open(());
204201
let mut state = ContractState::open(());
205202
match #fn_name(&ctx, #(#fn_optional_args, )* &mut state) {
206203
Ok(()) => 0,
207204
Err(reject) => {
208-
let code = Reject::from(reject).error_code;
209-
-i32::try_from(code).expect(#exceeded_error_code_limit)
205+
let code = Reject::from(reject).error_code.get();
206+
if code <= i32::MAX as u32 {
207+
- (code as i32)
208+
} else {
209+
trap() // precondition violation
210+
}
210211
}
211212
}
212213
}
@@ -216,7 +217,6 @@ fn init_worker(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream>
216217
#[export_name = #wasm_export_fn_name]
217218
pub extern "C" fn #rust_export_fn_name(amount: Amount) -> i32 {
218219
use concordium_std::{trap, ExternContext, InitContextExtern, ContractState};
219-
use concordium_std::convert::TryFrom;
220220
#setup_fn_optional_args
221221
let ctx = ExternContext::<InitContextExtern>::open(());
222222
match #fn_name(&ctx, #(#fn_optional_args),*) {
@@ -228,8 +228,12 @@ fn init_worker(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream>
228228
0
229229
}
230230
Err(reject) => {
231-
let code = Reject::from(reject).error_code;
232-
-i32::try_from(code).expect(#exceeded_error_code_limit)
231+
let code = Reject::from(reject).error_code.get();
232+
if code <= i32::MAX as u32 {
233+
- (code as i32)
234+
} else {
235+
trap() // precondition violation
236+
}
233237
}
234238
}
235239
}
@@ -374,8 +378,6 @@ fn receive_worker(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStre
374378
let rust_export_fn_name = format_ident!("export_{}", fn_name);
375379
let wasm_export_fn_name = format!("{}.{}", contract_name, name);
376380
let amount_ident = format_ident!("amount");
377-
let exceeded_error_code_limit =
378-
format!("Error code should not exceed {} (i32::MAX).", i32::MAX);
379381

380382
// Accumulate a list of required arguments, if the function contains a
381383
// different number of arguments, than elements in this vector, then the
@@ -391,7 +393,6 @@ fn receive_worker(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStre
391393
#[export_name = #wasm_export_fn_name]
392394
pub extern "C" fn #rust_export_fn_name(#amount_ident: Amount) -> i32 {
393395
use concordium_std::{SeekFrom, ContractState, Logger, ReceiveContextExtern, ExternContext};
394-
use concordium_std::convert::TryFrom;
395396
#setup_fn_optional_args
396397
let ctx = ExternContext::<ReceiveContextExtern>::open(());
397398
let mut state = ContractState::open(());
@@ -401,8 +402,12 @@ fn receive_worker(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStre
401402
act.tag() as i32
402403
}
403404
Err(reject) => {
404-
let code = Reject::from(reject).error_code;
405-
-i32::try_from(code).expect(#exceeded_error_code_limit)
405+
let code = Reject::from(reject).error_code.get();
406+
if code <= i32::MAX as u32 {
407+
- (code as i32)
408+
} else {
409+
trap() // precondition violation
410+
}
406411
}
407412
}
408413
}
@@ -414,7 +419,6 @@ fn receive_worker(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStre
414419
#[export_name = #wasm_export_fn_name]
415420
pub extern "C" fn #rust_export_fn_name(#amount_ident: Amount) -> i32 {
416421
use concordium_std::{SeekFrom, ContractState, Logger, trap};
417-
use concordium_std::convert::TryFrom;
418422
#setup_fn_optional_args
419423
let ctx = ExternContext::<ReceiveContextExtern>::open(());
420424
let mut state_bytes = ContractState::open(());
@@ -432,8 +436,12 @@ fn receive_worker(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStre
432436
}
433437
}
434438
Err(reject) => {
435-
let code = Reject::from(reject).error_code;
436-
-i32::try_from(code).expect(#exceeded_error_code_limit)
439+
let code = Reject::from(reject).error_code.get();
440+
if code <= i32::MAX as u32 {
441+
- (code as i32)
442+
} else {
443+
trap() // precondition violation
444+
}
437445
}
438446
}
439447
} else {
@@ -1246,10 +1254,12 @@ const RESERVED_ERROR_CODES: i32 = i32::MIN + 100;
12461254
/// // TimeExpired(time: Timestamp), /* currently not supported */
12471255
/// ...
12481256
/// }
1257+
/// ```
12491258
/// ```ignore
12501259
/// #[receive(contract = "my_contract", name = "some_receive")]
12511260
/// fn receive<A: HasActions>(ctx: &impl HasReceiveContext, state: &mut MyState)
1252-
/// -> Result<A, MyError> {...} ```
1261+
/// -> Result<A, MyError> {...}
1262+
/// ```
12531263
#[proc_macro_derive(Reject, attributes(from))]
12541264
pub fn reject_derive(input: TokenStream) -> TokenStream {
12551265
unwrap_or_report(reject_derive_worker(input))
@@ -1277,14 +1287,13 @@ fn reject_derive_worker(input: TokenStream) -> syn::Result<TokenStream> {
12771287
}
12781288
};
12791289

1280-
let variant_error_conversions: Vec<_> =
1281-
generate_variant_error_conversions(&enum_data, &enum_ident);
1290+
let variant_error_conversions = generate_variant_error_conversions(&enum_data, &enum_ident)?;
12821291

12831292
let gen = quote! {
12841293
impl From<#enum_ident> for Reject {
1285-
#[inline]
1294+
#[inline(always)]
12861295
fn from(e: #enum_ident) -> Self {
1287-
Reject { error_code: (e as u32 + 1) }
1296+
Reject { error_code: unsafe { concordium_std::num::NonZeroU32::new_unchecked((e as u32 + 1)) } }
12881297
}
12891298
}
12901299

@@ -1306,51 +1315,69 @@ fn reject_derive_worker(input: TokenStream) -> syn::Result<TokenStream> {
13061315
fn generate_variant_error_conversions(
13071316
enum_data: &DataEnum,
13081317
enum_name: &syn::Ident,
1309-
) -> Vec<proc_macro2::TokenStream> {
1310-
enum_data
1318+
) -> syn::Result<Vec<proc_macro2::TokenStream>> {
1319+
Ok(enum_data
13111320
.variants
13121321
.iter()
1313-
.flat_map(|variant| {
1322+
.map(|variant| {
1323+
// in the future we might incorporate explicit discriminants,
1324+
// but the general case of this requires evaluating constant expressions,
1325+
// which is not easily supported at the moment.
1326+
if let Some((_, discriminant)) = variant.discriminant.as_ref() {
1327+
return Err(syn::Error::new(
1328+
discriminant.span(),
1329+
"Explicit discriminants are not yet supported.",
1330+
));
1331+
}
13141332
let variant_attributes = variant.attrs.iter();
1315-
variant_attributes.flat_map(move |attr| {
1316-
parse_attr_and_gen_error_conversions(attr, enum_name, &variant.ident)
1317-
})
1333+
variant_attributes
1334+
.map(move |attr| {
1335+
parse_attr_and_gen_error_conversions(attr, enum_name, &variant.ident)
1336+
})
1337+
.collect::<syn::Result<Vec<_>>>()
13181338
})
1319-
.collect()
1339+
.collect::<syn::Result<Vec<_>>>()?
1340+
.into_iter()
1341+
.flatten()
1342+
.flatten()
1343+
.collect())
13201344
}
13211345

13221346
/// Generate error conversion for a given enum variant.
13231347
fn parse_attr_and_gen_error_conversions(
13241348
attr: &syn::Attribute,
13251349
enum_name: &syn::Ident,
13261350
variant_name: &syn::Ident,
1327-
) -> Vec<proc_macro2::TokenStream> {
1351+
) -> syn::Result<Vec<proc_macro2::TokenStream>> {
13281352
let wrong_from_usage = |x: &dyn Spanned| {
13291353
let err = syn::Error::new(
13301354
x.span(),
13311355
"The `from` attribute expects a list of error types, e.g.: #[from(ParseError)].",
13321356
);
1333-
vec![err.to_compile_error()]
1357+
err
13341358
};
13351359
match attr.parse_meta() {
13361360
Ok(syn::Meta::List(list)) if list.path.is_ident("from") => {
13371361
let mut from_error_names = vec![];
13381362
for nested in list.nested.iter() {
1339-
if let syn::NestedMeta::Meta(syn::Meta::Path(from_error)) = nested {
1340-
match from_error.get_ident() {
1341-
Some(ident) => {
1363+
// check that all items in the list are paths
1364+
match nested {
1365+
syn::NestedMeta::Meta(meta) => match meta {
1366+
Meta::Path(from_error) => {
1367+
let ident = from_error
1368+
.get_ident()
1369+
.ok_or_else(|| wrong_from_usage(from_error))?;
13421370
from_error_names.push(ident);
13431371
}
1344-
None => {
1345-
return wrong_from_usage(from_error);
1346-
}
1347-
}
1372+
other => return Err(wrong_from_usage(&other)),
1373+
},
1374+
syn::NestedMeta::Lit(l) => return Err(wrong_from_usage(&l)),
13481375
}
13491376
}
1350-
from_error_token_stream(from_error_names, &enum_name, variant_name)
1377+
Ok(from_error_token_stream(&from_error_names, &enum_name, variant_name).collect())
13511378
}
1352-
Ok(syn::Meta::NameValue(mnv)) if mnv.path.is_ident("from") => wrong_from_usage(&mnv),
1353-
_ => vec![],
1379+
Ok(syn::Meta::NameValue(mnv)) if mnv.path.is_ident("from") => Err(wrong_from_usage(&mnv)),
1380+
_ => Ok(vec![]),
13541381
}
13551382
}
13561383

@@ -1362,23 +1389,20 @@ fn parse_attr_and_gen_error_conversions(
13621389
/// }
13631390
/// }
13641391
/// ```
1365-
fn from_error_token_stream(
1366-
paths: Vec<&syn::Ident>,
1367-
enum_name: &syn::Ident,
1368-
variant_name: &syn::Ident,
1369-
) -> Vec<proc_macro2::TokenStream> {
1370-
paths
1371-
.iter()
1372-
.map(|from_error| {
1373-
quote! {
1374-
impl From<#from_error> for #enum_name {
1375-
#[inline]
1376-
fn from(fe: #from_error) -> Self {
1377-
#enum_name::#variant_name
1378-
}
1379-
}}
1380-
})
1381-
.collect()
1392+
fn from_error_token_stream<'a>(
1393+
paths: &'a [&'a syn::Ident],
1394+
enum_name: &'a syn::Ident,
1395+
variant_name: &'a syn::Ident,
1396+
) -> impl Iterator<Item = proc_macro2::TokenStream> + 'a {
1397+
paths.iter().map(move |from_error| {
1398+
quote! {
1399+
impl From<#from_error> for #enum_name {
1400+
#[inline]
1401+
fn from(fe: #from_error) -> Self {
1402+
#enum_name::#variant_name
1403+
}
1404+
}}
1405+
})
13821406
}
13831407

13841408
#[proc_macro_attribute]

concordium-std/src/impls.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{convert, mem, prims::*, traits::*, types::*};
1+
use crate::{convert, mem, num, prims::*, traits::*, types::*};
22
use concordium_contracts_common::*;
33

44
use mem::MaybeUninit;
@@ -7,7 +7,7 @@ impl convert::From<()> for Reject {
77
#[inline(always)]
88
fn from(_: ()) -> Self {
99
Reject {
10-
error_code: -(i32::MIN + 1) as u32,
10+
error_code: unsafe { num::NonZeroU32::new_unchecked((i32::MAX - 1) as u32) },
1111
}
1212
}
1313
}
@@ -16,7 +16,7 @@ impl convert::From<ParseError> for Reject {
1616
#[inline(always)]
1717
fn from(_: ParseError) -> Self {
1818
Reject {
19-
error_code: -(i32::MIN + 2) as u32,
19+
error_code: unsafe { num::NonZeroU32::new_unchecked((i32::MAX - 2) as u32) },
2020
}
2121
}
2222
}

concordium-std/src/lib.rs

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -150,42 +150,18 @@ fn abort_panic(_info: &core::panic::PanicInfo) -> ! {
150150

151151
// Provide some re-exports to make it easier to use the library.
152152
// This should be expanded in the future.
153-
#[cfg(not(feature = "std"))]
154-
pub use core::result::*;
155-
156-
/// Re-export.
157-
#[cfg(not(feature = "std"))]
158-
pub use alloc::collections;
159-
/// Re-export.
160-
#[cfg(not(feature = "std"))]
161-
pub use alloc::{string, string::String, string::ToString, vec, vec::Vec};
162-
/// Re-export.
163-
#[cfg(not(feature = "std"))]
164-
pub use core::convert;
165153
/// Re-export.
166154
#[cfg(not(feature = "std"))]
167-
pub use core::marker;
155+
pub use alloc::{collections, string, string::String, string::ToString, vec, vec::Vec};
168156
/// Re-export.
169157
#[cfg(not(feature = "std"))]
170-
pub use core::mem;
158+
pub use core::{convert, marker, mem, num, result::*};
171159
#[cfg(feature = "std")]
172160
pub(crate) use std::vec;
173-
#[cfg(feature = "std")]
174-
pub use std::vec::Vec;
175161

176162
/// Re-export.
177163
#[cfg(feature = "std")]
178-
pub use std::collections;
179-
/// Re-export.
180-
#[cfg(feature = "std")]
181-
pub use std::convert;
182-
#[cfg(feature = "std")]
183-
pub use std::marker;
184-
/// Re-export.
185-
#[cfg(feature = "std")]
186-
pub use std::mem;
187-
#[cfg(feature = "std")]
188-
pub use std::string::String;
164+
pub use std::{collections, convert, marker, mem, num, string::String, vec::Vec};
189165

190166
/// Chain constants that impose limits on various aspects of smart contract
191167
/// execution.

concordium-std/src/types.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,19 @@ impl Action {
4848
/// provided, the error message corresponding to the error code will be
4949
/// displayed. The valid range for an error code is from 1 to i32::MAX. We are
5050
/// using a u32 here to highlight that the error code should be nonnegative.
51-
#[derive(Default, Eq, PartialEq, Debug)]
51+
#[derive(Eq, PartialEq, Debug)]
5252
pub struct Reject {
53-
pub error_code: u32,
53+
pub error_code: crate::num::NonZeroU32,
54+
}
55+
56+
/// Default error is i32::MAX (the i32 is deliberate)
57+
impl Default for Reject {
58+
#[inline(always)]
59+
fn default() -> Self {
60+
Self {
61+
error_code: unsafe { crate::num::NonZeroU32::new_unchecked(i32::MAX as u32) },
62+
}
63+
}
5464
}
5565

5666
// Macros for failing a contract function

0 commit comments

Comments
 (0)