Skip to content

Commit d5dcb95

Browse files
authored
Merge branch 'main' into typos1
2 parents d1c65e3 + c63cfcc commit d5dcb95

File tree

12 files changed

+406
-8
lines changed

12 files changed

+406
-8
lines changed

src/bin/cli/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,10 @@ pub struct DebugFeatures {
349349
#[arg(name = "RELEASE", help = "Disable all debugging features such as prints, logging runtime errors, and logging api return codes", long = "release", action = ArgAction::SetTrue)]
350350
#[serde(default)]
351351
pub release: bool,
352+
353+
#[arg(name = "STRICTSOROBANTYPES", help = "Turn Soroban integer width warnings into errors for stricter type safety", long = "strict-soroban-types", action = ArgAction::SetTrue)]
354+
#[serde(default)]
355+
pub strict_soroban_types: bool,
352356
}
353357

354358
impl Default for DebugFeatures {
@@ -358,6 +362,7 @@ impl Default for DebugFeatures {
358362
log_prints: true,
359363
generate_debug_info: false,
360364
release: false,
365+
strict_soroban_types: false,
361366
}
362367
}
363368
}
@@ -581,6 +586,7 @@ pub fn options_arg(
581586
opt_level,
582587
log_runtime_errors: debug.log_runtime_errors && !debug.release,
583588
log_prints: debug.log_prints && !debug.release,
589+
strict_soroban_types: debug.strict_soroban_types,
584590
#[cfg(feature = "wasm_opt")]
585591
wasm_opt: optimizations.wasm_opt_passes.or(if debug.release {
586592
Some(OptimizationPasses::Z)

src/bin/cli/test.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ mod tests {
214214
log_runtime_errors: true,
215215
log_prints: true,
216216
generate_debug_info: false,
217-
release: false
217+
release: false,
218+
strict_soroban_types: false,
218219
},
219220
optimizations: cli::Optimizations {
220221
dead_storage: true,
@@ -269,7 +270,8 @@ mod tests {
269270
log_runtime_errors: true,
270271
log_prints: true,
271272
generate_debug_info: false,
272-
release: false
273+
release: false,
274+
strict_soroban_types: false,
273275
},
274276
optimizations: cli::Optimizations {
275277
dead_storage: false,

src/bin/solang.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,12 @@ fn doc(doc_args: Doc) {
129129
let mut files = Vec::new();
130130

131131
for filename in doc_args.package.input {
132-
let ns = solang::parse_and_resolve(filename.as_os_str(), &mut resolver, target);
132+
let ns = solang::parse_and_resolve_with_options(
133+
filename.as_os_str(),
134+
&mut resolver,
135+
target,
136+
None,
137+
);
133138

134139
ns.print_diagnostics(&resolver, verbose);
135140

@@ -322,7 +327,8 @@ fn process_file(
322327
};
323328

324329
// resolve phase
325-
let mut ns = solang::parse_and_resolve(filepath.as_os_str(), resolver, target);
330+
let mut ns =
331+
solang::parse_and_resolve_with_options(filepath.as_os_str(), resolver, target, Some(opt));
326332

327333
// codegen all the contracts; some additional errors/warnings will be detected here
328334
codegen(&mut ns, opt);

src/codegen/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ pub struct Options {
170170
pub opt_level: OptimizationLevel,
171171
pub log_runtime_errors: bool,
172172
pub log_prints: bool,
173+
pub strict_soroban_types: bool,
173174
#[cfg(feature = "wasm_opt")]
174175
pub wasm_opt: Option<OptimizationPasses>,
175176
pub soroban_version: Option<u64>,
@@ -187,6 +188,7 @@ impl Default for Options {
187188
opt_level: OptimizationLevel::Default,
188189
log_runtime_errors: false,
189190
log_prints: true,
191+
strict_soroban_types: false,
190192
#[cfg(feature = "wasm_opt")]
191193
wasm_opt: None,
192194
soroban_version: None,

src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ pub fn compile(
127127
authors: Vec<String>,
128128
version: &str,
129129
) -> (Vec<(Vec<u8>, String)>, sema::ast::Namespace) {
130-
let mut ns = parse_and_resolve(filename, resolver, target);
130+
let mut ns = parse_and_resolve_with_options(filename, resolver, target, Some(opts));
131131

132132
if ns.diagnostics.any_errors() {
133133
return (Vec::new(), ns);
@@ -167,6 +167,16 @@ pub fn parse_and_resolve(
167167
filename: &OsStr,
168168
resolver: &mut FileResolver,
169169
target: Target,
170+
) -> sema::ast::Namespace {
171+
parse_and_resolve_with_options(filename, resolver, target, None)
172+
}
173+
174+
/// Parse and resolve the Solidity source code with options.
175+
pub fn parse_and_resolve_with_options(
176+
filename: &OsStr,
177+
resolver: &mut FileResolver,
178+
target: Target,
179+
_options: Option<&codegen::Options>,
170180
) -> sema::ast::Namespace {
171181
let mut ns = sema::ast::Namespace::new(target);
172182

src/sema/ast.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,63 @@ impl Type {
143143
self
144144
}
145145
}
146+
147+
/// Round integer width to Soroban-compatible size and emit warning if needed
148+
pub fn round_soroban_width(&self, ns: &mut Namespace, loc: pt::Loc) -> Type {
149+
match self {
150+
Type::Int(width) => {
151+
let rounded_width = Self::get_soroban_int_width(*width);
152+
if rounded_width != *width {
153+
let message = format!(
154+
"int{} is not supported by the Soroban runtime and will be rounded up to int{}",
155+
width, rounded_width
156+
);
157+
if ns.strict_soroban_types {
158+
ns.diagnostics.push(Diagnostic::error(loc, message));
159+
} else {
160+
ns.diagnostics.push(Diagnostic::warning(loc, message));
161+
}
162+
Type::Int(rounded_width)
163+
} else {
164+
Type::Int(*width)
165+
}
166+
}
167+
Type::Uint(width) => {
168+
let rounded_width = Self::get_soroban_int_width(*width);
169+
if rounded_width != *width {
170+
let message = format!(
171+
"uint{} is not supported by the Soroban runtime and will be rounded up to uint{}",
172+
width, rounded_width
173+
);
174+
if ns.strict_soroban_types {
175+
ns.diagnostics.push(Diagnostic::error(loc, message));
176+
} else {
177+
ns.diagnostics.push(Diagnostic::warning(loc, message));
178+
}
179+
Type::Uint(rounded_width)
180+
} else {
181+
Type::Uint(*width)
182+
}
183+
}
184+
_ => self.clone(),
185+
}
186+
}
187+
188+
/// Get the Soroban-compatible integer width by rounding up to the next supported size
189+
pub fn get_soroban_int_width(width: u16) -> u16 {
190+
match width {
191+
1..=32 => 32,
192+
33..=64 => 64,
193+
65..=128 => 128,
194+
129..=256 => 256,
195+
_ => width, // Keep as-is if already 256+ or invalid
196+
}
197+
}
198+
199+
/// Check if an integer width is Soroban-compatible
200+
pub fn is_soroban_compatible_width(width: u16) -> bool {
201+
matches!(width, 32 | 64 | 128 | 256)
202+
}
146203
}
147204

148205
#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash)]
@@ -703,6 +760,8 @@ pub struct Namespace {
703760
pub var_constants: HashMap<pt::Loc, codegen::Expression>,
704761
/// Overrides for hover in the language server
705762
pub hover_overrides: HashMap<pt::Loc, String>,
763+
/// Strict mode for Soroban integer width checking
764+
pub strict_soroban_types: bool,
706765
}
707766

708767
#[derive(Debug)]

src/sema/namespace.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ impl Namespace {
6666
next_id: 0,
6767
var_constants: HashMap::new(),
6868
hover_overrides: HashMap::new(),
69+
strict_soroban_types: false,
6970
};
7071

7172
match target {
@@ -1191,7 +1192,14 @@ impl Namespace {
11911192
Type::Address(true)
11921193
}
11931194
}
1194-
_ => Type::from(ty),
1195+
_ => {
1196+
let mut ty = Type::from(ty);
1197+
// Apply Soroban integer width rounding if target is Soroban
1198+
if self.target == Target::Soroban {
1199+
ty = ty.round_soroban_width(self, id.loc());
1200+
}
1201+
ty
1202+
}
11951203
};
11961204

11971205
return if dimensions.is_empty() {

tests/soroban.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// SPDX-License-Identifier: Apache-2.0
22

33
#[cfg(feature = "soroban")]
4-
pub mod soroban_testcases;
5-
64
use solang::codegen::Options;
75
use solang::file_resolver::FileResolver;
86
use solang::sema::ast::Namespace;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
use crate::build_solidity;
4+
use soroban_sdk::{IntoVal, Val};
5+
6+
#[test]
7+
fn test_int56_rounds_to_int64() {
8+
let runtime = build_solidity(
9+
r#"contract test {
10+
function test_int56(int56 a) public returns (int64) {
11+
return int64(a);
12+
}
13+
}"#,
14+
|_| {},
15+
);
16+
17+
// Check that the function compiles and works with the rounded type
18+
let arg: Val = 42_i64.into_val(&runtime.env);
19+
let addr = runtime.contracts.last().unwrap();
20+
let res = runtime.invoke_contract(addr, "test_int56", vec![arg]);
21+
22+
let expected: Val = 42_i64.into_val(&runtime.env);
23+
assert!(expected.shallow_eq(&res));
24+
}
25+
26+
#[test]
27+
fn test_uint56_rounds_to_uint64() {
28+
let runtime = build_solidity(
29+
r#"contract test {
30+
function test_uint56(uint56 a) public returns (uint64) {
31+
return uint64(a);
32+
}
33+
}"#,
34+
|_| {},
35+
);
36+
37+
let arg: Val = 42_u64.into_val(&runtime.env);
38+
let addr = runtime.contracts.last().unwrap();
39+
let res = runtime.invoke_contract(addr, "test_uint56", vec![arg]);
40+
41+
let expected: Val = 42_u64.into_val(&runtime.env);
42+
assert!(expected.shallow_eq(&res));
43+
}
44+
45+
#[test]
46+
fn test_int96_rounds_to_int128() {
47+
let runtime = build_solidity(
48+
r#"contract test {
49+
function test_int96(int96 a) public returns (int128) {
50+
return int128(a);
51+
}
52+
}"#,
53+
|_| {},
54+
);
55+
56+
let arg: Val = 42_i128.into_val(&runtime.env);
57+
let addr = runtime.contracts.last().unwrap();
58+
let res = runtime.invoke_contract(addr, "test_int96", vec![arg]);
59+
60+
let expected: Val = 42_i128.into_val(&runtime.env);
61+
assert!(expected.shallow_eq(&res));
62+
}
63+
64+
#[test]
65+
fn test_uint96_rounds_to_uint128() {
66+
let runtime = build_solidity(
67+
r#"contract test {
68+
function test_uint96(uint96 a) public returns (uint128) {
69+
return uint128(a);
70+
}
71+
}"#,
72+
|_| {},
73+
);
74+
75+
let arg: Val = 42_u128.into_val(&runtime.env);
76+
let addr = runtime.contracts.last().unwrap();
77+
let res = runtime.invoke_contract(addr, "test_uint96", vec![arg]);
78+
79+
let expected: Val = 42_u128.into_val(&runtime.env);
80+
assert!(expected.shallow_eq(&res));
81+
}

0 commit comments

Comments
 (0)