Skip to content

Commit fc639ec

Browse files
committed
Fix nightly syntax combo gate
1 parent 208127c commit fc639ec

5 files changed

Lines changed: 136 additions & 25 deletions

File tree

‎Cargo.toml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ exclude = [
1616
".rustfmt.toml",
1717
".github/",
1818
"A2_TYPE_RESOLUTION_AUDIT.md",
19+
"BRANCHES.md",
20+
"README_REVIEW.md",
1921
"RUST_COMPARATIVE_AUDIT.md",
2022
"SECURITY_AUDIT_SEMANTIC.md",
2123
"cellscript-*.png",

‎src/fmt/mod.rs‎

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -549,26 +549,14 @@ impl Formatter {
549549
let fields = preserve.fields.join("\n");
550550
format!("preserve {} from {} {{\n{}\n}}", preserve.output_name, preserve.input_name, fields)
551551
}
552-
Expr::Block(stmts) => {
553-
let inner = stmts
554-
.iter()
555-
.map(|stmt| {
556-
let mut formatter = Formatter::new(self.config.clone());
557-
formatter.indent_level = 0;
558-
formatter.format_stmt(stmt);
559-
formatter.output.trim().to_string()
560-
})
561-
.collect::<Vec<_>>()
562-
.join(" ");
563-
format!("{{ {} }}", inner)
564-
}
552+
Expr::Block(stmts) => format!("{{ {} }}", self.format_expr_block_body(stmts)),
565553
Expr::Tuple(items) => format!("({})", items.iter().map(|item| self.format_expr(item)).collect::<Vec<_>>().join(", ")),
566554
Expr::Array(items) => format!("[{}]", items.iter().map(|item| self.format_expr(item)).collect::<Vec<_>>().join(", ")),
567555
Expr::If(if_expr) => format!(
568556
"if {} {{ {} }} else {{ {} }}",
569557
self.format_expr(&if_expr.condition),
570-
self.format_expr(&if_expr.then_branch),
571-
self.format_expr(&if_expr.else_branch)
558+
self.format_branch_expr_body(&if_expr.then_branch),
559+
self.format_branch_expr_body(&if_expr.else_branch)
572560
),
573561
Expr::Cast(cast) => format!("{} as {}", self.format_expr(&cast.expr), format_type(&cast.ty)),
574562
Expr::Range(range) => format!("{}..{}", self.format_expr(&range.start), self.format_expr(&range.end)),
@@ -599,6 +587,26 @@ impl Formatter {
599587
}
600588
}
601589

590+
fn format_branch_expr_body(&self, expr: &Expr) -> String {
591+
match expr {
592+
Expr::Block(stmts) => self.format_expr_block_body(stmts),
593+
expr => self.format_expr(expr),
594+
}
595+
}
596+
597+
fn format_expr_block_body(&self, stmts: &[Stmt]) -> String {
598+
stmts
599+
.iter()
600+
.map(|stmt| {
601+
let mut formatter = Formatter::new(self.config.clone());
602+
formatter.indent_level = 0;
603+
formatter.format_stmt(stmt);
604+
formatter.output.trim().to_string()
605+
})
606+
.collect::<Vec<_>>()
607+
.join(" ")
608+
}
609+
602610
fn format_field_initializer(&self, name: &str, value: &Expr) -> String {
603611
if matches!(value, Expr::Identifier(identifier) if identifier == name) {
604612
name.to_string()
@@ -867,6 +875,28 @@ where
867875
assert!(formatted.contains("create token = Token { amount, symbol }"), "unexpected formatted source:\n{}", formatted);
868876
}
869877

878+
#[test]
879+
fn format_if_expression_branch_blocks_are_idempotent() {
880+
let source = r#"
881+
module demo
882+
883+
action choose(flag: bool) -> u64
884+
where
885+
let pair = if flag { (1, 2) } else { (3, 4) }
886+
return pair.0
887+
"#;
888+
let tokens = lexer::lex(source).unwrap();
889+
let module = parser::parse(&tokens).unwrap();
890+
let formatted = format_default(&module).unwrap();
891+
let tokens2 = lexer::lex(&formatted).unwrap();
892+
let module2 = parser::parse(&tokens2).unwrap();
893+
let formatted2 = format_default(&module2).unwrap();
894+
895+
assert_eq!(formatted, formatted2);
896+
assert!(formatted.contains("let pair = if flag { (1, 2) } else { (3, 4) }"), "unexpected formatted source:\n{}", formatted);
897+
assert!(!formatted.contains("{{"), "unexpected formatted source:\n{}", formatted);
898+
}
899+
870900
#[test]
871901
fn format_uses_canonical_assert_and_no_const_semicolon() {
872902
let source = r#"

‎src/ir/mod.rs‎

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,10 +1961,10 @@ impl IrGenerator {
19611961
self.record_error("string literals are only supported in metadata positions such as assert messages", Span::default());
19621962
LoweredExpr { operand: IrOperand::Const(IrConst::U64(0)), current: Some(current) }
19631963
}
1964-
Expr::ByteString(_) => {
1965-
self.record_error("byte string literals require an explicit lowered byte-array context", Span::default());
1966-
LoweredExpr { operand: IrOperand::Const(IrConst::U64(0)), current: Some(current) }
1967-
}
1964+
Expr::ByteString(bytes) => LoweredExpr {
1965+
operand: IrOperand::Const(IrConst::Array(bytes.iter().copied().map(IrConst::U8).collect())),
1966+
current: Some(current),
1967+
},
19681968
Expr::Range(_) => {
19691969
self.record_error("range expressions are only supported as for-loop iterables", Span::default());
19701970
LoweredExpr { operand: IrOperand::Const(IrConst::U64(0)), current: Some(current) }
@@ -2013,7 +2013,7 @@ impl IrGenerator {
20132013
IrConst::Bool(_) => IrType::Bool,
20142014
IrConst::Address(_) => IrType::Address,
20152015
IrConst::Hash(_) => IrType::Hash,
2016-
IrConst::Array(_) => IrType::Array(Box::new(IrType::U8), 0),
2016+
IrConst::Array(items) => IrType::Array(Box::new(IrType::U8), items.len()),
20172017
}
20182018
}
20192019

@@ -6208,6 +6208,40 @@ where
62086208
);
62096209
}
62106210

6211+
#[test]
6212+
fn byte_string_literal_lowers_to_fixed_array_const() {
6213+
let source = r#"
6214+
module test
6215+
6216+
action symbol() -> [u8; 4]
6217+
where
6218+
return b"TEST"
6219+
"#;
6220+
let ir = parse_and_lower(source);
6221+
let action = ir
6222+
.items
6223+
.iter()
6224+
.find_map(|item| match item {
6225+
IrItem::Action(a) if a.name == "symbol" => Some(a),
6226+
_ => None,
6227+
})
6228+
.expect("expected symbol action");
6229+
let bytes = action.body.blocks.iter().find_map(|block| match &block.terminator {
6230+
IrTerminator::Return(Some(IrOperand::Const(IrConst::Array(items)))) => Some(items),
6231+
_ => None,
6232+
});
6233+
6234+
let bytes = bytes.expect("expected byte string return to lower to a fixed array const");
6235+
let lowered = bytes
6236+
.iter()
6237+
.map(|byte| match byte {
6238+
IrConst::U8(byte) => *byte,
6239+
other => panic!("expected fixed byte const, got {:?}", other),
6240+
})
6241+
.collect::<Vec<_>>();
6242+
assert_eq!(lowered, b"TEST");
6243+
}
6244+
62116245
#[test]
62126246
fn stdlib_transfer_lowers_to_single_consumed_input_and_locked_output() {
62136247
let source = r#"

‎src/repl.rs‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ impl Repl {
6363
println!(
6464
"{}",
6565
r#"
66-
____ _ _ _ _
67-
/ ___|__| | ___ | |_| |__ ___ | |_
66+
____ _ _ _ _
67+
/ ___|__| | ___ | |_| |__ ___ | |_
6868
| | / _` |/ _ \| __| '_ \ / _ \| __|
69-
| |__| (_| | (_) | |_| | | | (_) | |_
69+
| |__| (_| | (_) | |_| | | | (_) | |_
7070
\____\__,_|\___/ \__|_| |_|\___/ \__|
71-
71+
7272
CellScript Interactive Shell
7373
Version 0.1.0
7474
"#

‎src/types/mod.rs‎

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2659,7 +2659,7 @@ impl<'a> TypeChecker<'a> {
26592659
Expr::Integer(_) => Ok(Type::U64),
26602660
Expr::Bool(_) => Ok(Type::Bool),
26612661
Expr::String(_) => Ok(Type::Named("String".to_string())),
2662-
Expr::ByteString(_) => Ok(Type::Array(Box::new(Type::U8), 0)),
2662+
Expr::ByteString(bytes) => Ok(Type::Array(Box::new(Type::U8), bytes.len())),
26632663
Expr::Identifier(name) => {
26642664
if let Some(ty) = env.lookup(name).cloned() {
26652665
Ok(ty)
@@ -3334,9 +3334,15 @@ impl<'a> TypeChecker<'a> {
33343334

33353335
for arm in &match_expr.arms {
33363336
if arm.pattern == "_" {
3337+
if has_wildcard {
3338+
return Err(CompileError::new("duplicate wildcard match arm", arm.span));
3339+
}
33373340
has_wildcard = true;
33383341
continue;
33393342
}
3343+
if has_wildcard {
3344+
return Err(CompileError::new("wildcard pattern '_' must be the last match arm", arm.span));
3345+
}
33403346
let Some(variant) = match_pattern_variant(enum_name, &arm.pattern) else {
33413347
return Err(CompileError::new(
33423348
format!("match pattern '{}' does not match enum '{}'", arm.pattern, enum_name),
@@ -6275,6 +6281,45 @@ where
62756281
assert!(err.message.contains("require block contains assignment"), "unexpected error: {}", err.message);
62766282
}
62776283

6284+
#[test]
6285+
fn byte_string_literal_infers_exact_fixed_array_length() {
6286+
let module = source_module(
6287+
r#"
6288+
module test
6289+
6290+
action symbol() -> [u8; 4]
6291+
where
6292+
return b"TEST"
6293+
"#,
6294+
);
6295+
6296+
check(&module).unwrap();
6297+
}
6298+
6299+
#[test]
6300+
fn match_wildcard_arm_must_be_last() {
6301+
let module = source_module(
6302+
r#"
6303+
module test
6304+
6305+
enum Flag {
6306+
Off,
6307+
On,
6308+
}
6309+
6310+
action bad(flag: Flag) -> u64
6311+
where
6312+
return match flag {
6313+
_ => { 1 },
6314+
Flag::Off => { 2 },
6315+
}
6316+
"#,
6317+
);
6318+
6319+
let err = check(&module).unwrap_err();
6320+
assert!(err.message.contains("wildcard pattern '_' must be the last match arm"), "unexpected error: {}", err.message);
6321+
}
6322+
62786323
#[test]
62796324
fn stdlib_claim_rejects_non_receipt_input() {
62806325
let module = source_module(

0 commit comments

Comments
 (0)