From 068611cd47a6fe3bf2518eccbc149e46aab8e4bb Mon Sep 17 00:00:00 2001 From: Sdoba16 Date: Thu, 4 Jun 2026 17:11:15 +0300 Subject: [PATCH 1/5] Add version checking engine --- Cargo.lock | 7 ++ Cargo.toml | 1 + src/error.rs | 153 ++++++++++++++++++++++++ src/version.rs | 311 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 472 insertions(+) create mode 100644 src/version.rs diff --git a/Cargo.lock b/Cargo.lock index b298cfdb..db2a3671 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -572,6 +572,12 @@ dependencies = [ "secp256k1-sys", ] +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + [[package]] name = "serde" version = "1.0.188" @@ -648,6 +654,7 @@ dependencies = [ "getrandom", "itertools", "miniscript", + "semver", "serde", "serde_json", "simplicity-lang", diff --git a/Cargo.toml b/Cargo.toml index b376b561..62fc9614 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ itertools = "0.13.0" arbitrary = { version = "1", optional = true, features = ["derive"] } clap = "4.5.37" chumsky = "0.11.2" +semver = "1.0.27" [target.wasm32-unknown-unknown.dependencies] getrandom = { version = "0.2", features = ["js"] } diff --git a/src/error.rs b/src/error.rs index 83420973..e6cfa9d1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -475,6 +475,29 @@ impl fmt::Display for ErrorCollector { /// Records _what_ happened but not where. #[derive(Debug, Clone)] pub enum Error { + MissingSimcVersion { + compiler: String, + }, + InvalidSimcVersionSyntax { + err: String, + }, + SimcVersionExactMismatch { + required: String, + current: String, + }, + SimcVersionCompilerTooOld { + required: String, + current: String, + }, + SimcVersionContractTooOld { + required: String, + current: String, + }, + SimcVersionIncompatible { + required: String, + current: String, + }, + DependencyPathNotFound { path: PathBuf, }, @@ -640,6 +663,30 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + Error::MissingSimcVersion { compiler } => write!( + f, + "Missing compiler version: Contract must declare a version, e.g., `{}{}{}`", crate::version::DIRECTIVE_PREFIX, compiler, crate::version::DIRECTIVE_SUFFIX + ), + Error::InvalidSimcVersionSyntax { err } => write!( + f, + "Invalid version syntax: {}", err + ), + Error::SimcVersionExactMismatch { required, current } => write!( + f, + "Exact version mismatch: Contract requires {}, but compiler is {}", required, current + ), + Error::SimcVersionCompilerTooOld { required, current } => write!( + f, + "Compiler too old: Contract requires {}, but compiler is {}. Please upgrade.", required, current + ), + Error::SimcVersionContractTooOld { required, current } => write!( + f, + "Contract too old: Contract requires {}, which is not supported by compiler {}.", required, current + ), + Error::SimcVersionIncompatible { required, current } => write!( + f, + "Incompatible version: Contract requirement '{}' is not satisfied by compiler {}.", required, current + ), Error::DependencyPathNotFound { path } => write!( f, "Path not found: {}", path.display() @@ -1074,6 +1121,112 @@ let x: u32 = Left( assert_eq!(&expected[1..], &error.to_string()); } + #[test] + fn display_compiler_version_missing() { + let file = "fn main() {}"; + let error = Error::MissingSimcVersion { + compiler: "0.5.0".to_string(), + } + .with_span(Span::new(0, 0)) + .with_content(Arc::from(file)); + + let expected = r#" + | +1 | fn main() {} + | ^ Missing compiler version: Contract must declare a version, e.g., `simc "0.5.0";`"#; + + assert_eq!(&expected[1..], &error.to_string()); + } + + #[test] + fn display_compiler_version_invalid_syntax() { + let file = "simc \"abc\";\nfn main() {}"; + let error = Error::InvalidSimcVersionSyntax { + err: "unexpected character 'a'".to_string(), + } + .with_span(Span::new(0, 11)) + .with_content(Arc::from(file)); + + let expected = r#" + | +1 | simc "abc"; + | ^^^^^^^^^^^ Invalid version syntax: unexpected character 'a'"#; + + assert_eq!(&expected[1..], &error.to_string()); + } + + #[test] + fn display_compiler_version_exact_mismatch() { + let file = "simc \"= 0.4.0\";\nfn main() {}"; + let error = Error::SimcVersionExactMismatch { + required: "= 0.4.0".to_string(), + current: "0.5.0".to_string(), + } + .with_span(Span::new(0, 15)) + .with_content(Arc::from(file)); + + let expected = r#" + | +1 | simc "= 0.4.0"; + | ^^^^^^^^^^^^^^^ Exact version mismatch: Contract requires = 0.4.0, but compiler is 0.5.0"#; + + assert_eq!(&expected[1..], &error.to_string()); + } + + #[test] + fn display_compiler_version_too_old() { + let file = "simc \">= 0.6.0\";\nfn main() {}"; + let error = Error::SimcVersionCompilerTooOld { + required: ">= 0.6.0".to_string(), + current: "0.5.0".to_string(), + } + .with_span(Span::new(0, 16)) + .with_content(Arc::from(file)); + + let expected = r#" + | +1 | simc ">= 0.6.0"; + | ^^^^^^^^^^^^^^^^ Compiler too old: Contract requires >= 0.6.0, but compiler is 0.5.0. Please upgrade."#; + + assert_eq!(&expected[1..], &error.to_string()); + } + + #[test] + fn display_compiler_version_contract_too_old() { + let file = "simc \"< 0.4.0\";\nfn main() {}"; + let error = Error::SimcVersionContractTooOld { + required: "< 0.4.0".to_string(), + current: "0.5.0".to_string(), + } + .with_span(Span::new(0, 15)) + .with_content(Arc::from(file)); + + let expected = r#" + | +1 | simc "< 0.4.0"; + | ^^^^^^^^^^^^^^^ Contract too old: Contract requires < 0.4.0, which is not supported by compiler 0.5.0."#; + + assert_eq!(&expected[1..], &error.to_string()); + } + + #[test] + fn display_compiler_version_incompatible() { + let file = "simc \"~0.6.0\";\nfn main() {}"; + let error = Error::SimcVersionIncompatible { + required: "~0.6.0".to_string(), + current: "0.5.0".to_string(), + } + .with_span(Span::new(0, 14)) + .with_content(Arc::from(file)); + + let expected = r#" + | +1 | simc "~0.6.0"; + | ^^^^^^^^^^^^^^ Incompatible version: Contract requirement '~0.6.0' is not satisfied by compiler 0.5.0."#; + + assert_eq!(&expected[1..], &error.to_string()); + } + // --- Tests with filename --- #[test] fn display_single_line_with_file() { diff --git a/src/version.rs b/src/version.rs new file mode 100644 index 00000000..04663a29 --- /dev/null +++ b/src/version.rs @@ -0,0 +1,311 @@ +use semver::{Version, VersionReq}; + +use crate::error::{Error, Span}; + +pub const DIRECTIVE_PREFIX: &str = "simc \""; +pub const DIRECTIVE_SUFFIX: &str = "\";"; + +/// Wrapper around `semver::VersionReq` for clarity and future extensibility +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct VersionRequirement { + req: VersionReq, + original_str: String, +} + +impl VersionRequirement { + pub fn parse(s: &str) -> Result { + VersionReq::parse(s) + .map(|req| VersionRequirement { + req, + original_str: s.to_string(), + }) + .map_err(|e| e.to_string()) + } + + pub fn matches(&self, version: &Version) -> bool { + let has_pre = self.req.comparators.iter().any(|c| !c.pre.is_empty()); + if !has_pre && !version.pre.is_empty() { + let mut stripped = version.clone(); + stripped.pre = semver::Prerelease::EMPTY; + self.req.matches(&stripped) + } else { + self.req.matches(version) + } + } + + pub fn classify_mismatch(&self, current: &Version) -> VersionMismatchKind { + if self.is_exact_mismatch() { + return VersionMismatchKind::ExactMismatch; + } + + let (violated_lower, violated_upper) = self.check_bounds(current); + self.resolve_mismatch_kind(current, violated_lower, violated_upper) + } + + fn is_exact_mismatch(&self) -> bool { + let s = self.original_str.trim(); + s.starts_with('=') + || (!s.contains('^') + && !s.contains('~') + && !s.contains('<') + && !s.contains('>') + && !s.contains('*')) + || self + .req + .comparators + .iter() + .any(|c| matches!(c.op, semver::Op::Exact)) + } + + fn check_bounds(&self, current: &Version) -> (bool, bool) { + let mut violated_lower = false; + let mut violated_upper = false; + + for comp in &self.req.comparators { + let comp_ver = Self::comparator_to_version(comp); + + match comp.op { + semver::Op::Greater if current <= &comp_ver => violated_lower = true, + semver::Op::GreaterEq if current < &comp_ver => violated_lower = true, + semver::Op::Less if current >= &comp_ver => violated_upper = true, + semver::Op::LessEq if current > &comp_ver => violated_upper = true, + semver::Op::Caret | semver::Op::Tilde => { + if current < &comp_ver { + violated_lower = true; + } else { + violated_upper = true; + } + } + _ => {} + } + } + + (violated_lower, violated_upper) + } + + fn resolve_mismatch_kind( + &self, + current: &Version, + violated_lower: bool, + violated_upper: bool, + ) -> VersionMismatchKind { + if violated_lower && violated_upper { + VersionMismatchKind::Incompatible + } else if violated_lower { + VersionMismatchKind::CompilerTooOld + } else if violated_upper { + VersionMismatchKind::ContractTooOld + } else if let Some(comp) = self.req.comparators.first() { + if current > &Self::comparator_to_version(comp) { + VersionMismatchKind::ContractTooOld + } else { + VersionMismatchKind::CompilerTooOld + } + } else { + VersionMismatchKind::Incompatible + } + } + + fn comparator_to_version(comp: &semver::Comparator) -> Version { + Version { + major: comp.major, + minor: comp.minor.unwrap_or(0), + patch: comp.patch.unwrap_or(0), + pre: comp.pre.clone(), + build: semver::BuildMetadata::EMPTY, + } + } + + pub fn describe_range(&self) -> String { + self.req.to_string() + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum VersionMismatchKind { + ExactMismatch, + CompilerTooOld, + ContractTooOld, + Incompatible, +} + +impl VersionMismatchKind { + pub fn into_error(self, required: String, current: String) -> Error { + match self { + Self::ExactMismatch => Error::SimcVersionExactMismatch { required, current }, + Self::CompilerTooOld => Error::SimcVersionCompilerTooOld { required, current }, + Self::ContractTooOld => Error::SimcVersionContractTooOld { required, current }, + Self::Incompatible => Error::SimcVersionIncompatible { required, current }, + } + } +} + +pub fn current_version() -> &'static str { + env!("CARGO_PKG_VERSION") +} + +pub fn inject_version_header(content: &str) -> String { + let current = current_version(); + format!("{DIRECTIVE_PREFIX}{current}{DIRECTIVE_SUFFIX}\n{content}") +} + +pub fn extract_version_directive(content: &str) -> Result, Span> { + let mut found = None; + let mut in_block_comment = false; + let mut current_offset = 0; + + for line in content.split_inclusive('\n') { + let trimmed = skip_block_comments(line.trim_start(), &mut in_block_comment); + + if in_block_comment || trimmed.is_empty() || trimmed.starts_with("//") { + current_offset += line.len(); + continue; + } + + if let Some(directive) = extract_directive_from_line(line, trimmed, current_offset) { + if found.is_some() { + return Err(directive.1); + } + found = Some(directive); + } else { + break; + } + + current_offset += line.len(); + } + Ok(found) +} + +fn skip_block_comments<'a>(mut trimmed: &'a str, in_block_comment: &mut bool) -> &'a str { + loop { + if *in_block_comment { + if let Some(end_idx) = trimmed.find("*/") { + trimmed = trimmed[end_idx + 2..].trim_start(); + *in_block_comment = false; + } else { + break; + } + } else if let Some(rest) = trimmed.strip_prefix("/*") { + *in_block_comment = true; + trimmed = rest; + } else { + break; + } + } + trimmed +} + +fn extract_directive_from_line<'a>( + line: &str, + trimmed: &'a str, + current_offset: usize, +) -> Option<(&'a str, Span)> { + if !trimmed.starts_with("simc") { + return None; + } + let after_simc = &trimmed[4..]; + if !after_simc.is_empty() && !after_simc.starts_with(|c: char| c.is_whitespace() || c == '"') { + return None; + } + + let rest = after_simc.trim_start(); + let rest = rest.strip_prefix('"')?; + let end_quote_idx = rest.find('"')?; + let req_str = &rest[..end_quote_idx]; + + let after_quote = &rest[end_quote_idx + 1..].trim_start(); + if !after_quote.starts_with(';') { + return None; + } + + let col_offset = line.len() - trimmed.len(); + let span_start = current_offset + col_offset; + let semi_offset = after_quote.as_ptr() as usize - trimmed.as_ptr() as usize; + let span_end = span_start + semi_offset + 1; + + Some((req_str, Span::new(span_start, span_end))) +} + +pub fn strip_version_header(content: &str) -> String { + match extract_version_directive(content) { + Ok(Some((_, span))) => { + let mut stripped = String::with_capacity(content.len()); + stripped.push_str(&content[..span.start]); + + let mut end = span.end; + if content[end..].starts_with("\r\n") { + end += 2; + } else if content[end..].starts_with('\n') { + end += 1; + } + + stripped.push_str(&content[end..]); + stripped + } + _ => content.to_string(), + } +} + +pub fn has_version_header(content: &str) -> bool { + matches!(extract_version_directive(content), Ok(Some(_))) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_version_requirement_parsing() { + assert!(VersionRequirement::parse("=0.5.0").is_ok()); + assert!(VersionRequirement::parse("^0.5.0").is_ok()); + assert!(VersionRequirement::parse(">=0.1.0").is_ok()); + assert!(VersionRequirement::parse("invalid!!!").is_err()); + } + + #[test] + fn test_version_requirement_matching() { + let req = VersionRequirement::parse("^0.5.0").unwrap(); + let v050 = Version::parse("0.5.0").unwrap(); + let v051 = Version::parse("0.5.1").unwrap(); + let v060 = Version::parse("0.6.0").unwrap(); + + assert!(req.matches(&v050)); + assert!(req.matches(&v051)); + assert!(!req.matches(&v060)); + } + + #[test] + fn test_mismatch_classification_exact() { + let req = VersionRequirement::parse("=0.5.0").unwrap(); + let v050 = Version::parse("0.5.0").unwrap(); + let v051 = Version::parse("0.5.1").unwrap(); + + assert!(req.matches(&v050)); + assert!(!req.matches(&v051)); + assert_eq!( + req.classify_mismatch(&v051), + VersionMismatchKind::ExactMismatch + ); + } + + #[test] + fn test_version_header_injection() { + let content = "fn main() {}"; + let injected = inject_version_header(content); + assert!(injected.starts_with(DIRECTIVE_PREFIX)); + assert!(injected.contains("fn main() {}")); + } + + #[test] + fn test_has_version_header() { + assert!(has_version_header("simc \"=0.5.0\";\nfn main() {}")); + assert!(!has_version_header("fn main() {}")); + } + + #[test] + fn test_strip_version_header() { + let content = "simc \"=0.5.0\";\nfn main() {}"; + let stripped = strip_version_header(content); + assert_eq!(stripped, "fn main() {}"); + } +} From 052ccc3bdf6bf54d913257a73eb4cf3c1a368980 Mon Sep 17 00:00:00 2001 From: Sdoba16 Date: Thu, 4 Jun 2026 17:13:49 +0300 Subject: [PATCH 2/5] Integrate version checking engine into compiler --- src/driver/mod.rs | 20 +++++- src/lexer.rs | 17 +++++ src/lib.rs | 167 +++++++++++++++++++++++++++++++++++++++++----- src/parse.rs | 120 ++++++++++++++++++++++++++++++++- src/tracker.rs | 11 ++- src/witness.rs | 22 +++--- 6 files changed, 325 insertions(+), 32 deletions(-) diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 0f026f84..4cac35f3 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -30,6 +30,9 @@ mod linearization; pub(crate) mod resolve_order; +#[cfg(test)] +mod version_tests; + use std::collections::{HashMap, HashSet, VecDeque}; use std::path::PathBuf; use std::sync::Arc; @@ -223,6 +226,10 @@ impl DependencyGraph { let ast = parse::Program::parse_from_str_with_errors(source.clone(), &mut error_handler); + if let Some(parsed) = &ast { + parsed.check_version(&source, &mut error_handler); + } + if error_handler.has_errors() { handler.extend_with_handler(source, &error_handler); None @@ -356,11 +363,18 @@ pub(crate) mod tests { // Create all requested files for (path, content) in files { let full_path = format!("workspace/{}", path); - let created_file = canon(&ws.create_file(&full_path, content)); + let injected_content = if crate::version::has_version_header(content) + || content.contains("// NO_INJECT") + { + content.to_string() + } else { + crate::version::inject_version_header(content) + }; + let created_file = canon(&ws.create_file(&full_path, &injected_content)); if path == "main.simf" { root_file_path = Some(created_file); - root_content = content.to_string(); + root_content = injected_content; } } @@ -374,6 +388,8 @@ pub(crate) mod tests { return (None, HashMap::new(), ws, handler); }; + main_program.check_version(&main_canon_source, &mut handler); + let graph_option = DependencyGraph::new(main_canon_source, map, &main_program, &mut handler).unwrap(); diff --git a/src/lexer.rs b/src/lexer.rs index 06d63adc..45493ebc 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -41,6 +41,8 @@ pub enum Token<'src> { RBrace, LAngle, RAngle, + Hash, + Bang, // Number literals DecLiteral(Decimal), @@ -50,6 +52,9 @@ pub enum Token<'src> { // Boolean literal Bool(bool), + //String literal + StringLiteral(&'src str), + // Identifier Ident(&'src str), @@ -97,6 +102,8 @@ impl<'src> fmt::Display for Token<'src> { Token::RBrace => write!(f, "}}"), Token::LAngle => write!(f, "<"), Token::RAngle => write!(f, ">"), + Token::Hash => write!(f, "#"), + Token::Bang => write!(f, "!"), Token::DecLiteral(s) => write!(f, "{}", s), Token::HexLiteral(s) => write!(f, "0x{}", s), @@ -105,6 +112,7 @@ impl<'src> fmt::Display for Token<'src> { Token::Ident(s) => write!(f, "{}", s), Token::Macro(s) => write!(f, "{}", s), + Token::StringLiteral(s) => write!(f, "\"{}\"", s), Token::Jet(s) => write!(f, "jet::{}", s), Token::Witness(s) => write!(f, "witness::{}", s), @@ -191,6 +199,8 @@ pub fn lexer<'src>( just("}").to(Token::RBrace), just("<").to(Token::LAngle), just(">").to(Token::RAngle), + just("#").to(Token::Hash), + just("!").to(Token::Bang), )); let comment = just("//") @@ -209,6 +219,12 @@ pub fn lexer<'src>( Token::BlockComment }) }); + + let string_literal = just('"') + .ignore_then(any().and_is(just('"').not()).repeated().to_slice()) + .then_ignore(just('"')) + .map(Token::StringLiteral); + let token = choice(( comment, block_comment, @@ -220,6 +236,7 @@ pub fn lexer<'src>( hex, bin, num, + string_literal, op, )); diff --git a/src/lib.rs b/src/lib.rs index 707ce622..a825628a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,7 @@ pub mod test_utils; pub mod tracker; pub mod types; pub mod value; +pub mod version; mod witness; use std::sync::Arc; @@ -76,6 +77,11 @@ impl TemplateProgram { parse::Program::parse_from_str_with_errors(source.clone(), &mut error_handler) .ok_or_else(|| error_handler.to_string())?; + parsed_program.check_version(&source, &mut error_handler); + if error_handler.has_errors() { + return Err(error_handler.to_string()); + } + // 2. Create the driver program let graph = DependencyGraph::new( source.clone(), @@ -111,10 +117,16 @@ impl TemplateProgram { let file = s.into(); let source = SourceFile::anonymous(file.clone()); let mut error_handler = ErrorCollector::new(); - let parse_program = parse::Program::parse_from_str_with_errors(source, &mut error_handler); + let parse_program = + parse::Program::parse_from_str_with_errors(source.clone(), &mut error_handler); let driver_program = if let Some(parse_program) = parse_program { - driver::Program::from_parse(&parse_program, file.clone(), &mut error_handler) + parse_program.check_version(&source, &mut error_handler); + if error_handler.has_errors() { + None + } else { + driver::Program::from_parse(&parse_program, file.clone(), &mut error_handler) + } } else { None }; @@ -464,8 +476,11 @@ pub(crate) mod tests { } pub fn template_text(program_text: Cow) -> Self { + let clean_text = crate::version::strip_version_header(&program_text); + let injected_text = crate::version::inject_version_header(&clean_text); + let program = match TemplateProgram::new( - program_text.as_ref(), + injected_text.as_str(), Box::new(ElementsJetHinter::new()), ) { Ok(x) => x, @@ -746,11 +761,13 @@ pub(crate) mod tests { let root = ws.create_dir("workspace"); ws.create_file( "workspace/main.simf", - "use crate::utils::add;\nfn main() { assert!(jet::eq_32(add(2, 2), 4)); }", + &crate::version::inject_version_header( + "use crate::utils::add;\nfn main() { assert!(jet::eq_32(add(2, 2), 4)); }", + ), ); ws.create_file( "workspace/utils.simf", - "pub fn add(a: u32, b: u32) -> u32 { let (_, sum): (bool, u32) = jet::add_32(a, b); sum }", + &crate::version::inject_version_header("pub fn add(a: u32, b: u32) -> u32 { let (_, sum): (bool, u32) = jet::add_32(a, b); sum }"), ); let main_path = root.join("main.simf"); @@ -766,11 +783,12 @@ pub(crate) mod tests { #[test] fn test_anonymous_source_compiles_without_dependencies() { - let code = "fn main() { assert!(true); }"; - let program = TemplateProgram::new(code, Box::new(ElementsJetHinter::new())); + let code = crate::version::inject_version_header("fn main() { assert!(true); }"); + let program = TemplateProgram::new(code.as_str(), Box::new(ElementsJetHinter::new())); assert!( program.is_ok(), - "TemplateProgram::new should successfully compile anonymous source files without requiring canonical paths" + "TemplateProgram::new should successfully compile anonymous source files without requiring canonical paths, error: {:?}", + program.err() ); } @@ -960,14 +978,17 @@ pub(crate) mod tests { #[test] fn empty_function_body_nonempty_return() { - let prog_text = r#"fn my_true() -> bool { + let prog_text = crate::version::inject_version_header( + r#" +fn my_true() -> bool { // function body is empty, although function must return `bool` } fn main() { assert!(my_true()); } -"#; +"#, + ); match SatisfiedProgram::new( prog_text, Arguments::default(), @@ -1172,6 +1193,115 @@ fn main() { regression_test("transfer_with_timeout"); } } + + #[test] + fn test_compiler_version_missing() { + let missing = "fn main() {}"; + let err = TemplateProgram::new(missing, Box::new(crate::ast::ElementsJetHinter::new())) + .unwrap_err(); + assert!(err.contains("Missing compiler version")); + } + + #[test] + fn test_compiler_version_checked_with_empty_dependency_map() { + let missing = "fn main() {}"; + let temp = crate::test_utils::TempWorkspace::new( + "test_compiler_version_checked_with_empty_dependency_map", + ); + let root = crate::source::CanonPath::canonicalize(&temp.create_dir("test")).unwrap(); + let source = CanonSourceFile::new(root.clone(), Arc::from(missing)); + let empty_map = crate::resolution::DependencyMapBuilder::new(root) + .build() + .unwrap(); + let err = TemplateProgram::new_with_dep( + source, + &empty_map, + Box::new(crate::ast::ElementsJetHinter::new()), + ) + .unwrap_err(); + assert!(err.contains("Missing compiler version")); + } + + #[test] + fn test_compiler_version_exact_match() { + let exact = crate::version::inject_version_header("fn main() {}"); + assert!(TemplateProgram::new( + exact.as_str(), + Box::new(crate::ast::ElementsJetHinter::new()) + ) + .is_ok()); + } + + #[test] + fn test_compiler_version_mismatch_too_old() { + // We require 99.99.99, meaning the current compiler is TOO OLD + let too_old = "simc \">= 99.99.99\";\nfn main() {}"; + let err = TemplateProgram::new(too_old, Box::new(crate::ast::ElementsJetHinter::new())) + .unwrap_err(); + assert!( + err.contains("Compiler too old"), + "Expected 'Compiler too old', got: {}", + err + ); + } + + #[test] + fn test_compiler_version_contract_too_old() { + // We strictly require an ancient version, meaning the contract is TOO OLD for this compiler + let contract_too_old = "simc \"< 0.0.1\";\nfn main() {}"; + let err = TemplateProgram::new( + contract_too_old, + Box::new(crate::ast::ElementsJetHinter::new()), + ) + .unwrap_err(); + assert!( + err.contains("Contract too old"), + "Expected 'Contract too old', got: {}", + err + ); + } + + #[test] + fn test_compiler_version_exact_mismatch() { + let exact_mismatch = "simc \"= 0.0.1\";\nfn main() {}"; + let err = TemplateProgram::new( + exact_mismatch, + Box::new(crate::ast::ElementsJetHinter::new()), + ) + .unwrap_err(); + assert!( + err.contains("Exact version mismatch"), + "Expected 'Exact version mismatch', got: {}", + err + ); + } + + #[test] + fn test_compiler_version_operator_caret() { + let caret = format!("simc \"^{}\";\nfn main() {{}}", env!("CARGO_PKG_VERSION")); + assert!(TemplateProgram::new( + caret.as_str(), + Box::new(crate::ast::ElementsJetHinter::new()) + ) + .is_ok()); + } + + #[test] + fn test_compiler_version_operator_gte() { + let gte = format!("simc \">={}\";\nfn main() {{}}", env!("CARGO_PKG_VERSION")); + assert!( + TemplateProgram::new(gte.as_str(), Box::new(crate::ast::ElementsJetHinter::new())) + .is_ok() + ); + } + + #[test] + fn test_compiler_version_syntax_garbage_version() { + let garbage = "simc \"i-love-rust\";\nfn main() {}"; + let err = TemplateProgram::new(garbage, Box::new(crate::ast::ElementsJetHinter::new())) + .unwrap_err(); + assert!(err.contains("Invalid version syntax")); + } } #[cfg(test)] @@ -1209,11 +1339,11 @@ mod error_tests { let lib_dir = ws.create_dir("workspace/lib"); let main_path = ws.create_file( "workspace/main.simf", - "use lib::bad::f;\nfn main() { f(); }\n", + &crate::version::inject_version_header("use lib::bad::f;\nfn main() { f(); }\n"), ); let bad_path = ws.create_file( "workspace/lib/bad.simf", - "pub fn f() { let x: u32 = true; }\n", + &crate::version::inject_version_header("pub fn f() { let x: u32 = true; }\n"), ); let dependencies = dependency_map(&root_dir, "lib", &lib_dir); @@ -1239,13 +1369,18 @@ mod error_tests { let lib_dir = ws.create_dir("workspace/lib"); let main_path = ws.create_file( "workspace/main.simf", - "use lib::nested::two;\nfn main() { assert!(jet::eq_32(two(), 2)); }\n", + &crate::version::inject_version_header( + "use lib::nested::two;\nfn main() { assert!(jet::eq_32(two(), 2)); }\n", + ), ); ws.create_file( "workspace/lib/nested.simf", - "use lib::base::one;\npub fn two() -> u32 {\n let (_, out): (bool, u32) = jet::add_32(one(), 1);\n out\n}\n", + &crate::version::inject_version_header("use lib::base::one;\npub fn two() -> u32 {\n let (_, out): (bool, u32) = jet::add_32(one(), 1);\n out\n}\n"), + ); + ws.create_file( + "workspace/lib/base.simf", + &crate::version::inject_version_header("pub fn one() -> u32 { 1 }\n"), ); - ws.create_file("workspace/lib/base.simf", "pub fn one() -> u32 { 1 }\n"); let dependencies = dependency_map(&root_dir, "lib", &lib_dir); let _err = TemplateProgram::new_with_dep( @@ -1263,7 +1398,7 @@ mod error_tests { let lib_dir = ws.create_dir("workspace/lib"); let main_path = ws.create_file( "workspace/main.simf", - "use lib::missing::Thing;\nfn main() {}\n", + &crate::version::inject_version_header("use lib::missing::Thing;\nfn main() {}\n"), ); let dependencies = dependency_map(&root_dir, "lib", &lib_dir); diff --git a/src/parse.rs b/src/parse.rs index 42b5dac2..0dca4c14 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -43,6 +43,70 @@ impl Program { pub fn items(&self) -> &[Item] { &self.items } + + pub fn check_version + Clone>( + &self, + source: &S, + handler: &mut ErrorCollector, + ) { + let source_file: crate::source::SourceFile = source.clone().into(); + + if let Err((err, span)) = Self::validate_version(&source_file.content()) { + handler.push(RichError::new(err, span).with_source(source_file)); + } + } + + fn validate_version(content: &str) -> Result<(), (Error, Span)> { + let (req_str, span) = Self::extract_version(content)?; + let req = Self::parse_version(req_str, span)?; + Self::check_compatibility(&req, req_str, span) + } + + fn extract_version(content: &str) -> Result<(&str, Span), (Error, Span)> { + match crate::version::extract_version_directive(content) { + Ok(Some(res)) => Ok(res), + Ok(None) => Err(( + Error::MissingSimcVersion { + compiler: crate::version::current_version().to_string(), + }, + Span::new(0, 0), + )), + Err(span) => Err(( + Error::Syntax { + expected: vec!["Exactly one compiler version directive".to_string()], + label: None, + found: Some("Multiple directives".to_string()), + }, + span, + )), + } + } + + fn parse_version( + req_str: &str, + span: Span, + ) -> Result { + crate::version::VersionRequirement::parse(req_str.trim()) + .map_err(|e| (Error::InvalidSimcVersionSyntax { err: e }, span)) + } + + fn check_compatibility( + req: &crate::version::VersionRequirement, + req_str: &str, + span: Span, + ) -> Result<(), (Error, Span)> { + let current_str = crate::version::current_version(); + let current_ver = semver::Version::parse(current_str).unwrap(); + + if !req.matches(¤t_ver) { + let err = req + .classify_mismatch(¤t_ver) + .into_error(req_str.to_string(), current_str.to_string()); + return Err((err, span)); + } + + Ok(()) + } } impl_eq_hash!(Program; items); @@ -1345,7 +1409,7 @@ impl ChumskyParse for Program { .repeated(), ) // map to empty module - .map_with(|_, _| Item::Module); + .to(Item::Module); Item::parser() .recover_with(via_parser(skip_until_next_item)) @@ -1358,6 +1422,25 @@ impl ChumskyParse for Program { } } +fn evaluate_cfg_attribute(cfg_attr: Option<(&str, Span)>) -> Result { + let Some((req_str, span)) = cfg_attr else { + return Ok(true); + }; + + let req = match crate::version::VersionRequirement::parse(req_str) { + Ok(req) => req, + Err(e) => { + return Err(RichError::new( + Error::InvalidSimcVersionSyntax { err: e }, + span, + )); + } + }; + + let current = semver::Version::parse(crate::version::current_version()).unwrap(); + Ok(req.matches(¤t)) +} + impl ChumskyParse for Item { fn parser<'tokens, 'src: 'tokens, I>() -> impl Parser<'tokens, I, Self, ParseError<'src>> + Clone where @@ -1366,9 +1449,40 @@ impl ChumskyParse for Item { let func_parser = Function::parser().map(Item::Function); let type_parser = TypeAlias::parser().map(Item::TypeAlias); let use_parser = UseDecl::parser().map(Item::Use); - let mod_parser = Module::parser().map(|_| Item::Module); + let mod_parser = Module::parser().to(Item::Module); + + let compiler_version = just(Token::Ident("simc")) + .ignore_then(select! { Token::StringLiteral(_) => () }) + .then_ignore(just(Token::Semi)) + .to(Item::Module); + + let cfg_attribute = just(Token::Hash) + .ignore_then(just(Token::LBracket)) + .ignore_then(just(Token::Ident("cfg"))) + .ignore_then(just(Token::LParen)) + .ignore_then(just(Token::Ident("compiler_version"))) + .ignore_then(just(Token::LParen)) + .ignore_then(select! { Token::StringLiteral(s) => s }) + .then_ignore(just(Token::RParen)) + .then_ignore(just(Token::RParen)) + .then_ignore(just(Token::RBracket)) + .map_with(|req_str, e| (req_str, e.span())); + + let conditionally_compiled_items = cfg_attribute + .or_not() + .then(choice((func_parser, type_parser, use_parser, mod_parser))) + .validate(|(cfg_attr, item), _, emit| { + match evaluate_cfg_attribute(cfg_attr) { + Ok(true) => item, + Ok(false) => Item::Module, // Safe fallback: Drop the item entirely from the AST + Err(err) => { + emit.emit(err); + Item::Module + } + } + }); - choice((func_parser, use_parser, type_parser, mod_parser)) + choice((conditionally_compiled_items, compiler_version)) } } diff --git a/src/tracker.rs b/src/tracker.rs index 630dfa1a..2be571a8 100644 --- a/src/tracker.rs +++ b/src/tracker.rs @@ -439,8 +439,10 @@ mod tests { #[test] fn test_debug_and_jet_tracing() { + let program_text = crate::version::inject_version_header(TEST_PROGRAM); let program = - TemplateProgram::new(TEST_PROGRAM, Box::new(ElementsJetHinter::new())).unwrap(); + TemplateProgram::new(program_text.as_str(), Box::new(ElementsJetHinter::new())) + .unwrap(); let program = program.instantiate(Arguments::default(), true).unwrap(); let satisfied = program.satisfy(WitnessValues::default()).unwrap(); @@ -509,8 +511,10 @@ mod tests { fn test_arith_jet_trace_regression() { let env = create_test_env(); + let program_text = crate::version::inject_version_header(TEST_ARITHMETIC_JETS); let program = - TemplateProgram::new(TEST_ARITHMETIC_JETS, Box::new(ElementsJetHinter::new())).unwrap(); + TemplateProgram::new(program_text.as_str(), Box::new(ElementsJetHinter::new())) + .unwrap(); let program = program.instantiate(Arguments::default(), true).unwrap(); let satisfied = program.satisfy(WitnessValues::default()).unwrap(); @@ -564,8 +568,9 @@ mod tests { let env = create_test_env(); + let program_text = crate::version::inject_version_header(TEST_FULL_MULTIPLY_JETS); let program = - TemplateProgram::new(TEST_FULL_MULTIPLY_JETS, Box::new(ElementsJetHinter::new())) + TemplateProgram::new(program_text.as_str(), Box::new(ElementsJetHinter::new())) .unwrap(); let program = program.instantiate(Arguments::default(), true).unwrap(); let satisfied = program.satisfy(WitnessValues::default()).unwrap(); diff --git a/src/witness.rs b/src/witness.rs index 3fd878d4..8fb24efb 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -221,15 +221,17 @@ mod tests { #[test] fn witness_reuse() { - let s = r#"fn main() { + let s = crate::version::inject_version_header( + r#"fn main() { assert!(jet::eq_32(witness::A, witness::A)); -}"#; - let parse_program = parse::Program::parse_from_str(s).expect("parsing works"); +}"#, + ); + let parse_program = parse::Program::parse_from_str(&s).expect("parsing works"); let mut error_collector = ErrorCollector::new(); let driver_program = driver::resolve_order::Program::from_parse( &parse_program, - Arc::from(s), + Arc::from(s.as_str()), &mut error_collector, ) .expect("driver works"); @@ -244,9 +246,11 @@ mod tests { #[test] fn witness_type_mismatch() { - let s = r#"fn main() { + let s = crate::version::inject_version_header( + r#"fn main() { assert!(jet::is_zero_32(witness::A)); -}"#; +}"#, + ); let witness = WitnessValues::from(HashMap::from([( WitnessName::from_str_unchecked("A"), @@ -269,13 +273,15 @@ mod tests { #[test] fn witness_outside_main() { - let s = r#"fn f() -> u32 { + let s = crate::version::inject_version_header( + r#"fn f() -> u32 { witness::OUTPUT_OF_F } fn main() { assert!(jet::is_zero_32(f())); -}"#; +}"#, + ); match CompiledProgram::new( s, From ba17babbf82e30db4e201116fb0570337d261b44 Mon Sep 17 00:00:00 2001 From: Sdoba16 Date: Thu, 4 Jun 2026 17:15:17 +0300 Subject: [PATCH 3/5] Add version tests for multi-file resolution --- src/driver/version_tests.rs | 402 ++++++++++++++++++++++++++++++++++++ 1 file changed, 402 insertions(+) create mode 100644 src/driver/version_tests.rs diff --git a/src/driver/version_tests.rs b/src/driver/version_tests.rs new file mode 100644 index 00000000..816d0313 --- /dev/null +++ b/src/driver/version_tests.rs @@ -0,0 +1,402 @@ +use crate::driver::tests::setup_graph_raw; + +/// Helper macro to generate version compatibility tests. +/// +/// It automatically replaces the `{v}` placeholder with the current compiler version +/// (`env!("CARGO_PKG_VERSION")`), allowing tests to dynamically adapt to version bumps +/// without requiring manual updates. It also reduces the boilerplate required to set up +/// multi-file dependency graphs and assert on compilation success or specific error messages. +macro_rules! version_test { + ($name:ident, $expect_success:expr, $expected_err:expr, $( $path:expr => $content:expr ),+ $(,)?) => { + #[test] + fn $name() { + let v = env!("CARGO_PKG_VERSION"); + let files: Vec<(&str, String)> = vec![ + $( ($path, $content.replace("{v}", v)) ),+ + ]; + + let refs: Vec<(&str, &str)> = files.iter().map(|(p, c)| (*p, c.as_str())).collect(); + let (graph_opt, _, _ws, handler) = setup_graph_raw(refs); + + if $expect_success { + assert!(graph_opt.is_some() && !handler.has_errors(), + "Scenario failed unexpectedly. Errors:\n{}", handler); + return; + } + + assert!(graph_opt.is_none() || handler.has_errors(), + "Scenario succeeded when it should have failed."); + + let expected_err: Option<&str> = $expected_err; + if let Some(err) = expected_err { + assert!(handler.to_string().contains(err), + "Expected error containing '{}' but got:\n{}", err, handler); + } + } + }; +} + +version_test!( + exact_match_all, true, None, + "main.simf" => "simc \"={v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\npub fn foo() {}" +); + +version_test!( + caret_match_all, true, None, + "main.simf" => "simc \"^{v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"^{v}\";\npub fn foo() {}" +); + +version_test!( + tilde_match_all, true, None, + "main.simf" => "simc \"~{v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"~{v}\";\npub fn foo() {}" +); + +version_test!( + range_gt_match_all, true, None, + "main.simf" => "simc \">0.1.0\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \">0.1.0\";\npub fn foo() {}" +); + +version_test!( + range_gte_match_all, true, None, + "main.simf" => "simc \">=0.1.0\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \">=0.1.0\";\npub fn foo() {}" +); + +version_test!( + star_match_all, true, None, + "main.simf" => "simc \"*\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"*\";\npub fn foo() {}" +); + +version_test!( + mixed_valid_operators, true, None, + "main.simf" => "simc \"^{v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\nuse crate::B::foo;\npub fn foo() {}", + "libs/lib/B.simf" => "simc \">0.1.0\";\nuse crate::C::foo;\npub fn foo() {}", + "libs/lib/C.simf" => "simc \"*\";\npub fn foo() {}" +); + +version_test!( + main_exact_lib_float, true, None, + "main.simf" => "simc \"={v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \">=0.1.0\";\npub fn foo() {}" +); + +version_test!( + main_float_lib_exact, true, None, + "main.simf" => "simc \">=0.1.0\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\npub fn foo() {}" +); + +version_test!( + main_too_old_fails, false, Some("Compiler too old"), + "main.simf" => "simc \">99.0.0\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\npub fn foo() {}" +); + +version_test!( + lib_too_old_fails, false, Some("Compiler too old"), + "main.simf" => "simc \"={v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \">99.0.0\";\npub fn foo() {}" +); + +version_test!( + main_contract_too_old_fails, false, Some("Contract too old"), + "main.simf" => "simc \"<0.0.1\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\npub fn foo() {}" +); + +version_test!( + lib_contract_too_old_fails, false, Some("Contract too old"), + "main.simf" => "simc \"={v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"<0.0.1\";\npub fn foo() {}" +); + +version_test!( + main_exact_mismatch_fails, false, Some("Exact version mismatch"), + "main.simf" => "simc \"=0.0.1\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\npub fn foo() {}" +); + +version_test!( + lib_exact_mismatch_fails, false, Some("Exact version mismatch"), + "main.simf" => "simc \"={v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"=0.0.1\";\npub fn foo() {}" +); + +version_test!( + deep_chain_success, true, None, + "main.simf" => "simc \">0.1.0\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \">0.1.0\";\nuse crate::B::foo;\npub fn foo() {}", + "libs/lib/B.simf" => "simc \">0.1.0\";\nuse crate::C::foo;\npub fn foo() {}", + "libs/lib/C.simf" => "simc \">0.1.0\";\nuse crate::D::foo;\npub fn foo() {}", + "libs/lib/D.simf" => "simc \">0.1.0\";\npub fn foo() {}" +); + +version_test!( + deep_chain_fail_at_leaf, false, Some("Compiler too old"), + "main.simf" => "simc \">0.1.0\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \">0.1.0\";\nuse crate::B::foo;\npub fn foo() {}", + "libs/lib/B.simf" => "simc \">0.1.0\";\nuse crate::C::foo;\npub fn foo() {}", + "libs/lib/C.simf" => "simc \">0.1.0\";\nuse crate::D::foo;\npub fn foo() {}", + "libs/lib/D.simf" => "simc \">99.0.0\";\npub fn foo() {}" +); + +version_test!( + diamond_dependency_success, true, None, + "main.simf" => "simc \"={v}\";\nuse lib::A::foo; use lib::B::bar;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\nuse crate::C::baz;\npub fn foo() {}", + "libs/lib/B.simf" => "simc \"={v}\";\nuse crate::C::baz;\npub fn bar() {}", + "libs/lib/C.simf" => "simc \"={v}\";\npub fn baz() {}" +); + +version_test!( + diamond_dependency_fail_at_c, false, Some("Contract too old"), + "main.simf" => "simc \"={v}\";\nuse lib::A::foo; use lib::B::bar;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\nuse crate::C::baz;\npub fn foo() {}", + "libs/lib/B.simf" => "simc \"={v}\";\nuse crate::C::baz;\npub fn bar() {}", + "libs/lib/C.simf" => "simc \"<0.0.1\";\npub fn baz() {}" +); + +version_test!( + crate_import_success, true, None, + "main.simf" => "simc \"={v}\";\nuse crate::Utils::helper;\nfn main() {}", + "Utils.simf" => "simc \"={v}\";\npub fn helper() {}" +); + +version_test!( + crate_import_fail, false, Some("Exact version mismatch"), + "main.simf" => "simc \"={v}\";\nuse crate::Utils::helper;\nfn main() {}", + "Utils.simf" => "simc \"=0.0.1\";\npub fn helper() {}" +); + +version_test!( + lib_crate_import_success, true, None, + "main.simf" => "simc \"={v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\nuse crate::B::foo;\npub fn foo() {}", + "libs/lib/B.simf" => "simc \"={v}\";\npub fn foo() {}" +); + +version_test!( + lib_crate_import_fail, false, Some("Compiler too old"), + "main.simf" => "simc \"={v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\nuse crate::B::foo;\npub fn foo() {}", + "libs/lib/B.simf" => "simc \">99.0.0\";\npub fn foo() {}" +); + +version_test!( + invalid_syntax_main, false, Some("Invalid version syntax"), + "main.simf" => "simc \"foo\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\npub fn foo() {}" +); + +version_test!( + invalid_syntax_lib, false, Some("Invalid version syntax"), + "main.simf" => "simc \"={v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"bar\";\npub fn foo() {}" +); + +version_test!( + multiple_ranges_success, true, None, + "main.simf" => "simc \">=0.1.0, <99.0.0\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\npub fn foo() {}" +); + +version_test!( + multiple_ranges_fail, false, Some("too old"), + "main.simf" => "simc \">=0.1.0, <0.2.0\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\npub fn foo() {}" +); + +version_test!( + whitespace_in_version, true, None, + "main.simf" => "simc \" >= 0.1.0 \";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\npub fn foo() {}" +); + +version_test!( + caret_zero_minor_fail, false, Some("Contract too old"), + "main.simf" => "simc \"^0.0.1\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\npub fn foo() {}" +); + +version_test!( + large_project_mix_success, true, None, + "main.simf" => "simc \">0.1.0\";\nuse lib::A::a;\nuse crate::B::b;\nfn main() {}", + "B.simf" => "simc \"={v}\";\npub fn b() {}", + "libs/lib/A.simf" => "simc \"*\";\nuse crate::C::c;\npub fn a() {}", + "libs/lib/C.simf" => "simc \"~{v}\";\nuse crate::D::d;\npub fn c() {}", + "libs/lib/D.simf" => "simc \">0.0.0\";\npub fn d() {}" +); + +version_test!( + mixed_dependent_ranges_success, true, None, + "main.simf" => "simc \"={v}\";\nuse lib::A::foo; use lib::B::bar;\nfn main() {}", + "libs/lib/A.simf" => "simc \"<99.0.0\";\npub fn foo() {}", + "libs/lib/B.simf" => "simc \">0.1.0\";\npub fn bar() {}" +); + +version_test!( + mixed_dependent_ranges_fail_less_than_v, false, Some("Contract too old"), + "main.simf" => "simc \"={v}\";\nuse lib::A::foo; use lib::B::bar;\nfn main() {}", + "libs/lib/A.simf" => "simc \"<{v}\";\npub fn foo() {}", + "libs/lib/B.simf" => "simc \">0.1.0\";\npub fn bar() {}" +); + +version_test!( + mixed_dependent_ranges_fail_greater_than_v, false, Some("Compiler too old"), + "main.simf" => "simc \"={v}\";\nuse lib::A::foo; use lib::B::bar;\nfn main() {}", + "libs/lib/A.simf" => "simc \"<99.0.0\";\npub fn foo() {}", + "libs/lib/B.simf" => "simc \">{v}\";\npub fn bar() {}" +); + +version_test!( + mixed_dependent_ranges_both_fail, false, Some("too old"), + "main.simf" => "simc \"={v}\";\nuse lib::A::foo; use lib::B::bar;\nfn main() {}", + "libs/lib/A.simf" => "simc \"<{v}\";\npub fn foo() {}", + "libs/lib/B.simf" => "simc \">{v}\";\npub fn bar() {}" +); + +version_test!( + chained_mixed_dependent_ranges_success, true, None, + "main.simf" => "simc \"={v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"<99.0.0\";\nuse crate::B::foo;\npub fn foo() {}", + "libs/lib/B.simf" => "simc \">0.1.0\";\npub fn foo() {}" +); + +version_test!( + chained_mixed_dependent_ranges_fail, false, Some("Compiler too old"), + "main.simf" => "simc \"={v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"<99.0.0\";\nuse crate::B::foo;\npub fn foo() {}", + "libs/lib/B.simf" => "simc \">{v}\";\npub fn foo() {}" +); + +version_test!( + logical_or_unsupported_syntax, false, Some("Invalid version syntax"), + "main.simf" => "simc \"<0.1.0 || ={v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\npub fn foo() {}" +); + +version_test!( + prerelease_tag_mismatch_across_bases, false, Some("Contract too old"), + "main.simf" => "simc \">=0.1.0-alpha.1\";\nfn main() {}" +); + +version_test!( + missing_version_attribute, false, Some("Missing compiler version"), + "main.simf" => "// NO_INJECT\nfn main() {}" +); + +version_test!( + build_metadata_success, true, None, + "main.simf" => "simc \">=0.1.0+build.2023\";\nfn main() {}" +); + +version_test!( + empty_version_string, false, Some("Invalid version syntax"), + "main.simf" => "simc \"\";\nfn main() {}" +); + +version_test!( + malformed_attribute_no_quotes, false, Some("Expected"), + "main.simf" => "simc ={v};\nfn main() {}" +); + +version_test!( + malformed_attribute_wrong_name, false, Some("Expected"), + "main.simf" => "compiler_version \"={v}\";\nfn main() {}" +); + +version_test!( + circular_dependency_version_success, true, None, + "main.simf" => "simc \"={v}\";\nuse lib::A::a;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\nuse crate::B::b;\npub fn a() {}", + "libs/lib/B.simf" => "simc \"={v}\";\nuse crate::A::a;\npub fn b() {}" +); + +version_test!( + multiple_directives_same_file_fails, false, Some("Expected"), + "main.simf" => "simc \"={v}\";\nsimc \"={v}\";\nfn main() {}" +); + +version_test!( + caret_zero_minor_fails_on_newer_compiler, false, Some("Contract too old"), + "main.simf" => "simc \"^0.1.2\";\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\npub fn foo() {}" +); + +version_test!( + invalid_leading_zeros_fails, false, Some("Invalid version syntax"), + "main.simf" => "simc \"=01.0.0\";\nfn main() {}" +); + +version_test!( + invalid_empty_prerelease_segment_fails, false, Some("Invalid version syntax"), + "main.simf" => "simc \"=1.0.0-alpha..1\";\nfn main() {}" +); + +version_test!( + invalid_characters_in_version_fails, false, Some("Invalid version syntax"), + "main.simf" => "simc \"=1.0.0-alpha!+build\";\nfn main() {}" +); + +version_test!( + wildcard_x_minor_patch, true, None, + "main.simf" => "simc \"0.x.x\";\nfn main() {}" +); + +version_test!( + wildcard_x_major, true, None, + "main.simf" => "simc \"X\";\nfn main() {}" +); + +version_test!( + heavy_whitespace_and_tabs_success, true, None, + "main.simf" => "simc \" >= 0.0.1 , < 99.0.0 \";\nfn main() {}" +); + +version_test!( + unreferenced_file_with_invalid_version_ignored, true, None, + "main.simf" => "simc \"={v}\";\nuse lib::A::foo;\nfn main() {}", + "libs/lib/A.simf" => "simc \"={v}\";\npub fn foo() {}", + "libs/lib/B.simf" => "simc \">99.0.0\";\npub fn unused() {}" +); + +version_test!( + incompatible_fallback, false, Some("Compiler too old"), + "main.simf" => "simc \"~99\";\nfn main() {}" +); + +version_test!( + bare_version_exact_mismatch_fallback, false, Some("Exact version mismatch"), + "main.simf" => "simc \"0.0.1\";\nfn main() {}" +); + +version_test!( + missing_version_with_comments, false, Some("Missing compiler version"), + "main.simf" => "// NO_INJECT\n/* Some initial block comment */\nfn main() {}" +); + +version_test!( + missing_version_type_alias_first, false, Some("Missing compiler version"), + "main.simf" => "// NO_INJECT\npub type MyType = u32;\nfn main() {}" +); + +version_test!( + missing_version_empty_file, false, Some("Missing compiler version"), + "main.simf" => "// NO_INJECT" +); + +version_test!( + version_in_comment_ignored, true, None, + "main.simf" => "// simc \"=99.0.0\";\nsimc \"={v}\";\nfn main() {}" +); + +version_test!( + version_in_block_comment_ignored, true, None, + "main.simf" => "/*\nsimc \"=99.0.0\";\n*/\nsimc \"={v}\";\nfn main() {}" +); From 386e423502edc706489b2b9dfdf6910f910b31cc Mon Sep 17 00:00:00 2001 From: Sdoba16 Date: Thu, 4 Jun 2026 17:22:08 +0300 Subject: [PATCH 4/5] Update examples with versions --- examples/array_fold.simf | 2 ++ examples/array_fold_2n.simf | 4 ++++ examples/cat.simf | 2 ++ examples/ctv.simf | 4 ++++ examples/escrow_with_delay.simf | 4 ++++ examples/hash_loop.simf | 2 ++ examples/hodl_vault.simf | 4 ++++ examples/htlc.simf | 4 ++++ examples/last_will.simf | 4 ++++ examples/local_crate/main.simf | 2 ++ examples/local_crate/math.simf | 2 ++ examples/multiple_deps/main.simf | 2 ++ examples/multiple_deps/math/simple_op.simf | 2 ++ examples/multiple_deps/merkle/build_root.simf | 2 ++ examples/non_interactive_fee_bump.simf | 3 +++ examples/p2ms.simf | 4 ++++ examples/p2pk.simf | 4 ++++ examples/p2pkh.simf | 4 ++++ examples/pattern_matching.simf | 2 ++ examples/presigned_vault.simf | 4 ++++ examples/reveal_collision.simf | 4 ++++ examples/reveal_fix_point.simf | 4 ++++ examples/sighash_all_anyonecanpay.simf | 4 ++++ examples/sighash_all_anyprevout.simf | 4 ++++ examples/sighash_all_anyprevoutanyscript.simf | 4 ++++ examples/sighash_none.simf | 4 ++++ examples/sighash_single.simf | 4 ++++ examples/simple_multidep/crypto/hashes.simf | 2 ++ examples/simple_multidep/main.simf | 2 ++ examples/simple_multidep/math/arithmetic.simf | 2 ++ examples/single_dep/main.simf | 2 ++ examples/single_dep/temp/constants/utils.simf | 2 ++ examples/single_dep/temp/funcs.simf | 2 ++ examples/transfer_with_timeout.simf | 4 ++++ .../error-test-cases/crate-file-not-found/main.simf | 2 ++ .../error-test-cases/cyclic-dependency/lib/module_a.simf | 2 ++ .../error-test-cases/cyclic-dependency/lib/module_b.simf | 2 ++ functional-tests/error-test-cases/cyclic-dependency/main.simf | 2 ++ functional-tests/error-test-cases/file-not-found/main.simf | 2 ++ functional-tests/error-test-cases/lib-not-found/main.simf | 2 ++ .../error-test-cases/local-file-as-external/main.simf | 2 ++ .../error-test-cases/local-file-as-external/utils.simf | 2 ++ .../error-test-cases/name-collision/lib/groups.simf | 2 ++ .../error-test-cases/name-collision/lib/math.simf | 2 ++ functional-tests/error-test-cases/name-collision/main.simf | 2 ++ .../error-test-cases/private-visibility/lib/hidden.simf | 2 ++ .../error-test-cases/private-visibility/main.simf | 2 ++ .../type-alias-duplication/lib/duplication.simf | 2 ++ .../error-test-cases/type-alias-duplication/main.simf | 2 ++ .../valid-test-cases/deep-reexport-chain/lib/level1.simf | 2 ++ .../valid-test-cases/deep-reexport-chain/lib/level2.simf | 2 ++ .../valid-test-cases/deep-reexport-chain/lib/level3.simf | 2 ++ .../valid-test-cases/deep-reexport-chain/main.simf | 2 ++ .../diamond-dependency-resolution/lib/base.simf | 2 ++ .../diamond-dependency-resolution/lib/left.simf | 2 ++ .../diamond-dependency-resolution/lib/right.simf | 2 ++ .../valid-test-cases/diamond-dependency-resolution/main.simf | 2 ++ .../external-library-uses-crate/ext_lib/internal.simf | 2 ++ .../external-library-uses-crate/ext_lib/math.simf | 2 ++ .../valid-test-cases/external-library-uses-crate/main.simf | 2 ++ .../valid-test-cases/interleaved-waterfall/auth/verify.simf | 2 ++ .../valid-test-cases/interleaved-waterfall/db/store.simf | 2 ++ .../valid-test-cases/interleaved-waterfall/main.simf | 2 ++ .../valid-test-cases/interleaved-waterfall/orch/handler.simf | 2 ++ .../valid-test-cases/interleaved-waterfall/types/def.simf | 2 ++ .../valid-test-cases/leaky-signature/lib/internal.simf | 2 ++ functional-tests/valid-test-cases/leaky-signature/main.simf | 2 ++ functional-tests/valid-test-cases/local-crate-nested/a/b.simf | 2 ++ .../valid-test-cases/local-crate-nested/main.simf | 2 ++ functional-tests/valid-test-cases/local-crate/main.simf | 2 ++ functional-tests/valid-test-cases/local-crate/utils.simf | 2 ++ .../valid-test-cases/module-simple/lib/module.simf | 2 ++ functional-tests/valid-test-cases/module-simple/main.simf | 2 ++ .../valid-test-cases/multi-lib-facade/api/api.simf | 2 ++ .../valid-test-cases/multi-lib-facade/crypto/crypto.simf | 2 ++ functional-tests/valid-test-cases/multi-lib-facade/main.simf | 2 ++ .../valid-test-cases/multi-lib-facade/math/math.simf | 2 ++ .../valid-test-cases/reexport-diamond/lib/core.simf | 2 ++ .../valid-test-cases/reexport-diamond/lib/route_a.simf | 2 ++ .../valid-test-cases/reexport-diamond/lib/route_b.simf | 2 ++ functional-tests/valid-test-cases/reexport-diamond/main.simf | 2 ++ 81 files changed, 199 insertions(+) diff --git a/examples/array_fold.simf b/examples/array_fold.simf index e671493c..f571a0bf 100644 --- a/examples/array_fold.simf +++ b/examples/array_fold.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + fn sum(elt: u32, acc: u32) -> u32 { let (_, acc): (bool, u32) = jet::add_32(elt, acc); acc diff --git a/examples/array_fold_2n.simf b/examples/array_fold_2n.simf index 9d86325c..9dad67c7 100644 --- a/examples/array_fold_2n.simf +++ b/examples/array_fold_2n.simf @@ -1,5 +1,9 @@ +simc ">=0.6.0"; + // From https://github.com/BlockstreamResearch/SimplicityHL/issues/153 + + fn sum(elt: u32, acc: u32) -> u32 { let (_, acc): (bool, u32) = jet::add_32(elt, acc); acc diff --git a/examples/cat.simf b/examples/cat.simf index f6c5e76c..a5d03267 100644 --- a/examples/cat.simf +++ b/examples/cat.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + fn main() { let ab: u16 = <(u8, u8)>::into((0x10, 0x01)); let c: u16 = 0x1001; diff --git a/examples/ctv.simf b/examples/ctv.simf index 3eadb44c..d1452be2 100644 --- a/examples/ctv.simf +++ b/examples/ctv.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + /* * This program is an emulation of CTV using simplicity * @@ -5,6 +7,8 @@ * we require the user to specify all the components of the sighash * that they want to commit. */ + + fn main() { let ctx: Ctx8 = jet::sha_256_ctx_8_init(); let ctx: Ctx8 = jet::sha_256_ctx_8_add_4(ctx, jet::version()); diff --git a/examples/escrow_with_delay.simf b/examples/escrow_with_delay.simf index 8a11a54a..99bd2bd9 100644 --- a/examples/escrow_with_delay.simf +++ b/examples/escrow_with_delay.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + /* * ESCROW WITH DELAY * @@ -7,6 +9,8 @@ * * https://docs.ivylang.org/bitcoin/language/ExampleContracts.html#escrowwithdelay */ + + fn not(bit: bool) -> bool { ::into(jet::complement_1(::into(bit))) } diff --git a/examples/hash_loop.simf b/examples/hash_loop.simf index f554d894..82ad7086 100644 --- a/examples/hash_loop.simf +++ b/examples/hash_loop.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + // Add counter to streaming hash and finalize when the loop exists fn hash_counter_8(ctx: Ctx8, unused: (), byte: u8) -> Either { let new_ctx: Ctx8 = jet::sha_256_ctx_8_add_1(ctx, byte); diff --git a/examples/hodl_vault.simf b/examples/hodl_vault.simf index fdc29ae5..15988097 100644 --- a/examples/hodl_vault.simf +++ b/examples/hodl_vault.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + /* * HODL VAULT * @@ -8,6 +10,8 @@ * the use of old data. The transaction is timelocked to the oracle height, * which means that the transaction becomes valid after the oracle height. */ + + fn checksig(pk: Pubkey, sig: Signature) { let msg: u256 = jet::sig_all_hash(); jet::bip_0340_verify((pk, msg), sig); diff --git a/examples/htlc.simf b/examples/htlc.simf index 2ee98c5e..fa467b01 100644 --- a/examples/htlc.simf +++ b/examples/htlc.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + /* * HTLC (Hash Time-Locked Contract) * @@ -9,6 +11,8 @@ * * https://docs.ivylang.org/bitcoin/language/ExampleContracts.html#htlc */ + + fn sha2(string: u256) -> u256 { let hasher: Ctx8 = jet::sha_256_ctx_8_init(); let hasher: Ctx8 = jet::sha_256_ctx_8_add_32(hasher, string); diff --git a/examples/last_will.simf b/examples/last_will.simf index 9790a1cf..a32e32ba 100644 --- a/examples/last_will.simf +++ b/examples/last_will.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + /* * LAST WILL * @@ -5,6 +7,8 @@ * days. The owner has to repeat the covenant when he moves the coins with his * hot key. The owner can break out of the covenant with his cold key. */ + + fn checksig(pk: Pubkey, sig: Signature) { let msg: u256 = jet::sig_all_hash(); jet::bip_0340_verify((pk, msg), sig); diff --git a/examples/local_crate/main.simf b/examples/local_crate/main.simf index c4df525f..e4f2ba79 100644 --- a/examples/local_crate/main.simf +++ b/examples/local_crate/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use crate::math::add; fn main() { diff --git a/examples/local_crate/math.simf b/examples/local_crate/math.simf index 25076863..f5763b04 100644 --- a/examples/local_crate/math.simf +++ b/examples/local_crate/math.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub fn add(a: u32, b: u32) -> u32 { let (_, sum): (bool, u32) = jet::add_32(a, b); sum diff --git a/examples/multiple_deps/main.simf b/examples/multiple_deps/main.simf index cead0852..d78ff150 100644 --- a/examples/multiple_deps/main.simf +++ b/examples/multiple_deps/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use merkle::build_root::{get_root, hash as and_hash}; use base_math::simple_op::hash as or_hash; diff --git a/examples/multiple_deps/math/simple_op.simf b/examples/multiple_deps/math/simple_op.simf index b152a361..198d9013 100644 --- a/examples/multiple_deps/math/simple_op.simf +++ b/examples/multiple_deps/math/simple_op.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub fn hash(x: u32, y: u32) -> u32 { jet::xor_32(x, y) } \ No newline at end of file diff --git a/examples/multiple_deps/merkle/build_root.simf b/examples/multiple_deps/merkle/build_root.simf index f41c37e4..8e2b7bd9 100644 --- a/examples/multiple_deps/merkle/build_root.simf +++ b/examples/multiple_deps/merkle/build_root.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use math::simple_op::hash as temp_hash; pub fn get_root(tx1: u32, tx2: u32) -> u32 { diff --git a/examples/non_interactive_fee_bump.simf b/examples/non_interactive_fee_bump.simf index 5188633c..617bdb75 100644 --- a/examples/non_interactive_fee_bump.simf +++ b/examples/non_interactive_fee_bump.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + /* * NON-INTERACTIVE FEE BUMPING * @@ -12,6 +14,7 @@ * sponsors, Child-Pays-For-Parent (CPFP), or anchor outputs, simplifying fee management for transaction inclusion. */ + // This function computes a signature hash for transactions that allows non-interactive fee bumping. // It omits certain fields from the transaction that can be modified by anyone, // specifically nLockTime and change/fee outputs amounts. diff --git a/examples/p2ms.simf b/examples/p2ms.simf index d95c1a0e..3ba9e07e 100644 --- a/examples/p2ms.simf +++ b/examples/p2ms.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + /* * PAY TO MULTISIG * @@ -6,6 +8,8 @@ * * https://docs.ivylang.org/bitcoin/language/ExampleContracts.html#lockwithmultisig */ + + fn not(bit: bool) -> bool { ::into(jet::complement_1(::into(bit))) } diff --git a/examples/p2pk.simf b/examples/p2pk.simf index 7e40dd55..5666fb83 100644 --- a/examples/p2pk.simf +++ b/examples/p2pk.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + /* * PAY TO PUBLIC KEY * @@ -5,6 +7,8 @@ * * https://docs.ivylang.org/bitcoin/language/ExampleContracts.html#lockwithpublickey */ + + fn main() { jet::bip_0340_verify((param::ALICE_PUBLIC_KEY, jet::sig_all_hash()), witness::ALICE_SIGNATURE) } diff --git a/examples/p2pkh.simf b/examples/p2pkh.simf index 42f527a9..2eaa6142 100644 --- a/examples/p2pkh.simf +++ b/examples/p2pkh.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + /* * PAY TO PUBLIC KEY HASH * @@ -6,6 +8,8 @@ * * https://docs.ivylang.org/bitcoin/language/ExampleContracts.html#lockwithpublickeyhash */ + + fn sha2(string: u256) -> u256 { let hasher: Ctx8 = jet::sha_256_ctx_8_init(); let hasher: Ctx8 = jet::sha_256_ctx_8_add_32(hasher, string); diff --git a/examples/pattern_matching.simf b/examples/pattern_matching.simf index 706996fa..e57ba53a 100644 --- a/examples/pattern_matching.simf +++ b/examples/pattern_matching.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + fn main() { let complex_pattern: Either<(u32, u32, (u1, u1)), [u1; 8]> = Left((32, 3, (0, 1))); diff --git a/examples/presigned_vault.simf b/examples/presigned_vault.simf index d1a836e4..f179d7a5 100644 --- a/examples/presigned_vault.simf +++ b/examples/presigned_vault.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + /* * PRESIGNED VAULT * @@ -16,6 +18,8 @@ * * https://docs.ivylang.org/bitcoin/language/ExampleContracts.html#vaultspend */ + + fn checksig(pk: Pubkey, sig: Signature) { let msg: u256 = jet::sig_all_hash(); jet::bip_0340_verify((pk, msg), sig); diff --git a/examples/reveal_collision.simf b/examples/reveal_collision.simf index bec8b1f3..ff5fed67 100644 --- a/examples/reveal_collision.simf +++ b/examples/reveal_collision.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + /* * REVEAL COLLISION * @@ -8,6 +10,8 @@ * * https://docs.ivylang.org/bitcoin/language/ExampleContracts.html#revealcollision */ + + fn not(bit: bool) -> bool { ::into(jet::complement_1(::into(bit))) } diff --git a/examples/reveal_fix_point.simf b/examples/reveal_fix_point.simf index 02f5da8d..9a7256b2 100644 --- a/examples/reveal_fix_point.simf +++ b/examples/reveal_fix_point.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + /* * REVEAL FIX POINT * @@ -8,6 +10,8 @@ * * https://docs.ivylang.org/bitcoin/language/ExampleContracts.html#revealfixedpoint */ + + fn sha2(string: u256) -> u256 { let hasher: Ctx8 = jet::sha_256_ctx_8_init(); let hasher: Ctx8 = jet::sha_256_ctx_8_add_32(hasher, string); diff --git a/examples/sighash_all_anyonecanpay.simf b/examples/sighash_all_anyonecanpay.simf index ed1f6e2f..3827a45b 100644 --- a/examples/sighash_all_anyonecanpay.simf +++ b/examples/sighash_all_anyonecanpay.simf @@ -1,7 +1,11 @@ +simc ">=0.6.0"; + /* * This program verifies a Schnorr signature based on * SIGHASH_ALL | SIGHASH_ANYONECANPAY. */ + + fn main() { let ctx: Ctx8 = jet::sha_256_ctx_8_init(); // Blockchain diff --git a/examples/sighash_all_anyprevout.simf b/examples/sighash_all_anyprevout.simf index e5448792..f2124577 100644 --- a/examples/sighash_all_anyprevout.simf +++ b/examples/sighash_all_anyprevout.simf @@ -1,7 +1,11 @@ +simc ">=0.6.0"; + /* * This program verifies a Schnorr signature based on * SIGHASH_ALL | SIGHASH_ANYPREVOUT. */ + + fn main() { let ctx: Ctx8 = jet::sha_256_ctx_8_init(); // Blockchain diff --git a/examples/sighash_all_anyprevoutanyscript.simf b/examples/sighash_all_anyprevoutanyscript.simf index fb7ab0a7..f9075ed9 100644 --- a/examples/sighash_all_anyprevoutanyscript.simf +++ b/examples/sighash_all_anyprevoutanyscript.simf @@ -1,7 +1,11 @@ +simc ">=0.6.0"; + /* * This program verifies a Schnorr signature based on * SIGHASH_ALL | SIGHASH_ANYPREVOUTANYSCRIPT. */ + + fn main() { let ctx: Ctx8 = jet::sha_256_ctx_8_init(); // Blockchain diff --git a/examples/sighash_none.simf b/examples/sighash_none.simf index 3ae78c45..1e3e5a3b 100644 --- a/examples/sighash_none.simf +++ b/examples/sighash_none.simf @@ -1,7 +1,11 @@ +simc ">=0.6.0"; + /* * This program verifies a Schnorr signature based on * SIGHASH_NONE. */ + + fn main() { let ctx: Ctx8 = jet::sha_256_ctx_8_init(); // Blockchain diff --git a/examples/sighash_single.simf b/examples/sighash_single.simf index db06aa0a..fe4a4516 100644 --- a/examples/sighash_single.simf +++ b/examples/sighash_single.simf @@ -1,7 +1,11 @@ +simc ">=0.6.0"; + /* * This program verifies a Schnorr signature based on * SIGHASH_SINGLE. */ + + fn main() { let ctx: Ctx8 = jet::sha_256_ctx_8_init(); // Blockchain diff --git a/examples/simple_multidep/crypto/hashes.simf b/examples/simple_multidep/crypto/hashes.simf index 0e463d89..45ae1f3a 100644 --- a/examples/simple_multidep/crypto/hashes.simf +++ b/examples/simple_multidep/crypto/hashes.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub fn sha256(data: u32) -> u256 { let ctx: Ctx8 = jet::sha_256_ctx_8_init(); let ctx: Ctx8 = jet::sha_256_ctx_8_add_4(ctx, data); diff --git a/examples/simple_multidep/main.simf b/examples/simple_multidep/main.simf index 81031d4c..12537bd2 100644 --- a/examples/simple_multidep/main.simf +++ b/examples/simple_multidep/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use math::arithmetic::add; use crypto::hashes::sha256; diff --git a/examples/simple_multidep/math/arithmetic.simf b/examples/simple_multidep/math/arithmetic.simf index 2f348e0c..30266a5a 100644 --- a/examples/simple_multidep/math/arithmetic.simf +++ b/examples/simple_multidep/math/arithmetic.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub fn add(a: u32, b: u32) -> u32 { let (_, res): (bool, u32) = jet::add_32(a, b); res diff --git a/examples/single_dep/main.simf b/examples/single_dep/main.simf index 7897ab8a..7ce457d6 100644 --- a/examples/single_dep/main.simf +++ b/examples/single_dep/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub use temp::constants::utils::two as smth; use temp::funcs::{get_five, Smth}; diff --git a/examples/single_dep/temp/constants/utils.simf b/examples/single_dep/temp/constants/utils.simf index 4fd2102e..12aa2c09 100644 --- a/examples/single_dep/temp/constants/utils.simf +++ b/examples/single_dep/temp/constants/utils.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub use crate::funcs::Smth; pub fn two() -> Smth { diff --git a/examples/single_dep/temp/funcs.simf b/examples/single_dep/temp/funcs.simf index 0ff2da55..f2af8f8c 100644 --- a/examples/single_dep/temp/funcs.simf +++ b/examples/single_dep/temp/funcs.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub type Smth = u32; pub fn get_five() -> u32 { diff --git a/examples/transfer_with_timeout.simf b/examples/transfer_with_timeout.simf index 91f6f69e..4c998720 100644 --- a/examples/transfer_with_timeout.simf +++ b/examples/transfer_with_timeout.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + /* * TRANSFER WITH TIMEOUT * @@ -12,6 +14,8 @@ * * https://docs.ivylang.org/bitcoin/language/ExampleContracts.html#transferwithtimeout */ + + fn checksig(pk: Pubkey, sig: Signature) { let msg: u256 = jet::sig_all_hash(); jet::bip_0340_verify((pk, msg), sig); diff --git a/functional-tests/error-test-cases/crate-file-not-found/main.simf b/functional-tests/error-test-cases/crate-file-not-found/main.simf index c07ebfbf..c780ebfa 100644 --- a/functional-tests/error-test-cases/crate-file-not-found/main.simf +++ b/functional-tests/error-test-cases/crate-file-not-found/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use crate::missing::foo; fn main() {} diff --git a/functional-tests/error-test-cases/cyclic-dependency/lib/module_a.simf b/functional-tests/error-test-cases/cyclic-dependency/lib/module_a.simf index 4d32267e..477f627d 100644 --- a/functional-tests/error-test-cases/cyclic-dependency/lib/module_a.simf +++ b/functional-tests/error-test-cases/cyclic-dependency/lib/module_a.simf @@ -1,2 +1,4 @@ +simc ">=0.6.0"; + pub use crate::module_b::TypeB; pub type TypeA = u32; diff --git a/functional-tests/error-test-cases/cyclic-dependency/lib/module_b.simf b/functional-tests/error-test-cases/cyclic-dependency/lib/module_b.simf index f51190c1..c33138fd 100644 --- a/functional-tests/error-test-cases/cyclic-dependency/lib/module_b.simf +++ b/functional-tests/error-test-cases/cyclic-dependency/lib/module_b.simf @@ -1,2 +1,4 @@ +simc ">=0.6.0"; + pub use crate::module_a::TypeA; pub type TypeB = u32; diff --git a/functional-tests/error-test-cases/cyclic-dependency/main.simf b/functional-tests/error-test-cases/cyclic-dependency/main.simf index 338a4030..49e4a75c 100644 --- a/functional-tests/error-test-cases/cyclic-dependency/main.simf +++ b/functional-tests/error-test-cases/cyclic-dependency/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use lib::module_a::TypeA; fn main() {} diff --git a/functional-tests/error-test-cases/file-not-found/main.simf b/functional-tests/error-test-cases/file-not-found/main.simf index 2d016d20..963a32aa 100644 --- a/functional-tests/error-test-cases/file-not-found/main.simf +++ b/functional-tests/error-test-cases/file-not-found/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use lib::module::AssetId; fn main() { diff --git a/functional-tests/error-test-cases/lib-not-found/main.simf b/functional-tests/error-test-cases/lib-not-found/main.simf index 2d016d20..963a32aa 100644 --- a/functional-tests/error-test-cases/lib-not-found/main.simf +++ b/functional-tests/error-test-cases/lib-not-found/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use lib::module::AssetId; fn main() { diff --git a/functional-tests/error-test-cases/local-file-as-external/main.simf b/functional-tests/error-test-cases/local-file-as-external/main.simf index a8b7e844..a69525f4 100644 --- a/functional-tests/error-test-cases/local-file-as-external/main.simf +++ b/functional-tests/error-test-cases/local-file-as-external/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use ext::utils::helper; fn main() { diff --git a/functional-tests/error-test-cases/local-file-as-external/utils.simf b/functional-tests/error-test-cases/local-file-as-external/utils.simf index dfaf506e..51420042 100644 --- a/functional-tests/error-test-cases/local-file-as-external/utils.simf +++ b/functional-tests/error-test-cases/local-file-as-external/utils.simf @@ -1 +1,3 @@ +simc ">=0.6.0"; + pub fn helper() {} diff --git a/functional-tests/error-test-cases/name-collision/lib/groups.simf b/functional-tests/error-test-cases/name-collision/lib/groups.simf index 7ef2eff1..dac4ddec 100644 --- a/functional-tests/error-test-cases/name-collision/lib/groups.simf +++ b/functional-tests/error-test-cases/name-collision/lib/groups.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub fn add(a: u32, b: u32) -> (bool, u32) { jet::add_32(a, b) } \ No newline at end of file diff --git a/functional-tests/error-test-cases/name-collision/lib/math.simf b/functional-tests/error-test-cases/name-collision/lib/math.simf index b9a83fd9..77cd253f 100644 --- a/functional-tests/error-test-cases/name-collision/lib/math.simf +++ b/functional-tests/error-test-cases/name-collision/lib/math.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub fn add(a: u32, b: u32) -> (bool, u32) { let (_, c): (bool, u32) = jet::add_32(a, b); jet::add_32(c, 1) diff --git a/functional-tests/error-test-cases/name-collision/main.simf b/functional-tests/error-test-cases/name-collision/main.simf index 59b5c813..f22c77a2 100644 --- a/functional-tests/error-test-cases/name-collision/main.simf +++ b/functional-tests/error-test-cases/name-collision/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use lib::groups::add; use lib::math::add; diff --git a/functional-tests/error-test-cases/private-visibility/lib/hidden.simf b/functional-tests/error-test-cases/private-visibility/lib/hidden.simf index 70da82d4..351f1dd7 100644 --- a/functional-tests/error-test-cases/private-visibility/lib/hidden.simf +++ b/functional-tests/error-test-cases/private-visibility/lib/hidden.simf @@ -1 +1,3 @@ +simc ">=0.6.0"; + type SecretType = u32; pub fn ok() {} \ No newline at end of file diff --git a/functional-tests/error-test-cases/private-visibility/main.simf b/functional-tests/error-test-cases/private-visibility/main.simf index e1eb9dd8..e284a5a1 100644 --- a/functional-tests/error-test-cases/private-visibility/main.simf +++ b/functional-tests/error-test-cases/private-visibility/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use lib::hidden::SecretType; fn main() {} \ No newline at end of file diff --git a/functional-tests/error-test-cases/type-alias-duplication/lib/duplication.simf b/functional-tests/error-test-cases/type-alias-duplication/lib/duplication.simf index c668cb7b..cc575f8e 100644 --- a/functional-tests/error-test-cases/type-alias-duplication/lib/duplication.simf +++ b/functional-tests/error-test-cases/type-alias-duplication/lib/duplication.simf @@ -1,2 +1,4 @@ +simc ">=0.6.0"; + pub type A = u32; pub type A = u64; \ No newline at end of file diff --git a/functional-tests/error-test-cases/type-alias-duplication/main.simf b/functional-tests/error-test-cases/type-alias-duplication/main.simf index da4c08be..dd813394 100644 --- a/functional-tests/error-test-cases/type-alias-duplication/main.simf +++ b/functional-tests/error-test-cases/type-alias-duplication/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use lib::duplication::A; fn main() { diff --git a/functional-tests/valid-test-cases/deep-reexport-chain/lib/level1.simf b/functional-tests/valid-test-cases/deep-reexport-chain/lib/level1.simf index a56bb43b..8cf1acf4 100644 --- a/functional-tests/valid-test-cases/deep-reexport-chain/lib/level1.simf +++ b/functional-tests/valid-test-cases/deep-reexport-chain/lib/level1.simf @@ -1,2 +1,4 @@ +simc ">=0.6.0"; + pub use crate::level2::CoreSmth; pub use crate::level2::core_val; \ No newline at end of file diff --git a/functional-tests/valid-test-cases/deep-reexport-chain/lib/level2.simf b/functional-tests/valid-test-cases/deep-reexport-chain/lib/level2.simf index 3c73e1bd..043235d7 100644 --- a/functional-tests/valid-test-cases/deep-reexport-chain/lib/level2.simf +++ b/functional-tests/valid-test-cases/deep-reexport-chain/lib/level2.simf @@ -1,2 +1,4 @@ +simc ">=0.6.0"; + pub use crate::level3::CoreSmth; pub use crate::level3::core_val; \ No newline at end of file diff --git a/functional-tests/valid-test-cases/deep-reexport-chain/lib/level3.simf b/functional-tests/valid-test-cases/deep-reexport-chain/lib/level3.simf index 6f0b9983..e3cb54b1 100644 --- a/functional-tests/valid-test-cases/deep-reexport-chain/lib/level3.simf +++ b/functional-tests/valid-test-cases/deep-reexport-chain/lib/level3.simf @@ -1,2 +1,4 @@ +simc ">=0.6.0"; + pub type CoreSmth = u32; pub fn core_val() -> CoreSmth { 42 } \ No newline at end of file diff --git a/functional-tests/valid-test-cases/deep-reexport-chain/main.simf b/functional-tests/valid-test-cases/deep-reexport-chain/main.simf index bd15f456..ea5bcd0d 100644 --- a/functional-tests/valid-test-cases/deep-reexport-chain/main.simf +++ b/functional-tests/valid-test-cases/deep-reexport-chain/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use lib::level1::CoreSmth; use lib::level1::core_val; diff --git a/functional-tests/valid-test-cases/diamond-dependency-resolution/lib/base.simf b/functional-tests/valid-test-cases/diamond-dependency-resolution/lib/base.simf index ad05850a..331edec1 100644 --- a/functional-tests/valid-test-cases/diamond-dependency-resolution/lib/base.simf +++ b/functional-tests/valid-test-cases/diamond-dependency-resolution/lib/base.simf @@ -1 +1,3 @@ +simc ">=0.6.0"; + pub type BaseType = u32; \ No newline at end of file diff --git a/functional-tests/valid-test-cases/diamond-dependency-resolution/lib/left.simf b/functional-tests/valid-test-cases/diamond-dependency-resolution/lib/left.simf index 7204af2f..b0b703b9 100644 --- a/functional-tests/valid-test-cases/diamond-dependency-resolution/lib/left.simf +++ b/functional-tests/valid-test-cases/diamond-dependency-resolution/lib/left.simf @@ -1,2 +1,4 @@ +simc ">=0.6.0"; + pub use crate::base::BaseType; pub fn get_left() -> BaseType { 1 } \ No newline at end of file diff --git a/functional-tests/valid-test-cases/diamond-dependency-resolution/lib/right.simf b/functional-tests/valid-test-cases/diamond-dependency-resolution/lib/right.simf index a4086971..54079642 100644 --- a/functional-tests/valid-test-cases/diamond-dependency-resolution/lib/right.simf +++ b/functional-tests/valid-test-cases/diamond-dependency-resolution/lib/right.simf @@ -1,2 +1,4 @@ +simc ">=0.6.0"; + pub use crate::base::BaseType; pub fn get_right() -> BaseType { 2 } \ No newline at end of file diff --git a/functional-tests/valid-test-cases/diamond-dependency-resolution/main.simf b/functional-tests/valid-test-cases/diamond-dependency-resolution/main.simf index 8b2835cb..834ebf75 100644 --- a/functional-tests/valid-test-cases/diamond-dependency-resolution/main.simf +++ b/functional-tests/valid-test-cases/diamond-dependency-resolution/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use lib::left::get_left; use lib::right::get_right; use lib::base::BaseType; diff --git a/functional-tests/valid-test-cases/external-library-uses-crate/ext_lib/internal.simf b/functional-tests/valid-test-cases/external-library-uses-crate/ext_lib/internal.simf index 0105464e..542e28af 100644 --- a/functional-tests/valid-test-cases/external-library-uses-crate/ext_lib/internal.simf +++ b/functional-tests/valid-test-cases/external-library-uses-crate/ext_lib/internal.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub fn core_add(a: u32, b: u32) -> u32 { let (_, sum): (bool, u32) = jet::add_32(a, b); sum diff --git a/functional-tests/valid-test-cases/external-library-uses-crate/ext_lib/math.simf b/functional-tests/valid-test-cases/external-library-uses-crate/ext_lib/math.simf index 2d5beb65..12a44821 100644 --- a/functional-tests/valid-test-cases/external-library-uses-crate/ext_lib/math.simf +++ b/functional-tests/valid-test-cases/external-library-uses-crate/ext_lib/math.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use crate::internal::core_add; pub fn add_one(a: u32) -> u32 { diff --git a/functional-tests/valid-test-cases/external-library-uses-crate/main.simf b/functional-tests/valid-test-cases/external-library-uses-crate/main.simf index 78cc1a9c..8c0ad129 100644 --- a/functional-tests/valid-test-cases/external-library-uses-crate/main.simf +++ b/functional-tests/valid-test-cases/external-library-uses-crate/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use ext_lib::math::add_one; fn main() { diff --git a/functional-tests/valid-test-cases/interleaved-waterfall/auth/verify.simf b/functional-tests/valid-test-cases/interleaved-waterfall/auth/verify.simf index 5b33d6b9..a248f0c5 100644 --- a/functional-tests/valid-test-cases/interleaved-waterfall/auth/verify.simf +++ b/functional-tests/valid-test-cases/interleaved-waterfall/auth/verify.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use types::def::UserId; use db::store::get_record; diff --git a/functional-tests/valid-test-cases/interleaved-waterfall/db/store.simf b/functional-tests/valid-test-cases/interleaved-waterfall/db/store.simf index 20a6cc61..e5bd72d5 100644 --- a/functional-tests/valid-test-cases/interleaved-waterfall/db/store.simf +++ b/functional-tests/valid-test-cases/interleaved-waterfall/db/store.simf @@ -1,2 +1,4 @@ +simc ">=0.6.0"; + use types::def::UserId; pub fn get_record(id: UserId) -> UserId { id } \ No newline at end of file diff --git a/functional-tests/valid-test-cases/interleaved-waterfall/main.simf b/functional-tests/valid-test-cases/interleaved-waterfall/main.simf index 0abb7218..569e9ad0 100644 --- a/functional-tests/valid-test-cases/interleaved-waterfall/main.simf +++ b/functional-tests/valid-test-cases/interleaved-waterfall/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use orch::handler::run_system; fn main() { diff --git a/functional-tests/valid-test-cases/interleaved-waterfall/orch/handler.simf b/functional-tests/valid-test-cases/interleaved-waterfall/orch/handler.simf index 86d627d6..12116be1 100644 --- a/functional-tests/valid-test-cases/interleaved-waterfall/orch/handler.simf +++ b/functional-tests/valid-test-cases/interleaved-waterfall/orch/handler.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use auth::verify::is_valid; use db::store::get_record; use types::def::UserId; diff --git a/functional-tests/valid-test-cases/interleaved-waterfall/types/def.simf b/functional-tests/valid-test-cases/interleaved-waterfall/types/def.simf index a8fd5650..8d2c7185 100644 --- a/functional-tests/valid-test-cases/interleaved-waterfall/types/def.simf +++ b/functional-tests/valid-test-cases/interleaved-waterfall/types/def.simf @@ -1 +1,3 @@ +simc ">=0.6.0"; + pub type UserId = u32; \ No newline at end of file diff --git a/functional-tests/valid-test-cases/leaky-signature/lib/internal.simf b/functional-tests/valid-test-cases/leaky-signature/lib/internal.simf index 2f11c01e..9cfdccf5 100644 --- a/functional-tests/valid-test-cases/leaky-signature/lib/internal.simf +++ b/functional-tests/valid-test-cases/leaky-signature/lib/internal.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + type SecretKey = u64; pub fn unlock(key: SecretKey) -> u64 { diff --git a/functional-tests/valid-test-cases/leaky-signature/main.simf b/functional-tests/valid-test-cases/leaky-signature/main.simf index 77592ac0..92ed04c1 100644 --- a/functional-tests/valid-test-cases/leaky-signature/main.simf +++ b/functional-tests/valid-test-cases/leaky-signature/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use lib::internal::unlock; fn main() { diff --git a/functional-tests/valid-test-cases/local-crate-nested/a/b.simf b/functional-tests/valid-test-cases/local-crate-nested/a/b.simf index 3804e1cb..42e1105d 100644 --- a/functional-tests/valid-test-cases/local-crate-nested/a/b.simf +++ b/functional-tests/valid-test-cases/local-crate-nested/a/b.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub fn helper() -> bool { true } \ No newline at end of file diff --git a/functional-tests/valid-test-cases/local-crate-nested/main.simf b/functional-tests/valid-test-cases/local-crate-nested/main.simf index 3e1d833e..7865290a 100644 --- a/functional-tests/valid-test-cases/local-crate-nested/main.simf +++ b/functional-tests/valid-test-cases/local-crate-nested/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use crate::a::b::helper; fn main() { diff --git a/functional-tests/valid-test-cases/local-crate/main.simf b/functional-tests/valid-test-cases/local-crate/main.simf index ba2cb9bd..8ddd9774 100644 --- a/functional-tests/valid-test-cases/local-crate/main.simf +++ b/functional-tests/valid-test-cases/local-crate/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use crate::utils::helper; fn main() { diff --git a/functional-tests/valid-test-cases/local-crate/utils.simf b/functional-tests/valid-test-cases/local-crate/utils.simf index 3804e1cb..42e1105d 100644 --- a/functional-tests/valid-test-cases/local-crate/utils.simf +++ b/functional-tests/valid-test-cases/local-crate/utils.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub fn helper() -> bool { true } \ No newline at end of file diff --git a/functional-tests/valid-test-cases/module-simple/lib/module.simf b/functional-tests/valid-test-cases/module-simple/lib/module.simf index d5cfec25..0d75ceaa 100644 --- a/functional-tests/valid-test-cases/module-simple/lib/module.simf +++ b/functional-tests/valid-test-cases/module-simple/lib/module.simf @@ -1 +1,3 @@ +simc ">=0.6.0"; + pub fn add() {} \ No newline at end of file diff --git a/functional-tests/valid-test-cases/module-simple/main.simf b/functional-tests/valid-test-cases/module-simple/main.simf index bb2705df..f63174ef 100644 --- a/functional-tests/valid-test-cases/module-simple/main.simf +++ b/functional-tests/valid-test-cases/module-simple/main.simf @@ -1,2 +1,4 @@ +simc ">=0.6.0"; + use lib::module::add; fn main() {} \ No newline at end of file diff --git a/functional-tests/valid-test-cases/multi-lib-facade/api/api.simf b/functional-tests/valid-test-cases/multi-lib-facade/api/api.simf index d80ca18a..232a37f5 100644 --- a/functional-tests/valid-test-cases/multi-lib-facade/api/api.simf +++ b/functional-tests/valid-test-cases/multi-lib-facade/api/api.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub use crypto::crypto::mock_hash; pub use math::math::MathInt; pub use math::math::add_two; \ No newline at end of file diff --git a/functional-tests/valid-test-cases/multi-lib-facade/crypto/crypto.simf b/functional-tests/valid-test-cases/multi-lib-facade/crypto/crypto.simf index 51111aca..3ea41187 100644 --- a/functional-tests/valid-test-cases/multi-lib-facade/crypto/crypto.simf +++ b/functional-tests/valid-test-cases/multi-lib-facade/crypto/crypto.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use math::math::MathInt; pub fn mock_hash(x: MathInt) -> (bool, MathInt) { diff --git a/functional-tests/valid-test-cases/multi-lib-facade/main.simf b/functional-tests/valid-test-cases/multi-lib-facade/main.simf index 44ff5c3c..939447b6 100644 --- a/functional-tests/valid-test-cases/multi-lib-facade/main.simf +++ b/functional-tests/valid-test-cases/multi-lib-facade/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use api::api::{add_two, mock_hash, MathInt}; fn main() { diff --git a/functional-tests/valid-test-cases/multi-lib-facade/math/math.simf b/functional-tests/valid-test-cases/multi-lib-facade/math/math.simf index d8d16499..76fb182d 100644 --- a/functional-tests/valid-test-cases/multi-lib-facade/math/math.simf +++ b/functional-tests/valid-test-cases/multi-lib-facade/math/math.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub type MathInt = u32; pub fn add_two(x: MathInt) -> (bool, MathInt) { jet::add_32(x, 2) diff --git a/functional-tests/valid-test-cases/reexport-diamond/lib/core.simf b/functional-tests/valid-test-cases/reexport-diamond/lib/core.simf index 71526bed..37c3e96b 100644 --- a/functional-tests/valid-test-cases/reexport-diamond/lib/core.simf +++ b/functional-tests/valid-test-cases/reexport-diamond/lib/core.simf @@ -1,2 +1,4 @@ +simc ">=0.6.0"; + pub type Coin = u64; pub fn mint(val: u64) -> Coin { val } \ No newline at end of file diff --git a/functional-tests/valid-test-cases/reexport-diamond/lib/route_a.simf b/functional-tests/valid-test-cases/reexport-diamond/lib/route_a.simf index 8021599e..36abe356 100644 --- a/functional-tests/valid-test-cases/reexport-diamond/lib/route_a.simf +++ b/functional-tests/valid-test-cases/reexport-diamond/lib/route_a.simf @@ -1,2 +1,4 @@ +simc ">=0.6.0"; + pub use crate::core::Coin; pub use crate::core::mint; \ No newline at end of file diff --git a/functional-tests/valid-test-cases/reexport-diamond/lib/route_b.simf b/functional-tests/valid-test-cases/reexport-diamond/lib/route_b.simf index 32edb866..57e192e2 100644 --- a/functional-tests/valid-test-cases/reexport-diamond/lib/route_b.simf +++ b/functional-tests/valid-test-cases/reexport-diamond/lib/route_b.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + pub use crate::core::Coin; pub fn burn(c: Coin) -> (bool, Coin) { diff --git a/functional-tests/valid-test-cases/reexport-diamond/main.simf b/functional-tests/valid-test-cases/reexport-diamond/main.simf index 799b96e6..f61edfe9 100644 --- a/functional-tests/valid-test-cases/reexport-diamond/main.simf +++ b/functional-tests/valid-test-cases/reexport-diamond/main.simf @@ -1,3 +1,5 @@ +simc ">=0.6.0"; + use lib::route_a::Coin; use lib::route_a::mint; use lib::route_b::burn; From cd5b1e96ad60102e538fd9d39310c3d8601b2066 Mon Sep 17 00:00:00 2001 From: Sdoba16 Date: Thu, 4 Jun 2026 17:22:21 +0300 Subject: [PATCH 5/5] Add documentation for versioning --- doc/versioning.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 doc/versioning.md diff --git a/doc/versioning.md b/doc/versioning.md new file mode 100644 index 00000000..c9ef6cd3 --- /dev/null +++ b/doc/versioning.md @@ -0,0 +1,30 @@ +# Compiler Versioning + +The SimplicityHL compiler enforces strict version compatibility to prevent contracts from silently breaking due to compiler updates, semantic changes, or new language features. + +Every `.simf` file must begin with a compiler version directive: +```rust +simc ">=0.6.0"; +``` + +## Semantic Versioning (SemVer) + +The compiler uses standard Semantic Versioning rules to evaluate whether a file is compatible with the currently running compiler. You can use operators to define acceptable ranges: + +* **Caret (`^`) or Bare strings:** `^0.6.0` or `0.6.0`. Allows patch-level and minor-level updates that do not modify the left-most non-zero digit. (e.g., `^0.6.0` allows `0.6.1`, but rejects `0.7.0`). +* **Tilde (`~`):** `~0.6.0`. Allows only patch-level updates. (e.g., `~0.6.0` allows `0.6.1`, but rejects `0.6.2` if it introduces new minor features). +* **Exact (`=`):** `=0.6.0`. Strictly requires this exact version of the compiler. +* **Inequalities (`>`, `>=`, `<`, `<=`):** `>=0.6.0`. Allows any compiler version equal to or newer than `0.6.0`. +* **Wildcards (`*`, `x`):** `0.x.x`. Allows any version matching the specified major release. +* **Multiple Bounds:** `>=0.6.0, <1.0.0`. You can combine operators with a comma. + +### Pre-release versions +If the compiler is currently on a pre-release version (e.g., `0.6.0-rc.0`), it will only match against contracts that explicitly request that exact pre-release base, or contracts that safely encompass the base version. + +## Multi-File Enforcement + +Version checking is performed eagerly immediately after the initial syntax parsing, before dependency resolution and semantic analysis occur. When building a multi-file project, the compiler driver evaluates the version directive of the `main.simf` entry point, as well as the directives of every external library file imported via the `--dep` flag. + +If *any* file in the dependency graph requires a compiler version that is incompatible with the currently running compiler, the driver immediately halts compilation. + +This mathematical guarantee ensures that an older, stable library cannot be accidentally compiled with an incompatible compiler without the developer's explicit consent.