1use std::collections::hash_map::Entry;
2use std::collections::{HashMap, HashSet};
3use std::num::NonZeroUsize;
4use std::sync::Arc;
5
6use either::Either;
7use miniscript::iter::{Tree, TreeLike};
8use simplicity::jet::{Core, Elements, Jet};
9
10use crate::debug::{CallTracker, DebugSymbols, TrackedCallName};
11use crate::driver::{CRATE_STR, MAIN_STR};
12use crate::error::{Diagnostic, Error, Span, WithSpan};
13use crate::jet::{source_type, target_type, JetHL};
14use crate::num::{NonZeroPow2Usize, Pow2Usize};
15use crate::parse::{MatchPattern, UseDecl, Visibility};
16use crate::pattern::Pattern;
17use crate::str::{AliasName, FunctionName, Identifier, ModuleName, SymbolName, WitnessName};
18use crate::types::{
19 AliasedType, EnumInfo, EnumVariantInfo, ResolvedType, StructuralType, TypeConstructible,
20 TypeDeconstructible, TypeInner, UIntType,
21};
22use crate::value::{UIntValue, Value};
23use crate::witness::{Parameters, WitnessTypes};
24use crate::{impl_eq_hash, parse};
25
26#[derive(Clone, Debug, Eq, PartialEq)]
31pub struct Program {
32 main: Expression,
33 parameters: Parameters,
34 witness_types: WitnessTypes,
35 call_tracker: Arc<CallTracker>,
36}
37
38impl Program {
39 pub fn main(&self) -> &Expression {
43 &self.main
44 }
45
46 pub fn parameters(&self) -> &Parameters {
48 &self.parameters
49 }
50
51 pub fn witness_types(&self) -> &WitnessTypes {
53 &self.witness_types
54 }
55
56 pub fn debug_symbols(&self, file: &str) -> DebugSymbols {
58 self.call_tracker.with_file(file)
59 }
60
61 pub(crate) fn call_tracker(&self) -> &Arc<CallTracker> {
63 &self.call_tracker
64 }
65}
66
67#[derive(Clone, Debug, Eq, PartialEq, Hash)]
71pub enum Item {
72 TypeAlias,
76 EnumDeclaration,
81 Function(Function),
83 Use,
84 Module(Vec<Item>),
85 Ignored,
87}
88
89#[derive(Clone, Debug, Eq, PartialEq, Hash)]
93pub enum Function {
94 Custom,
98 Main(Expression),
106}
107
108#[derive(Clone, Debug, Eq, PartialEq, Hash)]
113pub enum Statement {
114 Assignment(Assignment),
116 Expression(Expression),
118}
119
120#[derive(Clone, Debug)]
122pub struct Assignment {
123 pattern: Pattern,
124 expression: Expression,
125 span: Span,
126}
127
128impl Assignment {
129 pub fn pattern(&self) -> &Pattern {
131 &self.pattern
132 }
133
134 pub fn expression(&self) -> &Expression {
136 &self.expression
137 }
138
139 pub fn span(&self) -> &Span {
141 &self.span
142 }
143}
144
145impl_eq_hash!(Assignment; pattern, expression);
146
147#[derive(Clone, Debug)]
149pub struct Expression {
150 inner: ExpressionInner,
151 ty: ResolvedType,
152 span: Span,
153}
154
155impl_eq_hash!(Expression; inner, ty);
156
157impl Expression {
158 pub fn inner(&self) -> &ExpressionInner {
160 &self.inner
161 }
162
163 pub fn ty(&self) -> &ResolvedType {
165 &self.ty
166 }
167
168 pub fn span(&self) -> &Span {
170 &self.span
171 }
172}
173
174#[derive(Clone, Debug, Eq, PartialEq, Hash)]
176pub enum ExpressionInner {
177 Single(SingleExpression),
179 Block(Arc<[Statement]>, Option<Arc<Expression>>),
183}
184
185#[derive(Clone, Debug)]
187pub struct SingleExpression {
188 inner: SingleExpressionInner,
189 ty: ResolvedType,
190 span: Span,
191}
192
193impl SingleExpression {
194 pub fn tuple(args: Arc<[Expression]>, span: Span) -> Self {
196 let ty = ResolvedType::tuple(
197 args.iter()
198 .map(Expression::ty)
199 .cloned()
200 .collect::<Vec<ResolvedType>>(),
201 );
202 let inner = SingleExpressionInner::Tuple(args);
203 Self { inner, ty, span }
204 }
205
206 pub fn inner(&self) -> &SingleExpressionInner {
208 &self.inner
209 }
210
211 pub fn ty(&self) -> &ResolvedType {
213 &self.ty
214 }
215
216 pub fn span(&self) -> &Span {
218 &self.span
219 }
220}
221
222impl_eq_hash!(SingleExpression; inner, ty);
223
224#[derive(Clone, Debug, Eq, PartialEq, Hash)]
226pub enum SingleExpressionInner {
227 Constant(Value),
229 Witness(WitnessName),
231 Parameter(WitnessName),
233 Variable(Identifier),
235 Expression(Arc<Expression>),
237 Tuple(Arc<[Expression]>),
239 Array(Arc<[Expression]>),
241 List(Arc<[Expression]>),
243 Either(Either<Arc<Expression>, Arc<Expression>>),
245 Option(Option<Arc<Expression>>),
247 Call(Call),
249 Match(Match),
251 EnumMatch(EnumMatch),
253 EnumConstruction(EnumConstruction),
257}
258
259#[derive(Clone, Debug)]
261pub struct Call {
262 name: CallName,
263 args: Arc<[Expression]>,
264 span: Span,
265}
266
267impl Call {
268 pub fn name(&self) -> &CallName {
270 &self.name
271 }
272
273 pub fn args(&self) -> &Arc<[Expression]> {
275 &self.args
276 }
277
278 pub fn span(&self) -> &Span {
280 &self.span
281 }
282}
283
284impl_eq_hash!(Call; name, args);
285
286#[derive(Clone, Debug, Eq, Hash)]
288#[allow(clippy::derived_hash_with_manual_eq)] pub enum CallName {
290 Jet(Box<dyn JetHL>),
292 UnwrapLeft(ResolvedType),
294 UnwrapRight(ResolvedType),
296 IsNone(ResolvedType),
298 Unwrap,
300 Assert,
302 Panic,
304 Debug,
306 TypeCast(ResolvedType),
308 Custom(CustomFunction),
313 Fold(CustomFunction, NonZeroPow2Usize),
315 ArrayFold(CustomFunction, NonZeroUsize),
317 ForWhile(CustomFunction, Pow2Usize),
319}
320
321impl PartialEq for CallName {
325 fn eq(&self, other: &Self) -> bool {
326 match (self, other) {
327 (Self::Jet(a), Self::Jet(b)) => a == b,
328 (Self::UnwrapLeft(a), Self::UnwrapLeft(b)) => a == b,
329 (Self::UnwrapRight(a), Self::UnwrapRight(b)) => a == b,
330 (Self::IsNone(a), Self::IsNone(b)) => a == b,
331 (Self::Unwrap, Self::Unwrap) => true,
332 (Self::Assert, Self::Assert) => true,
333 (Self::Panic, Self::Panic) => true,
334 (Self::Debug, Self::Debug) => true,
335 (Self::TypeCast(a), Self::TypeCast(b)) => a == b,
336 (Self::Custom(a), Self::Custom(b)) => a == b,
337 (Self::Fold(a, b), Self::Fold(c, d)) => a == c && b == d,
338 (Self::ArrayFold(a, b), Self::ArrayFold(c, d)) => a == c && b == d,
339 (Self::ForWhile(a, b), Self::ForWhile(c, d)) => a == c && b == d,
340 _ => false,
341 }
342 }
343}
344
345#[derive(Clone, Debug)]
347pub struct CustomFunction {
348 params: Arc<[FunctionParam]>,
349 body: Arc<Expression>,
350 span: Span,
351}
352
353impl CustomFunction {
354 pub fn params(&self) -> &[FunctionParam] {
356 &self.params
357 }
358
359 pub fn body(&self) -> &Expression {
361 &self.body
362 }
363
364 pub fn span(&self) -> &Span {
366 &self.span
367 }
368
369 pub fn params_pattern(&self) -> Pattern {
371 Pattern::tuple(
372 self.params()
373 .iter()
374 .map(FunctionParam::identifier)
375 .cloned()
376 .map(Pattern::Identifier),
377 )
378 }
379}
380
381impl_eq_hash!(CustomFunction; params, body);
382
383#[derive(Clone, Debug)]
385pub struct FunctionParam {
386 identifier: Identifier,
387 ty: ResolvedType,
388 span: Span,
389}
390
391impl FunctionParam {
392 pub fn identifier(&self) -> &Identifier {
394 &self.identifier
395 }
396
397 pub fn ty(&self) -> &ResolvedType {
399 &self.ty
400 }
401
402 pub fn span(&self) -> &Span {
404 &self.span
405 }
406}
407
408impl_eq_hash!(FunctionParam; identifier, ty);
409
410#[derive(Clone, Debug)]
412pub struct Match {
413 scrutinee: Arc<Expression>,
414 left: MatchArm,
415 right: MatchArm,
416 span: Span,
417}
418
419impl Match {
420 pub fn scrutinee(&self) -> &Expression {
422 &self.scrutinee
423 }
424
425 pub fn left(&self) -> &MatchArm {
427 &self.left
428 }
429
430 pub fn right(&self) -> &MatchArm {
432 &self.right
433 }
434
435 pub fn span(&self) -> &Span {
437 &self.span
438 }
439}
440
441impl_eq_hash!(Match; scrutinee, left, right);
442
443#[derive(Clone, Debug)]
445pub struct EnumMatch {
446 scrutinee: Arc<Expression>,
447 arms: Arc<[EnumMatchArm]>,
451 span: Span,
452}
453
454impl EnumMatch {
455 pub fn scrutinee(&self) -> &Expression {
457 &self.scrutinee
458 }
459
460 pub fn arms(&self) -> &[EnumMatchArm] {
462 &self.arms
463 }
464
465 pub fn span(&self) -> &Span {
467 &self.span
468 }
469}
470
471impl_eq_hash!(EnumMatch; scrutinee, arms);
472
473#[derive(Clone, Debug)]
475pub struct EnumMatchArm {
476 pattern: Pattern,
479 body: Arc<Expression>,
480 span: Span,
481}
482
483impl EnumMatchArm {
484 pub fn pattern(&self) -> &Pattern {
486 &self.pattern
487 }
488
489 pub fn body(&self) -> &Expression {
491 &self.body
492 }
493
494 pub fn span(&self) -> &Span {
496 &self.span
497 }
498}
499
500impl_eq_hash!(EnumMatchArm; pattern, body);
501
502#[derive(Clone, Debug)]
506pub struct EnumConstruction {
507 variant_index: usize,
508 payload: Arc<[Arc<Expression>]>,
509 span: Span,
510}
511
512impl EnumConstruction {
513 pub fn variant_index(&self) -> usize {
515 self.variant_index
516 }
517
518 pub fn payload(&self) -> &[Arc<Expression>] {
520 &self.payload
521 }
522}
523
524impl_eq_hash!(EnumConstruction; variant_index, payload);
525
526impl AsRef<Span> for EnumConstruction {
527 fn as_ref(&self) -> &Span {
528 &self.span
529 }
530}
531
532impl AsRef<Span> for EnumMatch {
533 fn as_ref(&self) -> &Span {
534 &self.span
535 }
536}
537
538#[derive(Clone, Debug)]
540pub struct MatchArm {
541 pattern: MatchPattern,
542 expression: Arc<Expression>,
543 span: Span,
544}
545
546impl MatchArm {
547 pub fn pattern(&self) -> &MatchPattern {
549 &self.pattern
550 }
551
552 pub fn expression(&self) -> &Expression {
554 &self.expression
555 }
556
557 pub fn span(&self) -> &Span {
559 &self.span
560 }
561}
562
563impl_eq_hash!(MatchArm; pattern, expression);
564
565#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
566pub enum ExprTree<'a> {
567 Expression(&'a Expression),
568 Block(&'a [Statement], &'a Option<Arc<Expression>>),
569 Statement(&'a Statement),
570 Assignment(&'a Assignment),
571 Single(&'a SingleExpression),
572 Call(&'a Call),
573 Match(&'a Match),
574 EnumMatch(&'a EnumMatch),
575}
576
577impl TreeLike for ExprTree<'_> {
578 fn as_node(&self) -> Tree<Self> {
579 use SingleExpressionInner as S;
580
581 match self {
582 Self::Expression(expr) => match expr.inner() {
583 ExpressionInner::Block(statements, maybe_expr) => {
584 Tree::Unary(Self::Block(statements, maybe_expr))
585 }
586 ExpressionInner::Single(single) => Tree::Unary(Self::Single(single)),
587 },
588 Self::Block(statements, maybe_expr) => Tree::Nary(
589 statements
590 .iter()
591 .map(Self::Statement)
592 .chain(maybe_expr.iter().map(Arc::as_ref).map(Self::Expression))
593 .collect(),
594 ),
595 Self::Statement(statement) => match statement {
596 Statement::Assignment(assignment) => Tree::Unary(Self::Assignment(assignment)),
597 Statement::Expression(expression) => Tree::Unary(Self::Expression(expression)),
598 },
599 Self::Assignment(assignment) => Tree::Unary(Self::Expression(assignment.expression())),
600 Self::Single(single) => match single.inner() {
601 S::Constant(_)
602 | S::Witness(_)
603 | S::Parameter(_)
604 | S::Variable(_)
605 | S::Option(None) => Tree::Nullary,
606 S::Expression(l)
607 | S::Either(Either::Left(l))
608 | S::Either(Either::Right(l))
609 | S::Option(Some(l)) => Tree::Unary(Self::Expression(l)),
610 S::Tuple(elements) | S::Array(elements) | S::List(elements) => {
611 Tree::Nary(elements.iter().map(Self::Expression).collect())
612 }
613 S::Call(call) => Tree::Unary(Self::Call(call)),
614 S::Match(match_) => Tree::Unary(Self::Match(match_)),
615 S::EnumMatch(enum_match) => Tree::Unary(Self::EnumMatch(enum_match)),
616 S::EnumConstruction(construction) => Tree::Nary(
617 construction
618 .payload()
619 .iter()
620 .map(|arg| Self::Expression(arg))
621 .collect(),
622 ),
623 },
624 Self::Call(call) => Tree::Nary(call.args().iter().map(Self::Expression).collect()),
625 Self::Match(match_) => Tree::Nary(Arc::new([
626 Self::Expression(match_.scrutinee()),
627 Self::Expression(match_.left().expression()),
628 Self::Expression(match_.right().expression()),
629 ])),
630 Self::EnumMatch(enum_match) => Tree::Nary(
631 std::iter::once(Self::Expression(enum_match.scrutinee()))
632 .chain(
633 enum_match
634 .arms()
635 .iter()
636 .map(|arm| Self::Expression(arm.body())),
637 )
638 .collect(),
639 ),
640 }
641 }
642}
643
644pub trait JetHinter: std::fmt::Debug + Send + Sync {
653 fn parse_jet(&self, name: &str) -> Option<Box<dyn JetHL>>;
655 fn construct_verify(&self) -> Box<dyn JetHL>;
657 fn conjure(&self, jet: &dyn Jet) -> Option<Box<dyn JetHL>>;
659
660 fn clone_box(&self) -> Box<dyn JetHinter>;
662}
663
664macro_rules! impl_jet_hinter {
665 ($struct_name:ident, $jet_type:ident) => {
666 #[derive(Clone, Debug, Default)]
667 pub struct $struct_name;
668
669 impl $struct_name {
670 pub fn new() -> Self {
671 Self
672 }
673 }
674
675 impl JetHinter for $struct_name {
676 fn parse_jet(&self, name: &str) -> Option<Box<dyn JetHL>> {
677 $jet_type::parse(name)
678 .ok()
679 .map(|jet| -> Box<dyn JetHL> { Box::new(jet) })
680 }
681
682 fn construct_verify(&self) -> Box<dyn JetHL> {
683 Box::new($jet_type::Verify)
684 }
685
686 fn conjure(&self, jet: &dyn Jet) -> Option<Box<dyn JetHL>> {
687 jet.as_any()
688 .downcast_ref::<$jet_type>()
689 .map(|jet| Box::new(*jet) as Box<dyn JetHL>)
690 }
691
692 fn clone_box(&self) -> Box<dyn JetHinter> {
693 Box::new(Self)
694 }
695 }
696 };
697}
698
699impl_jet_hinter!(ElementsJetHinter, Elements);
700impl_jet_hinter!(CoreJetHinter, Core);
701
702#[derive(Clone, Debug, Eq, PartialEq, Default)]
704struct ModuleScope {
705 aliases: HashMap<AliasName, (ResolvedType, Visibility)>,
706 functions: HashMap<FunctionName, (CustomFunction, Visibility)>,
707 submodules: HashMap<ModuleName, (ModuleScope, Visibility)>,
709}
710
711struct Scope {
719 module_path: Vec<ModuleName>,
722
723 root: ModuleScope,
725
726 variables: Vec<HashMap<Identifier, ResolvedType>>,
728 parameters: HashMap<WitnessName, ResolvedType>,
729 witnesses: HashMap<WitnessName, ResolvedType>,
730 unscoped_enum_names: bool,
734 is_main: bool,
735 call_tracker: CallTracker,
736 jet_hinter: Box<dyn JetHinter>,
737}
738
739impl Default for Scope {
740 fn default() -> Self {
741 Self::new(
742 Box::new(ElementsJetHinter),
744 )
745 }
746}
747
748impl Scope {
749 pub fn new(jet_hinter: Box<dyn JetHinter>) -> Self {
750 Self {
751 module_path: Vec::new(),
752 root: ModuleScope::default(),
753 variables: Vec::new(),
754 parameters: HashMap::new(),
755 witnesses: HashMap::new(),
756 unscoped_enum_names: false,
757 is_main: false,
758 call_tracker: CallTracker::default(),
759 jet_hinter,
760 }
761 }
762
763 fn for_value_parsing() -> Self {
766 Self {
767 unscoped_enum_names: true,
768 ..Self::default()
769 }
770 }
771
772 pub fn is_outside_function(&self) -> bool {
773 self.variables.is_empty()
774 }
775
776 pub fn enter_block(&mut self) {
778 self.variables.push(HashMap::new());
779 }
780
781 pub fn enter_main(&mut self) {
788 assert!(!self.is_main, "Already inside main function");
789 assert!(self.is_outside_function(), "Already inside a function body");
790 self.enter_block();
791 self.is_main = true;
792 }
793
794 pub fn exit_block(&mut self) {
800 self.variables.pop().expect("No active block to exit");
801 }
802
803 pub fn exit_main(&mut self) {
810 assert!(self.is_main, "Current scope is not inside main function");
811 self.exit_block();
812 self.is_main = false;
813 assert!(
814 self.is_outside_function(),
815 "Current scope is not nested in topmost scope"
816 )
817 }
818
819 pub fn enter_module(&mut self, name: ModuleName, visibility: Visibility) -> Result<(), Error> {
825 let current = self.current_module_mut();
826 if current.submodules.contains_key(&name) {
827 return Err(Error::ModuleRedefined { name });
828 }
829
830 current
831 .submodules
832 .insert(name.clone(), (ModuleScope::default(), visibility));
833 self.module_path.push(name);
834 Ok(())
835 }
836
837 pub fn exit_module(&mut self) {
843 self.module_path.pop().expect("Not inside any module");
844 }
845
846 fn current_module(&self) -> &ModuleScope {
849 self.module_path.iter().fold(&self.root, |scope, segment| {
850 &scope.submodules.get(segment).expect("Module not found").0
851 })
852 }
853
854 fn current_module_mut(&mut self) -> &mut ModuleScope {
857 self.module_path
858 .iter()
859 .fold(&mut self.root, |scope, segment| {
860 &mut scope
861 .submodules
862 .get_mut(segment)
863 .expect("Module not found")
864 .0
865 })
866 }
867
868 pub fn resolve_use(&mut self, use_decl: &UseDecl) -> Result<(), Error> {
880 let path = use_decl.path();
881 if path.first().map(|id| id.as_inner()) != Some(CRATE_STR) {
882 return Err(Error::MissingCrateKeyword);
883 }
884
885 let use_vis = use_decl.visibility().clone();
886 let use_decl_items = match use_decl.items() {
887 parse::UseItems::Single(elem) => std::slice::from_ref(elem),
888 parse::UseItems::List(elems) => elems.as_slice(),
889 };
890
891 let collected: Vec<_> = {
895 let shared_prefix_len = self
898 .module_path
899 .iter()
900 .zip(&path[1..])
901 .take_while(|(curr, nav)| curr.as_inner() == nav.as_inner())
902 .count();
903
904 let mut target_scope = &self.root;
905
906 for (ind, segment) in path[1..].iter().enumerate() {
907 let name = ModuleName::from_str_unchecked(segment.as_inner());
908
909 let (inner, visibility) = target_scope
910 .submodules
911 .get(&name)
912 .ok_or_else(|| Error::ModuleNotFound { name: name.clone() })?;
913
914 if matches!(visibility, Visibility::Private) && shared_prefix_len < ind {
915 return Err(Error::ModuleIsPrivate { name });
916 }
917
918 target_scope = inner;
919 }
920
921 let mut collected = Vec::with_capacity(use_decl_items.len());
922 for (name, aliased) in use_decl_items {
923 if aliased.as_ref().is_some_and(|a| a.as_inner() == MAIN_STR) {
924 return Err(Error::MainCannotBeAlias);
925 }
926
927 let local_name = aliased.as_ref().unwrap_or(name);
928
929 let alias_res =
930 Self::try_collect_item(name, local_name, &target_scope.aliases, &use_vis);
931 let func_res =
932 Self::try_collect_item(name, local_name, &target_scope.functions, &use_vis);
933 let mod_res =
934 Self::try_collect_item(name, local_name, &target_scope.submodules, &use_vis);
935
936 collected.push((alias_res, func_res, mod_res));
937 }
938 collected
939 };
940
941 let current = self.current_module_mut();
943 for (alias_res, func_res, mod_res) in collected {
944 Self::resolve_processing_use_items_error(&[
945 Self::insert_collected(alias_res, &mut current.aliases),
946 Self::insert_collected(func_res, &mut current.functions),
947 Self::insert_collected(mod_res, &mut current.submodules),
948 ])?;
949 }
950
951 Ok(())
952 }
953
954 fn try_collect_item<K, V>(
961 name: &SymbolName,
962 local_name: &SymbolName,
963 target_map: &HashMap<K, (V, Visibility)>,
964 use_vis: &Visibility,
965 ) -> Result<(K, (V, Visibility)), Error>
966 where
967 K: Eq + std::hash::Hash + From<SymbolName> + Clone,
968 V: Clone,
969 {
970 let (value, vis) =
971 target_map
972 .get(&K::from(name.clone()))
973 .ok_or_else(|| Error::UnresolvedItem {
974 name: name.to_string(),
975 })?;
976
977 if matches!(vis, Visibility::Private) {
978 return Err(Error::PrivateItem {
979 name: name.to_string(),
980 });
981 }
982
983 Ok((
984 K::from(local_name.clone()),
985 (value.clone(), use_vis.clone()),
986 ))
987 }
988
989 fn insert_collected<K, V>(
996 res: Result<(K, (V, Visibility)), Error>,
997 map: &mut HashMap<K, (V, Visibility)>,
998 ) -> Result<(), Error>
999 where
1000 K: Eq + std::hash::Hash + std::fmt::Display,
1001 {
1002 res.and_then(|(k, v)| match map.entry(k) {
1003 Entry::Occupied(entry) => Err(Error::RedefinedItem {
1004 name: entry.key().to_string(),
1005 }),
1006 Entry::Vacant(entry) => {
1007 entry.insert(v);
1008 Ok(())
1009 }
1010 })
1011 }
1012
1013 fn resolve_processing_use_items_error(results: &[Result<(), Error>]) -> Result<(), Error> {
1022 if results.iter().any(|res| res.is_ok()) {
1023 return Ok(());
1024 }
1025
1026 let errors: Vec<&Error> = results
1027 .iter()
1028 .filter_map(|res| res.as_ref().err())
1029 .collect();
1030
1031 if let Some(&specific_err) = errors
1032 .iter()
1033 .find(|e| !matches!(e, Error::UnresolvedItem { .. }))
1034 {
1035 return Err(specific_err.clone());
1036 }
1037
1038 Err(errors[0].clone())
1040 }
1041
1042 pub fn insert_variable(&mut self, identifier: Identifier, ty: ResolvedType) {
1048 self.variables
1049 .last_mut()
1050 .expect("Stack is empty")
1051 .insert(identifier, ty);
1052 }
1053
1054 pub fn get_variable(&self, identifier: &Identifier) -> Option<&ResolvedType> {
1056 self.variables
1057 .iter()
1058 .rev()
1059 .find_map(|scope| scope.get(identifier))
1060 }
1061
1062 fn get_alias(&self, name: &AliasName) -> Result<ResolvedType, Error> {
1068 self.current_module()
1069 .aliases
1070 .get(name)
1071 .map(|(ty, _)| ty.clone())
1072 .ok_or_else(|| Error::UndefinedAlias { name: name.clone() })
1073 }
1074
1075 pub fn resolve(&self, ty: &AliasedType) -> Result<ResolvedType, Error> {
1081 ty.resolve(|name| self.get_alias(name))
1082 }
1083
1084 fn check_alias_free(&self, name: &AliasName) -> Result<(), Error> {
1086 if self.current_module().aliases.contains_key(name) {
1087 return Err(Error::RedefinedAlias { name: name.clone() });
1088 }
1089
1090 Ok(())
1091 }
1092
1093 pub fn insert_alias(&mut self, alias: parse::TypeAlias) -> Result<(), Error> {
1099 self.check_alias_free(alias.name())?;
1100
1101 let resolved = self.resolve(alias.ty())?;
1102
1103 self.current_module_mut()
1104 .aliases
1105 .insert(alias.name().clone(), (resolved, alias.visibility().clone()));
1106
1107 Ok(())
1108 }
1109
1110 pub fn insert_enum(
1123 &mut self,
1124 name: AliasName,
1125 visibility: Visibility,
1126 variants: Arc<[EnumVariantInfo]>,
1127 ) -> Result<(), Error> {
1128 self.check_alias_free(&name)?;
1129
1130 let info = EnumInfo::new(Arc::from(name.as_inner()), variants);
1131 let resolved = ResolvedType::enumeration(info);
1132
1133 self.current_module_mut()
1134 .aliases
1135 .insert(name, (resolved, visibility));
1136
1137 Ok(())
1138 }
1139
1140 pub fn insert_parameter(&mut self, name: WitnessName, ty: ResolvedType) -> Result<(), Error> {
1146 match self.parameters.entry(name.clone()) {
1147 Entry::Occupied(entry) if entry.get() == &ty => Ok(()),
1148 Entry::Occupied(entry) => Err(Error::ExpressionTypeMismatch {
1149 expected: entry.get().clone(),
1150 found: ty,
1151 }),
1152 Entry::Vacant(entry) => {
1153 entry.insert(ty);
1154 Ok(())
1155 }
1156 }
1157 }
1158
1159 pub fn insert_witness(&mut self, name: WitnessName, ty: ResolvedType) -> Result<(), Error> {
1166 if !self.is_main {
1167 return Err(Error::WitnessOutsideMain);
1168 }
1169
1170 match self.witnesses.entry(name.clone()) {
1171 Entry::Occupied(_) => Err(Error::WitnessReused { name }),
1172 Entry::Vacant(entry) => {
1173 entry.insert(ty);
1174 Ok(())
1175 }
1176 }
1177 }
1178
1179 pub fn destruct(self) -> (Parameters, WitnessTypes, CallTracker) {
1185 (
1186 Parameters::from(self.parameters),
1187 WitnessTypes::from(self.witnesses),
1188 self.call_tracker,
1189 )
1190 }
1191
1192 pub fn insert_function(
1198 &mut self,
1199 name: FunctionName,
1200 visibility: Visibility,
1201 function: CustomFunction,
1202 ) -> Result<(), Error> {
1203 if self.current_module().functions.contains_key(&name) {
1204 return Err(Error::FunctionRedefined { name });
1205 }
1206
1207 self.current_module_mut()
1208 .functions
1209 .insert(name, (function, visibility));
1210 Ok(())
1211 }
1212
1213 pub fn get_function(&self, name: &FunctionName) -> Result<CustomFunction, Error> {
1219 self.current_module()
1220 .functions
1221 .get(name)
1222 .map(|(func, _)| func.clone())
1223 .ok_or_else(|| Error::FunctionUndefined { name: name.clone() })
1224 }
1225
1226 pub fn track_call<S: AsRef<Span>>(&mut self, span: &S, name: TrackedCallName) {
1228 self.call_tracker.track_call(*span.as_ref(), name);
1229 }
1230}
1231
1232trait AbstractSyntaxTree: Sized {
1234 type From;
1236
1237 fn analyze(from: &Self::From, ty: &ResolvedType, scope: &mut Scope)
1243 -> Result<Self, Diagnostic>;
1244}
1245
1246impl Program {
1247 pub fn analyze(
1248 from: &parse::Program,
1249 jet_hinter: Box<dyn JetHinter>,
1250 ) -> Result<Self, Diagnostic> {
1251 let unit = ResolvedType::unit();
1252 let mut scope = Scope::new(jet_hinter);
1253
1254 let items = from
1255 .items()
1256 .iter()
1257 .map(|s| Item::analyze(s, &unit, &mut scope))
1258 .collect::<Result<Vec<Item>, Diagnostic>>()?;
1259 debug_assert!(scope.is_outside_function());
1260 debug_assert!(
1261 scope.module_path.is_empty(),
1262 "Unclosed module scopes remain"
1263 );
1264
1265 let (parameters, witness_types, call_tracker) = scope.destruct();
1266 let main = Self::extract_single_main(&items)
1267 .map_err(|err| err.with_span(from.into()))?
1269 .ok_or(Error::MainRequired)
1270 .with_span(from)?;
1271
1272 Ok(Self {
1273 main,
1274 parameters,
1275 witness_types,
1276 call_tracker: Arc::new(call_tracker),
1277 })
1278 }
1279
1280 fn extract_single_main(items: &[Item]) -> Result<Option<Expression>, Error> {
1281 let mut main_expr = None;
1282
1283 for item in items {
1284 let extracted = match item {
1285 Item::Function(Function::Main(expr)) => Some(expr.clone()),
1286 Item::Module(items) => Self::extract_single_main(items)?,
1287 _ => None,
1288 };
1289
1290 let Some(expr) = extracted else {
1291 continue;
1292 };
1293
1294 if main_expr.replace(expr).is_some() {
1295 return Err(Error::FunctionRedefined {
1296 name: FunctionName::main(),
1297 });
1298 }
1299 }
1300
1301 Ok(main_expr)
1302 }
1303}
1304
1305impl AbstractSyntaxTree for Item {
1306 type From = parse::Item;
1307
1308 fn analyze(
1309 from: &Self::From,
1310 ty: &ResolvedType,
1311 scope: &mut Scope,
1312 ) -> Result<Self, Diagnostic> {
1313 assert!(ty.is_unit(), "Items cannot return anything");
1314 assert!(
1315 scope.is_outside_function(),
1316 "Variables live only inside the function"
1317 );
1318
1319 match from {
1320 parse::Item::TypeAlias(alias) => {
1321 scope.insert_alias(alias.clone()).with_span(alias)?;
1322 Ok(Self::TypeAlias)
1323 }
1324 parse::Item::Function(function) => {
1325 Function::analyze(function, ty, scope).map(Self::Function)
1326 }
1327 parse::Item::Use(use_decl) => {
1328 scope.resolve_use(use_decl).with_span(use_decl)?;
1329 Ok(Self::Use)
1330 }
1331 parse::Item::EnumDeclaration(decl) => {
1332 if decl.variants().is_empty() {
1333 return Err(Error::Grammar {
1336 msg: format!("enum '{}' must have at least one variant", decl.name()),
1337 })
1338 .with_span(decl);
1339 }
1340
1341 let mut seen_names = HashSet::new();
1342 for v in decl.variants() {
1343 if !seen_names.insert(v.name()) {
1344 return Err(Error::Grammar {
1345 msg: format!(
1346 "enum '{}' has duplicate variant name '{}'",
1347 decl.name(),
1348 v.name()
1349 ),
1350 })
1351 .with_span(decl);
1352 }
1353 }
1354
1355 let variants = decl
1356 .variants()
1357 .iter()
1358 .map(|v| {
1359 let payload = v
1360 .payload()
1361 .iter()
1362 .map(|ty| scope.resolve(ty))
1363 .collect::<Result<Arc<[ResolvedType]>, Error>>()
1364 .with_span(v)?;
1365 Ok(EnumVariantInfo::new(v.name().clone(), payload))
1366 })
1367 .collect::<Result<Arc<[EnumVariantInfo]>, Diagnostic>>()?;
1368 scope
1369 .insert_enum(decl.name().clone(), decl.visibility().clone(), variants)
1370 .with_span(decl)?;
1371
1372 Ok(Self::EnumDeclaration)
1373 }
1374 parse::Item::Module(module) => {
1375 scope
1376 .enter_module(module.name().clone(), module.visibility().clone())
1377 .with_span(module)?;
1378
1379 let mut analyzed_children = Vec::new();
1380 for item in module.items() {
1381 analyzed_children.push(Item::analyze(item, ty, scope)?);
1382 }
1383 scope.exit_module();
1384 Ok(Self::Module(analyzed_children))
1385 }
1386 parse::Item::Ignored => Ok(Self::Ignored),
1387 }
1388 }
1389}
1390
1391impl AbstractSyntaxTree for Function {
1392 type From = parse::Function;
1393
1394 fn analyze(
1395 from: &Self::From,
1396 ty: &ResolvedType,
1397 scope: &mut Scope,
1398 ) -> Result<Self, Diagnostic> {
1399 assert!(ty.is_unit(), "Function definitions cannot return anything");
1400 assert!(
1401 scope.is_outside_function(),
1402 "Variables live only inside the function"
1403 );
1404
1405 if from.name().as_inner() != MAIN_STR {
1406 let params = from
1407 .params()
1408 .iter()
1409 .map(|param| {
1410 let identifier = param.identifier().clone();
1411 let ty = scope.resolve(param.ty())?;
1412 Ok(FunctionParam {
1413 identifier,
1414 ty,
1415 span: *param.span(),
1416 })
1417 })
1418 .collect::<Result<Arc<[FunctionParam]>, Error>>()
1419 .with_span(from)?;
1420 let ret = from
1421 .ret()
1422 .as_ref()
1423 .map(|aliased| scope.resolve(aliased).with_span(from))
1424 .transpose()?
1425 .unwrap_or_else(ResolvedType::unit);
1426
1427 scope.enter_block();
1428 for param in params.iter() {
1429 scope.insert_variable(param.identifier().clone(), param.ty().clone());
1430 }
1431 let body = Expression::analyze(from.body(), &ret, scope).map(Arc::new)?;
1432 scope.exit_block();
1433
1434 debug_assert!(scope.is_outside_function());
1435 let function = CustomFunction {
1436 params,
1437 body,
1438 span: *from.span(),
1439 };
1440 scope
1441 .insert_function(from.name().clone(), from.visibility().clone(), function)
1442 .with_span(from)?;
1443
1444 return Ok(Self::Custom);
1445 }
1446
1447 if !from.params().is_empty() {
1448 return Err(Error::MainNoInputs).with_span(from);
1449 }
1450 if let Some(aliased) = from.ret() {
1451 let resolved = scope.resolve(aliased).with_span(from)?;
1452 if !resolved.is_unit() {
1453 return Err(Error::MainNoOutput).with_span(from);
1454 }
1455 }
1456
1457 if matches!(from.visibility(), Visibility::Public) {
1458 return Err(Error::MainCannotBePublic).with_span(from);
1459 }
1460
1461 scope.enter_main();
1462 let body = Expression::analyze(from.body(), ty, scope)?;
1463 scope.exit_main();
1464 Ok(Self::Main(body))
1465 }
1466}
1467
1468impl AbstractSyntaxTree for Statement {
1469 type From = parse::Statement;
1470
1471 fn analyze(
1472 from: &Self::From,
1473 ty: &ResolvedType,
1474 scope: &mut Scope,
1475 ) -> Result<Self, Diagnostic> {
1476 assert!(ty.is_unit(), "Statements cannot return anything");
1477 match from {
1478 parse::Statement::Assignment(assignment) => {
1479 Assignment::analyze(assignment, ty, scope).map(Self::Assignment)
1480 }
1481 parse::Statement::Expression(expression) => {
1482 Expression::analyze(expression, ty, scope).map(Self::Expression)
1483 }
1484 }
1485 }
1486}
1487
1488impl AbstractSyntaxTree for Assignment {
1489 type From = parse::Assignment;
1490
1491 fn analyze(
1492 from: &Self::From,
1493 ty: &ResolvedType,
1494 scope: &mut Scope,
1495 ) -> Result<Self, Diagnostic> {
1496 assert!(ty.is_unit(), "Assignments cannot return anything");
1497 let ty_expr = scope.resolve(from.ty()).with_span(from)?;
1502 let expression = Expression::analyze(from.expression(), &ty_expr, scope)?;
1503 let typed_variables = from.pattern().is_of_type(&ty_expr).with_span(from)?;
1504 for (identifier, ty) in typed_variables {
1505 scope.insert_variable(identifier, ty);
1506 }
1507
1508 Ok(Self {
1509 pattern: from.pattern().clone(),
1510 expression,
1511 span: *from.as_ref(),
1512 })
1513 }
1514}
1515
1516impl Expression {
1517 pub fn analyze_const(from: &parse::Expression, ty: &ResolvedType) -> Result<Self, Diagnostic> {
1526 let mut empty_scope = Scope::for_value_parsing();
1529 Self::analyze(from, ty, &mut empty_scope)
1530 }
1531}
1532
1533fn analyze_enum_construction(
1541 construction: &parse::EnumConstruction,
1542 ty: &ResolvedType,
1543 scope: &mut Scope,
1544) -> Result<EnumConstruction, Diagnostic> {
1545 let span = *construction.span();
1546 let Some(info) = ty.as_enum() else {
1547 return Err(Error::ExpressionUnexpectedType { ty: ty.clone() }).with_span(span);
1548 };
1549
1550 let written = construction.enum_path_string();
1555 let names_expected_enum = match construction.enum_path() {
1556 [single] => {
1557 let alias = AliasName::from_str_unchecked(single.as_inner());
1558 match scope.get_alias(&alias) {
1559 Ok(resolved) if &resolved == ty => true,
1560 Ok(resolved) => {
1561 return Err(Error::ExpressionTypeMismatch {
1562 expected: ty.clone(),
1563 found: resolved,
1564 })
1565 .with_span(span);
1566 }
1567 Err(_) => scope.unscoped_enum_names && written == info.name(),
1568 }
1569 }
1570 _ => false,
1571 };
1572 if !names_expected_enum {
1573 return Err(Error::Grammar {
1574 msg: format!("`{written}` does not name enum `{}`", info.name()),
1575 })
1576 .with_span(span);
1577 }
1578
1579 let (variant_index, variant) = info
1580 .variant(construction.variant())
1581 .ok_or_else(|| enum_variant_error(construction.variant().as_inner(), info))
1582 .with_span(span)?;
1583 if construction.args().len() != variant.payload().len() {
1584 return Err(Error::Grammar {
1585 msg: format!(
1586 "variant `{}` of enum `{}` carries {} payload value(s), found {}",
1587 construction.variant(),
1588 info.name(),
1589 variant.payload().len(),
1590 construction.args().len()
1591 ),
1592 })
1593 .with_span(span);
1594 }
1595
1596 let payload = construction
1597 .args()
1598 .iter()
1599 .zip(variant.payload())
1600 .map(|(arg, payload_ty)| Expression::analyze(arg, payload_ty, scope).map(Arc::new))
1601 .collect::<Result<Arc<[Arc<Expression>]>, Diagnostic>>()?;
1602
1603 Ok(EnumConstruction {
1604 variant_index,
1605 payload,
1606 span,
1607 })
1608}
1609
1610fn cast_preserves_enum_identity(source: &ResolvedType, target: &ResolvedType) -> bool {
1630 match (source.as_inner(), target.as_inner()) {
1631 (TypeInner::Enum(src), TypeInner::Enum(dst)) => src == dst,
1632 (TypeInner::Enum(_), _) | (_, TypeInner::Enum(_)) => false,
1633 (TypeInner::Option(src), TypeInner::Option(dst)) => cast_preserves_enum_identity(src, dst),
1634 (TypeInner::Either(src_l, src_r), TypeInner::Either(dst_l, dst_r)) => {
1635 cast_preserves_enum_identity(src_l, dst_l) && cast_preserves_enum_identity(src_r, dst_r)
1636 }
1637 (TypeInner::Tuple(src), TypeInner::Tuple(dst)) if src.len() == dst.len() => src
1638 .iter()
1639 .zip(dst.iter())
1640 .all(|(src_el, dst_el)| cast_preserves_enum_identity(src_el, dst_el)),
1641 (TypeInner::Array(src, src_len), TypeInner::Array(dst, dst_len)) if src_len == dst_len => {
1642 cast_preserves_enum_identity(src, dst)
1643 }
1644 (TypeInner::List(src, src_bound), TypeInner::List(dst, dst_bound))
1645 if src_bound == dst_bound =>
1646 {
1647 cast_preserves_enum_identity(src, dst)
1648 }
1649 _ => !source.contains_enum() && !target.contains_enum(),
1652 }
1653}
1654
1655fn enum_variant_error(found: &str, info: &EnumInfo) -> Error {
1657 let variants = info
1658 .variants()
1659 .iter()
1660 .map(|variant| variant.name().to_string())
1661 .collect::<Vec<_>>()
1662 .join(", ");
1663 Error::Grammar {
1664 msg: format!(
1665 "`{found}` is not a variant of enum `{}`; expected one of: {variants}",
1666 info.name()
1667 ),
1668 }
1669}
1670
1671impl AbstractSyntaxTree for Expression {
1672 type From = parse::Expression;
1673
1674 fn analyze(
1675 from: &Self::From,
1676 ty: &ResolvedType,
1677 scope: &mut Scope,
1678 ) -> Result<Self, Diagnostic> {
1679 match from.inner() {
1680 parse::ExpressionInner::Single(single) => {
1681 let ast_single = SingleExpression::analyze(single, ty, scope)?;
1682 Ok(Self {
1683 ty: ty.clone(),
1684 inner: ExpressionInner::Single(ast_single),
1685 span: *from.as_ref(),
1686 })
1687 }
1688 parse::ExpressionInner::Block(statements, expression) => {
1689 scope.enter_block();
1690 let ast_statements = statements
1691 .iter()
1692 .map(|s| Statement::analyze(s, &ResolvedType::unit(), scope))
1693 .collect::<Result<Arc<[Statement]>, Diagnostic>>()?;
1694 let ast_expression = match expression {
1695 Some(expression) => Expression::analyze(expression, ty, scope)
1696 .map(Arc::new)
1697 .map(Some),
1698 None if ty.is_unit() => Ok(None),
1699 None => Err(Error::ExpressionTypeMismatch {
1700 expected: ty.clone(),
1701 found: ResolvedType::unit(),
1702 })
1703 .with_span(from),
1704 }?;
1705 scope.exit_block();
1706
1707 Ok(Self {
1708 ty: ty.clone(),
1709 inner: ExpressionInner::Block(ast_statements, ast_expression),
1710 span: *from.as_ref(),
1711 })
1712 }
1713 }
1714 }
1715}
1716
1717impl AbstractSyntaxTree for SingleExpression {
1718 type From = parse::SingleExpression;
1719
1720 fn analyze(
1721 from: &Self::From,
1722 ty: &ResolvedType,
1723 scope: &mut Scope,
1724 ) -> Result<Self, Diagnostic> {
1725 let inner = match from.inner() {
1726 parse::SingleExpressionInner::Boolean(bit) => {
1727 if !ty.is_boolean() {
1728 return Err(Error::ExpressionTypeMismatch {
1729 expected: ty.clone(),
1730 found: ResolvedType::boolean(),
1731 })
1732 .with_span(from);
1733 }
1734 SingleExpressionInner::Constant(Value::from(*bit))
1735 }
1736 parse::SingleExpressionInner::Decimal(decimal) => {
1737 let ty = ty
1738 .as_integer()
1739 .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() })
1740 .with_span(from)?;
1741 UIntValue::parse_decimal(decimal, ty)
1742 .with_span(from)
1743 .map(Value::from)
1744 .map(SingleExpressionInner::Constant)?
1745 }
1746 parse::SingleExpressionInner::Binary(bits) => {
1747 let ty = ty
1748 .as_integer()
1749 .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() })
1750 .with_span(from)?;
1751 let value = UIntValue::parse_binary(bits, ty).with_span(from)?;
1752 SingleExpressionInner::Constant(Value::from(value))
1753 }
1754 parse::SingleExpressionInner::Hexadecimal(bytes) => {
1755 let value = Value::parse_hexadecimal(bytes, ty).with_span(from)?;
1756 SingleExpressionInner::Constant(value)
1757 }
1758 parse::SingleExpressionInner::Witness(name) => {
1759 scope
1760 .insert_witness(name.clone(), ty.clone())
1761 .with_span(from)?;
1762 SingleExpressionInner::Witness(name.clone())
1763 }
1764 parse::SingleExpressionInner::Parameter(name) => {
1765 scope
1766 .insert_parameter(name.shallow_clone(), ty.clone())
1767 .with_span(from)?;
1768 SingleExpressionInner::Parameter(name.shallow_clone())
1769 }
1770 parse::SingleExpressionInner::Variable(identifier) => {
1771 let bound_ty = scope
1772 .get_variable(identifier)
1773 .ok_or(Error::UndefinedVariable {
1774 identifier: identifier.clone(),
1775 })
1776 .with_span(from)?;
1777 if ty != bound_ty {
1778 return Err(Error::ExpressionTypeMismatch {
1779 expected: ty.clone(),
1780 found: bound_ty.clone(),
1781 })
1782 .with_span(from);
1783 }
1784 scope.insert_variable(identifier.clone(), ty.clone());
1785 SingleExpressionInner::Variable(identifier.clone())
1786 }
1787 parse::SingleExpressionInner::Expression(parse) => {
1788 Expression::analyze(parse, ty, scope)
1789 .map(Arc::new)
1790 .map(SingleExpressionInner::Expression)?
1791 }
1792 parse::SingleExpressionInner::Tuple(tuple) => {
1793 let types = ty
1794 .as_tuple()
1795 .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() })
1796 .with_span(from)?;
1797 if tuple.len() != types.len() {
1798 return Err(Error::ExpressionUnexpectedType { ty: ty.clone() }).with_span(from);
1799 }
1800 tuple
1801 .iter()
1802 .zip(types.iter())
1803 .map(|(el_parse, el_ty)| Expression::analyze(el_parse, el_ty, scope))
1804 .collect::<Result<Arc<[Expression]>, Diagnostic>>()
1805 .map(SingleExpressionInner::Tuple)?
1806 }
1807 parse::SingleExpressionInner::Array(array) => {
1808 let (el_ty, size) = ty
1809 .as_array()
1810 .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() })
1811 .with_span(from)?;
1812 if array.len() != size {
1813 return Err(Error::ExpressionUnexpectedType { ty: ty.clone() }).with_span(from);
1814 }
1815 array
1816 .iter()
1817 .map(|el_parse| Expression::analyze(el_parse, el_ty, scope))
1818 .collect::<Result<Arc<[Expression]>, Diagnostic>>()
1819 .map(SingleExpressionInner::Array)?
1820 }
1821 parse::SingleExpressionInner::List(list) => {
1822 let (el_ty, bound) = ty
1823 .as_list()
1824 .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() })
1825 .with_span(from)?;
1826 if bound.get() <= list.len() {
1827 return Err(Error::ExpressionUnexpectedType { ty: ty.clone() }).with_span(from);
1828 }
1829 list.iter()
1830 .map(|e| Expression::analyze(e, el_ty, scope))
1831 .collect::<Result<Arc<[Expression]>, Diagnostic>>()
1832 .map(SingleExpressionInner::List)?
1833 }
1834 parse::SingleExpressionInner::Either(either) => {
1835 let (ty_l, ty_r) = ty
1836 .as_either()
1837 .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() })
1838 .with_span(from)?;
1839 match either {
1840 Either::Left(parse_l) => Expression::analyze(parse_l, ty_l, scope)
1841 .map(Arc::new)
1842 .map(Either::Left),
1843 Either::Right(parse_r) => Expression::analyze(parse_r, ty_r, scope)
1844 .map(Arc::new)
1845 .map(Either::Right),
1846 }
1847 .map(SingleExpressionInner::Either)?
1848 }
1849 parse::SingleExpressionInner::Option(maybe_parse) => {
1850 let ty = ty
1851 .as_option()
1852 .ok_or(Error::ExpressionUnexpectedType { ty: ty.clone() })
1853 .with_span(from)?;
1854 match maybe_parse {
1855 Some(parse) => {
1856 Some(Expression::analyze(parse, ty, scope).map(Arc::new)).transpose()
1857 }
1858 None => Ok(None),
1859 }
1860 .map(SingleExpressionInner::Option)?
1861 }
1862 parse::SingleExpressionInner::Call(call) => {
1863 Call::analyze(call, ty, scope).map(SingleExpressionInner::Call)?
1864 }
1865 parse::SingleExpressionInner::Match(match_) => {
1866 Match::analyze(match_, ty, scope).map(SingleExpressionInner::Match)?
1867 }
1868 parse::SingleExpressionInner::EnumConstruction(construction) => {
1869 analyze_enum_construction(construction, ty, scope)
1870 .map(SingleExpressionInner::EnumConstruction)?
1871 }
1872 parse::SingleExpressionInner::EnumMatch(enum_match) => {
1873 EnumMatch::analyze(enum_match, ty, scope).map(SingleExpressionInner::EnumMatch)?
1874 }
1875 };
1876
1877 Ok(Self {
1878 inner,
1879 ty: ty.clone(),
1880 span: *from.as_ref(),
1881 })
1882 }
1883}
1884
1885impl AbstractSyntaxTree for EnumMatch {
1886 type From = parse::EnumMatch;
1887
1888 fn analyze(
1889 from: &Self::From,
1890 ty: &ResolvedType,
1891 scope: &mut Scope,
1892 ) -> Result<Self, Diagnostic> {
1893 let arms = from.arms();
1894 let span = *from.span();
1895 debug_assert!(!arms.is_empty(), "the parser rejects empty enum matches");
1896
1897 let enum_name = arms[0].enum_path_string();
1898 let [single] = arms[0].enum_path() else {
1899 return Err(Error::Grammar {
1900 msg: format!(
1901 "`{enum_name}` does not name an enum; enums are declared at the \
1902 top level, so match arms name them by a single identifier"
1903 ),
1904 })
1905 .with_span(span);
1906 };
1907 let alias = AliasName::from_str_unchecked(single.as_inner());
1908 let enum_ty = scope.get_alias(&alias).with_span(span)?;
1909 let info = match enum_ty.as_enum() {
1910 Some(info) => info.clone(),
1911 None => {
1912 return Err(Error::Grammar {
1913 msg: format!(
1914 "`{enum_name}` is not an enum, so match arms of the form \
1915 `{enum_name}::Variant` cannot apply to it"
1916 ),
1917 })
1918 .with_span(span)
1919 }
1920 };
1921
1922 let mut arms_by_index: Vec<Option<&parse::EnumMatchArm>> =
1925 vec![None; info.variants().len()];
1926 for arm in arms {
1927 if arm.enum_path() != arms[0].enum_path() {
1928 return Err(Error::Grammar {
1929 msg: format!(
1930 "all match arms must use the same enum; expected '{}', found '{}'",
1931 enum_name,
1932 arm.enum_path_string()
1933 ),
1934 })
1935 .with_span(span);
1936 }
1937 let (index, _) = info
1938 .variant(arm.variant())
1939 .ok_or_else(|| Error::Grammar {
1940 msg: format!(
1941 "variant '{}' is not defined in enum '{}'",
1942 arm.variant(),
1943 enum_name
1944 ),
1945 })
1946 .with_span(span)?;
1947 let slot = &mut arms_by_index[index];
1948 if slot.is_some() {
1949 return Err(Error::Grammar {
1950 msg: format!("duplicate arm for variant '{}'", arm.variant()),
1951 })
1952 .with_span(span);
1953 }
1954 *slot = Some(arm);
1955 }
1956
1957 let covered: Option<Vec<&parse::EnumMatchArm>> = arms_by_index.iter().copied().collect();
1959 let Some(covered) = covered else {
1960 let missing: Vec<String> = arms_by_index
1961 .iter()
1962 .zip(info.variants())
1963 .filter(|(slot, _)| slot.is_none())
1964 .map(|(_, variant)| format!("'{}'", variant.name()))
1965 .collect();
1966 return Err(Error::Grammar {
1967 msg: format!(
1968 "enum match on '{}' must cover all {} variants; missing: {}",
1969 enum_name,
1970 info.variants().len(),
1971 missing.join(", ")
1972 ),
1973 })
1974 .with_span(span);
1975 };
1976
1977 let scrutinee = Expression::analyze(from.scrutinee(), &enum_ty, scope).map(Arc::new)?;
1981
1982 let arm_asts = covered
1983 .into_iter()
1984 .zip(info.variants())
1985 .map(|(arm, variant)| {
1986 let arm_span = *arm.span();
1987 let pattern = analyze_enum_arm_bindings(arm, variant, scope, arm_span)?;
1988 scope.enter_block();
1989 let payload_ty = variant.payload_type();
1990 let typed_variables = pattern.is_of_type(payload_ty).with_span(arm_span)?;
1991 for (identifier, variable_ty) in typed_variables {
1992 scope.insert_variable(identifier, variable_ty);
1993 }
1994 let body = Expression::analyze(arm.expression(), ty, scope).map(Arc::new);
1995 scope.exit_block();
1996 Ok(EnumMatchArm {
1997 pattern,
1998 body: body?,
1999 span: arm_span,
2000 })
2001 })
2002 .collect::<Result<Arc<[EnumMatchArm]>, Diagnostic>>()?;
2003
2004 Ok(Self {
2005 scrutinee,
2006 arms: arm_asts,
2007 span,
2008 })
2009 }
2010}
2011
2012fn analyze_enum_arm_bindings(
2019 arm: &parse::EnumMatchArm,
2020 variant: &EnumVariantInfo,
2021 scope: &Scope,
2022 span: Span,
2023) -> Result<Pattern, Diagnostic> {
2024 if arm.bindings().len() != variant.payload().len() {
2025 return Err(Error::Grammar {
2026 msg: format!(
2027 "variant '{}' of enum '{}' carries {} payload value(s), \
2028 but the arm binds {}",
2029 arm.variant(),
2030 arm.enum_path_string(),
2031 variant.payload().len(),
2032 arm.bindings().len()
2033 ),
2034 })
2035 .with_span(span);
2036 }
2037
2038 let mut patterns = Vec::with_capacity(arm.bindings().len());
2039 for ((pattern, declared), payload_ty) in arm.bindings().iter().zip(variant.payload()) {
2040 let declared = scope.resolve(declared).with_span(span)?;
2041 if &declared != payload_ty {
2042 return Err(Error::ExpressionTypeMismatch {
2043 expected: payload_ty.clone(),
2044 found: declared,
2045 })
2046 .with_span(span);
2047 }
2048 patterns.push(pattern.clone());
2049 }
2050
2051 let pattern = match patterns.len() {
2052 0 => Pattern::Ignore,
2053 1 => patterns[0].clone(),
2054 _ => Pattern::tuple(patterns),
2055 };
2056 Ok(pattern)
2057}
2058
2059impl AbstractSyntaxTree for Call {
2060 type From = parse::Call;
2061
2062 fn analyze(
2063 from: &Self::From,
2064 ty: &ResolvedType,
2065 scope: &mut Scope,
2066 ) -> Result<Self, Diagnostic> {
2067 fn check_argument_types(
2068 parse_args: &[parse::Expression],
2069 expected_tys: &[ResolvedType],
2070 ) -> Result<(), Error> {
2071 if parse_args.len() == expected_tys.len() {
2072 Ok(())
2073 } else {
2074 Err(Error::InvalidNumberOfArguments {
2075 expected: expected_tys.len(),
2076 found: parse_args.len(),
2077 })
2078 }
2079 }
2080
2081 fn check_output_type(
2082 observed_ty: &ResolvedType,
2083 expected_ty: &ResolvedType,
2084 ) -> Result<(), Error> {
2085 if observed_ty == expected_ty {
2086 Ok(())
2087 } else {
2088 Err(Error::ExpressionTypeMismatch {
2089 expected: expected_ty.clone(),
2090 found: observed_ty.clone(),
2091 })
2092 }
2093 }
2094
2095 fn analyze_arguments(
2096 parse_args: &[parse::Expression],
2097 args_tys: &[ResolvedType],
2098 scope: &mut Scope,
2099 ) -> Result<Arc<[Expression]>, Diagnostic> {
2100 let args = parse_args
2101 .iter()
2102 .zip(args_tys.iter())
2103 .map(|(arg_parse, arg_ty)| Expression::analyze(arg_parse, arg_ty, scope))
2104 .collect::<Result<Arc<[Expression]>, Diagnostic>>()?;
2105 Ok(args)
2106 }
2107
2108 let name = CallName::analyze(from, ty, scope)?;
2109 let args = match name.clone() {
2110 CallName::Jet(jet) => {
2111 let args_tys = source_type(&*jet)
2112 .iter()
2113 .map(AliasedType::resolve_builtin)
2114 .collect::<Result<Vec<ResolvedType>, AliasName>>()
2115 .map_err(|alias| Error::UndefinedAlias { name: alias })
2116 .with_span(from)?;
2117 check_argument_types(from.args(), &args_tys).with_span(from)?;
2118 let out_ty = target_type(&*jet)
2119 .resolve_builtin()
2120 .map_err(|alias| Error::UndefinedAlias { name: alias })
2121 .with_span(from)?;
2122 check_output_type(&out_ty, ty).with_span(from)?;
2123 scope.track_call(from, TrackedCallName::Jet);
2124 analyze_arguments(from.args(), &args_tys, scope)?
2125 }
2126 CallName::UnwrapLeft(right_ty) => {
2127 let args_tys = [ResolvedType::either(ty.clone(), right_ty)];
2128 check_argument_types(from.args(), &args_tys).with_span(from)?;
2129 let args = analyze_arguments(from.args(), &args_tys, scope)?;
2130 let [arg_ty] = args_tys;
2131 scope.track_call(from, TrackedCallName::UnwrapLeft(arg_ty));
2132 args
2133 }
2134 CallName::UnwrapRight(left_ty) => {
2135 let args_tys = [ResolvedType::either(left_ty, ty.clone())];
2136 check_argument_types(from.args(), &args_tys).with_span(from)?;
2137 let args = analyze_arguments(from.args(), &args_tys, scope)?;
2138 let [arg_ty] = args_tys;
2139 scope.track_call(from, TrackedCallName::UnwrapRight(arg_ty));
2140 args
2141 }
2142 CallName::IsNone(some_ty) => {
2143 let args_tys = [ResolvedType::option(some_ty)];
2144 check_argument_types(from.args(), &args_tys).with_span(from)?;
2145 let out_ty = ResolvedType::boolean();
2146 check_output_type(&out_ty, ty).with_span(from)?;
2147 analyze_arguments(from.args(), &args_tys, scope)?
2148 }
2149 CallName::Unwrap => {
2150 let args_tys = [ResolvedType::option(ty.clone())];
2151 check_argument_types(from.args(), &args_tys).with_span(from)?;
2152 scope.track_call(from, TrackedCallName::Unwrap);
2153 analyze_arguments(from.args(), &args_tys, scope)?
2154 }
2155 CallName::Assert => {
2156 let args_tys = [ResolvedType::boolean()];
2157 check_argument_types(from.args(), &args_tys).with_span(from)?;
2158 let out_ty = ResolvedType::unit();
2159 check_output_type(&out_ty, ty).with_span(from)?;
2160 scope.track_call(from, TrackedCallName::Assert);
2161 analyze_arguments(from.args(), &args_tys, scope)?
2162 }
2163 CallName::Panic => {
2164 let args_tys = [];
2165 check_argument_types(from.args(), &args_tys).with_span(from)?;
2166 scope.track_call(from, TrackedCallName::Panic);
2168 analyze_arguments(from.args(), &args_tys, scope)?
2169 }
2170 CallName::Debug => {
2171 let args_tys = [ty.clone()];
2172 check_argument_types(from.args(), &args_tys).with_span(from)?;
2173 let args = analyze_arguments(from.args(), &args_tys, scope)?;
2174 let [arg_ty] = args_tys;
2175 scope.track_call(from, TrackedCallName::Debug(arg_ty));
2176 args
2177 }
2178 CallName::TypeCast(source) => {
2179 if !cast_preserves_enum_identity(&source, ty)
2184 || StructuralType::from(&source) != StructuralType::from(ty)
2185 {
2186 return Err(Error::InvalidCast {
2187 source,
2188 target: ty.clone(),
2189 })
2190 .with_span(from);
2191 }
2192
2193 let args_tys = [source];
2194 check_argument_types(from.args(), &args_tys).with_span(from)?;
2195 analyze_arguments(from.args(), &args_tys, scope)?
2196 }
2197 CallName::Custom(function) => {
2198 let args_ty = function
2199 .params()
2200 .iter()
2201 .map(FunctionParam::ty)
2202 .cloned()
2203 .collect::<Vec<ResolvedType>>();
2204 check_argument_types(from.args(), &args_ty).with_span(from)?;
2205 let out_ty = function.body().ty();
2206 check_output_type(out_ty, ty).with_span(from)?;
2207 analyze_arguments(from.args(), &args_ty, scope)?
2208 }
2209 CallName::Fold(function, bound) => {
2210 let element_ty = function.params().first().expect("foldable function").ty();
2215 let list_ty = ResolvedType::list(element_ty.clone(), bound);
2216 let accumulator_ty = function
2217 .params()
2218 .get(1)
2219 .expect("foldable function")
2220 .ty()
2221 .clone();
2222 let args_ty = [list_ty, accumulator_ty];
2223
2224 check_argument_types(from.args(), &args_ty).with_span(from)?;
2225 let out_ty = function.body().ty();
2226 check_output_type(out_ty, ty).with_span(from)?;
2227 analyze_arguments(from.args(), &args_ty, scope)?
2228 }
2229 CallName::ArrayFold(function, size) => {
2230 let element_ty = function.params().first().expect("foldable function").ty();
2235 let array_ty = ResolvedType::array(element_ty.clone(), size.get());
2236 let accumulator_ty = function
2237 .params()
2238 .get(1)
2239 .expect("foldable function")
2240 .ty()
2241 .clone();
2242 let args_ty = [array_ty, accumulator_ty];
2243
2244 check_argument_types(from.args(), &args_ty).with_span(from)?;
2245 let out_ty = function.body().ty();
2246 check_output_type(out_ty, ty).with_span(from)?;
2247 analyze_arguments(from.args(), &args_ty, scope)?
2248 }
2249 CallName::ForWhile(function, _bit_width) => {
2250 let accumulator_ty = function
2256 .params()
2257 .first()
2258 .expect("loopable function")
2259 .ty()
2260 .clone();
2261 let context_ty = function
2262 .params()
2263 .get(1)
2264 .expect("loopable function")
2265 .ty()
2266 .clone();
2267 let args_ty = [accumulator_ty, context_ty];
2268
2269 check_argument_types(from.args(), &args_ty).with_span(from)?;
2270 let out_ty = function.body().ty();
2271 check_output_type(out_ty, ty).with_span(from)?;
2272 analyze_arguments(from.args(), &args_ty, scope)?
2273 }
2274 };
2275
2276 Ok(Self {
2277 name,
2278 args,
2279 span: *from.as_ref(),
2280 })
2281 }
2282}
2283
2284impl AbstractSyntaxTree for CallName {
2285 type From = parse::Call;
2287
2288 fn analyze(
2289 from: &Self::From,
2290 _ty: &ResolvedType,
2291 scope: &mut Scope,
2292 ) -> Result<Self, Diagnostic> {
2293 match from.name() {
2294 parse::CallName::Jet(name) => match scope.jet_hinter.parse_jet(name.as_inner()) {
2295 Some(jet) if !jet.is_disabled() => Ok(Self::Jet(jet)),
2296 _ => Err(Error::JetDoesNotExist { name: name.clone() }).with_span(from),
2297 },
2298 parse::CallName::UnwrapLeft(right_ty) => scope
2299 .resolve(right_ty)
2300 .map(Self::UnwrapLeft)
2301 .with_span(from),
2302 parse::CallName::UnwrapRight(left_ty) => scope
2303 .resolve(left_ty)
2304 .map(Self::UnwrapRight)
2305 .with_span(from),
2306 parse::CallName::IsNone(some_ty) => {
2307 scope.resolve(some_ty).map(Self::IsNone).with_span(from)
2308 }
2309 parse::CallName::Unwrap => Ok(Self::Unwrap),
2310 parse::CallName::Assert => Ok(Self::Assert),
2311 parse::CallName::Panic => Ok(Self::Panic),
2312 parse::CallName::Debug => Ok(Self::Debug),
2313 parse::CallName::TypeCast(target) => {
2314 scope.resolve(target).map(Self::TypeCast).with_span(from)
2315 }
2316 parse::CallName::Custom(name) => {
2317 scope.get_function(name).map(Self::Custom).with_span(from)
2318 }
2319 parse::CallName::ArrayFold(name, size) => {
2320 let function = scope.get_function(name).with_span(from)?;
2321 if function.params().len() != 2 || function.params()[1].ty() != function.body().ty()
2324 {
2325 Err(Error::FunctionNotFoldable { name: name.clone() }).with_span(from)
2326 } else {
2327 Ok(Self::ArrayFold(function, *size))
2328 }
2329 }
2330 parse::CallName::Fold(name, bound) => {
2331 let function = scope.get_function(name).with_span(from)?;
2332 if function.params().len() != 2 || function.params()[1].ty() != function.body().ty()
2335 {
2336 Err(Error::FunctionNotFoldable { name: name.clone() }).with_span(from)
2337 } else {
2338 Ok(Self::Fold(function, *bound))
2339 }
2340 }
2341 parse::CallName::ForWhile(name) => {
2342 let function = scope.get_function(name).with_span(from)?;
2343 if function.params().len() != 3 {
2348 return Err(Error::FunctionNotLoopable { name: name.clone() }).with_span(from);
2349 }
2350 match function.body().ty().as_either() {
2351 Some((_, out_r)) if out_r == function.params().first().unwrap().ty() => {}
2352 _ => {
2353 return Err(Error::FunctionNotLoopable { name: name.clone() })
2354 .with_span(from);
2355 }
2356 }
2357 match function.params().get(2).unwrap().ty().as_integer() {
2361 Some(
2362 int_ty @ (UIntType::U1
2363 | UIntType::U2
2364 | UIntType::U4
2365 | UIntType::U8
2366 | UIntType::U16),
2367 ) => Ok(Self::ForWhile(function, int_ty.bit_width())),
2368 _ => Err(Error::FunctionNotLoopable { name: name.clone() }).with_span(from),
2369 }
2370 }
2371 }
2372 }
2373}
2374
2375impl AbstractSyntaxTree for Match {
2376 type From = parse::Match;
2377
2378 fn analyze(
2379 from: &Self::From,
2380 ty: &ResolvedType,
2381 scope: &mut Scope,
2382 ) -> Result<Self, Diagnostic> {
2383 let scrutinee_ty = from.scrutinee_type();
2384 let scrutinee_ty = scope.resolve(&scrutinee_ty).with_span(from)?;
2385 let scrutinee =
2386 Expression::analyze(from.scrutinee(), &scrutinee_ty, scope).map(Arc::new)?;
2387
2388 scope.enter_block();
2389 if let Some((pat_l, ty_l)) = from.left().pattern().as_typed_pattern() {
2390 let ty_l = scope.resolve(ty_l).with_span(from.left())?;
2391 let typed_variables = pat_l.is_of_type(&ty_l).with_span(from.left())?;
2392 for (identifier, ty) in typed_variables {
2393 scope.insert_variable(identifier, ty);
2394 }
2395 }
2396 let ast_l = Expression::analyze(from.left().expression(), ty, scope).map(Arc::new)?;
2397 scope.exit_block();
2398 scope.enter_block();
2399 if let Some((pat_r, ty_r)) = from.right().pattern().as_typed_pattern() {
2400 let ty_r = scope.resolve(ty_r).with_span(from.right())?;
2401 let typed_variables = pat_r.is_of_type(&ty_r).with_span(from.right())?;
2402 for (identifier, ty) in typed_variables {
2403 scope.insert_variable(identifier, ty);
2404 }
2405 }
2406 let ast_r = Expression::analyze(from.right().expression(), ty, scope).map(Arc::new)?;
2407 scope.exit_block();
2408
2409 Ok(Self {
2410 scrutinee,
2411 left: MatchArm {
2412 pattern: from.left().pattern().clone(),
2413 expression: ast_l,
2414 span: *from.left().span(),
2415 },
2416 right: MatchArm {
2417 pattern: from.right().pattern().clone(),
2418 expression: ast_r,
2419 span: *from.right().span(),
2420 },
2421 span: *from.as_ref(),
2422 })
2423 }
2424}
2425
2426impl AsRef<Span> for Assignment {
2427 fn as_ref(&self) -> &Span {
2428 &self.span
2429 }
2430}
2431
2432impl AsRef<Span> for FunctionParam {
2433 fn as_ref(&self) -> &Span {
2434 &self.span
2435 }
2436}
2437
2438impl AsRef<Span> for CustomFunction {
2439 fn as_ref(&self) -> &Span {
2440 &self.span
2441 }
2442}
2443
2444impl AsRef<Span> for Expression {
2445 fn as_ref(&self) -> &Span {
2446 &self.span
2447 }
2448}
2449
2450impl AsRef<Span> for SingleExpression {
2451 fn as_ref(&self) -> &Span {
2452 &self.span
2453 }
2454}
2455
2456impl AsRef<Span> for Call {
2457 fn as_ref(&self) -> &Span {
2458 &self.span
2459 }
2460}
2461
2462impl AsRef<Span> for Match {
2463 fn as_ref(&self) -> &Span {
2464 &self.span
2465 }
2466}
2467
2468impl AsRef<Span> for MatchArm {
2469 fn as_ref(&self) -> &Span {
2470 &self.span
2471 }
2472}
2473
2474impl AsRef<Span> for EnumMatchArm {
2475 fn as_ref(&self) -> &Span {
2476 &self.span
2477 }
2478}
2479
2480#[cfg(test)]
2481mod span_tests {
2482 use crate::parse::ParseFromStr;
2483
2484 use super::*;
2485
2486 #[test]
2487 fn analyzed_custom_function_preserves_declaration_and_parameter_spans() {
2488 let source = "fn helper(value: u8) -> u8 { value }";
2489 let parsed = parse::Function::parse_from_str(source).expect("function parses");
2490 let mut scope = Scope::new(Box::new(ElementsJetHinter));
2491
2492 Function::analyze(&parsed, &ResolvedType::unit(), &mut scope).expect("function analyzes");
2493 let function = scope
2494 .get_function(parsed.name())
2495 .expect("function is registered in scope");
2496
2497 assert_eq!(function.span().to_slice(source), Some(source));
2498 assert_eq!(
2499 function.params()[0].span().to_slice(source),
2500 Some("value: u8")
2501 );
2502 }
2503
2504 #[test]
2505 fn analyzed_match_arms_preserve_their_parsed_spans() {
2506 let source = r#"fn main() {
2507 let input: Either<u8, u8> = Left(1);
2508 match input {
2509 Left(left: u8) => {},
2510 Right(right: u8) => {},
2511 }
2512}"#;
2513 let parsed = parse::Program::parse_from_str(source).expect("program parses");
2514 let program =
2515 Program::analyze(&parsed, Box::new(ElementsJetHinter)).expect("program analyzes");
2516
2517 let ExpressionInner::Block(_, Some(last)) = program.main().inner() else {
2518 panic!("main body should end in a match");
2519 };
2520 let ExpressionInner::Single(single) = last.inner() else {
2521 panic!("match should be a single expression");
2522 };
2523 let SingleExpressionInner::Match(match_) = single.inner() else {
2524 panic!("expected a binary match");
2525 };
2526
2527 assert_eq!(
2528 match_.left().span().to_slice(source),
2529 Some("Left(left: u8) => {},")
2530 );
2531 assert_eq!(
2532 match_.right().span().to_slice(source),
2533 Some("Right(right: u8) => {},")
2534 );
2535 }
2536
2537 #[test]
2538 fn analyzed_enum_match_arms_preserve_their_parsed_spans() {
2539 let source = r#"enum Choice { First, Second, }
2540fn main() {
2541 let input: Choice = Choice::First;
2542 match input {
2543 Choice::First => {},
2544 Choice::Second => {},
2545 }
2546}"#;
2547 let parsed = parse::Program::parse_from_str(source).expect("program parses");
2548 let program =
2549 Program::analyze(&parsed, Box::new(ElementsJetHinter)).expect("program analyzes");
2550
2551 let ExpressionInner::Block(_, Some(last)) = program.main().inner() else {
2552 panic!("main body should end in an enum match");
2553 };
2554 let ExpressionInner::Single(single) = last.inner() else {
2555 panic!("enum match should be a single expression");
2556 };
2557 let SingleExpressionInner::EnumMatch(match_) = single.inner() else {
2558 panic!("expected an enum match");
2559 };
2560
2561 assert_eq!(
2562 match_.arms()[0].span().to_slice(source),
2563 Some("Choice::First => {},")
2564 );
2565 assert_eq!(
2566 match_.arms()[1].span().to_slice(source),
2567 Some("Choice::Second => {},")
2568 );
2569 }
2570}
2571
2572#[cfg(test)]
2573mod scope_resolution_tests {
2574 use super::{ElementsJetHinter, Program};
2575 use crate::driver::tests::setup_graph;
2576
2577 pub(super) fn analyze_multifile(files: Vec<(&str, &str)>) -> Result<(), String> {
2578 let (graph, _ids, _dir, mut diagnostics) = setup_graph(files);
2579
2580 let Some(driver_program) = graph.linearize_and_assemble(&mut diagnostics) else {
2581 return Err(diagnostics.render_to_string());
2582 };
2583
2584 Program::analyze(&driver_program, Box::new(ElementsJetHinter))
2585 .map(|_| ())
2586 .map_err(|e| e.to_string())
2587 }
2588
2589 #[test]
2590 fn private_type_alias_from_dependency_does_not_leak() {
2591 let result = analyze_multifile(vec![
2592 (
2593 "main.simf",
2594 "use lib::A::helper; fn main() { helper(); let x: Secret = 0; }",
2595 ),
2596 ("libs/lib/A.simf", "type Secret = u32; pub fn helper() {}"),
2597 ]);
2598
2599 assert!(
2600 result.is_err(),
2601 "private alias from another file leaked into root scope: {result:?}"
2602 );
2603 }
2604
2605 #[test]
2606 fn same_alias_name_in_different_modules_does_not_conflict_if_only_one_is_imported() {
2607 let result = analyze_multifile(vec![
2608 (
2609 "main.simf",
2610 "use lib::A::Word; use lib::B::id; fn main() { let x: Word = 0; assert!(jet::is_zero_32(id(x))); }",
2611 ),
2612 ("libs/lib/A.simf", "pub type Word = u32;"),
2613 ("libs/lib/B.simf", "pub type Word = u16; pub fn id(x: u32) -> u32 { x }"),
2614 ]);
2615
2616 assert!(
2617 result.is_ok(),
2618 "unimported alias from another module should not collide: {result:?}"
2619 );
2620 }
2621
2622 #[test]
2623 fn main_must_be_defined_once_per_project() {
2624 let result = analyze_multifile(vec![
2625 ("main.simf", "use lib::A::helper; fn main() { helper(); }"),
2626 ("libs/lib/A.simf", "fn main() {} pub fn helper() {}"),
2627 ]);
2628
2629 assert!(
2630 result.is_err(),
2631 "Main function must be inside an entry file: {result:?}"
2632 );
2633 }
2634
2635 #[test]
2636 fn test_local_definitions_visibility() {
2637 let result = analyze_multifile(vec![(
2640 "main.simf",
2641 "fn private_fn() {} pub fn public_fn() {} fn main() { private_fn(); public_fn(); }",
2642 )]);
2643
2644 assert!(
2645 result.is_ok(),
2646 "Local definitions should be visible: {result:?}"
2647 );
2648 }
2649
2650 #[test]
2651 fn test_pub_use_propagation() {
2652 let result = analyze_multifile(vec![
2654 ("libs/lib/A.simf", "pub fn foo() {}"),
2655 ("libs/lib/B.simf", "pub use crate::A::foo;"),
2656 ("main.simf", "use lib::B::foo; fn main() { foo(); }"),
2657 ]);
2658
2659 assert!(
2660 result.is_ok(),
2661 "Public re-exports must be visible: {result:?}"
2662 );
2663 }
2664
2665 #[test]
2666 fn test_private_import_encapsulation_error() {
2667 let result = analyze_multifile(vec![
2669 ("libs/lib/A.simf", "pub fn foo() {}"),
2670 ("libs/lib/B.simf", "use crate::A::foo;"), ("main.simf", "use lib::B::foo; fn main() {}"),
2672 ]);
2673
2674 let err = result.expect_err("Private imports should not be accessible");
2675 assert!(err.contains("private") || err.contains("foo"));
2676 }
2677
2678 #[test]
2679 fn test_separated_type_aliases_and_functions() {
2680 let result = analyze_multifile(vec![
2681 ("libs/lib/A.simf", "pub type bar = u32; pub fn bar() {}"),
2682 (
2683 "main.simf",
2684 "use lib::A::bar; fn main() { bar(); let x: bar = 0; }",
2685 ),
2686 ]);
2687
2688 assert!(
2689 result.is_ok(),
2690 "AST should support separate namespaces for types and functions: {result:?}"
2691 );
2692 }
2693
2694 #[test]
2695 fn test_public_main_is_forbidden() {
2696 let result = analyze_multifile(vec![("main.simf", "pub fn main() {}")]);
2697
2698 let err = result.expect_err("Public main should be rejected");
2699 assert!(err.contains("Main") && err.contains("public"));
2700 }
2701
2702 #[test]
2703 fn test_aliasing_to_main_is_forbidden() {
2704 let result = analyze_multifile(vec![
2705 ("libs/lib/A.simf", "pub type bar = u32;"),
2706 ("main.simf", "use lib::A::bar as main; fn main() {}"),
2707 ]);
2708
2709 let err = result.expect_err("Aliasing to main should be rejected");
2710 assert!(err.contains("Main") && err.contains("alias"));
2711 }
2712
2713 #[test]
2714 fn test_renaming_with_use() {
2715 let result = analyze_multifile(vec![
2717 ("libs/lib/A.simf", "pub fn foo() {}"),
2718 (
2719 "main.simf",
2720 "use lib::A::foo as bar; fn main() { bar(); foo(); }",
2721 ),
2722 ]);
2723
2724 let err = result.expect_err("Using the original unaliased name 'foo' should fail");
2725 assert!(err.contains("foo") && (err.contains("not defined") || err.contains("unresolved")));
2726 }
2727
2728 #[test]
2729 fn test_multiple_aliases_in_list() {
2730 let result = analyze_multifile(vec![
2731 ("libs/lib/A.simf", "pub fn foo() {} pub fn baz() {}"),
2732 (
2733 "main.simf",
2734 "use lib::A::{foo as bar, baz as qux}; fn main() { bar(); qux(); }",
2735 ),
2736 ]);
2737
2738 assert!(
2739 result.is_ok(),
2740 "List aliases should be resolvable: {result:?}"
2741 );
2742 }
2743
2744 #[test]
2745 fn test_alias_private_item_fails() {
2746 let result = analyze_multifile(vec![
2747 ("libs/lib/A.simf", "fn secret() {}"),
2748 ("main.simf", "use lib::A::secret as my_secret; fn main() {}"),
2749 ]);
2750
2751 let err = result.expect_err("Aliasing a private item should fail");
2752 assert!(err.contains("secret") && err.contains("private"));
2753 }
2754
2755 #[test]
2756 fn test_deep_reexport_with_aliases() {
2757 let result = analyze_multifile(vec![
2758 ("libs/lib/A.simf", "pub fn original() {}"),
2759 ("libs/lib/B.simf", "pub use crate::A::original as middle;"),
2760 (
2761 "main.simf",
2762 "use lib::B::middle as final_name; fn main() { final_name(); }",
2763 ),
2764 ]);
2765
2766 assert!(
2767 result.is_ok(),
2768 "Deep alias re-exports should work: {result:?}"
2769 );
2770 }
2771
2772 #[test]
2773 fn test_deep_reexport_private_link_fails() {
2774 let result = analyze_multifile(vec![
2775 ("libs/lib/A.simf", "pub fn target() {}"),
2776 ("libs/lib/B.simf", "use crate::A::target as hidden_alias;"),
2777 ("main.simf", "use lib::B::hidden_alias; fn main() {}"),
2778 ]);
2779
2780 let err = result.expect_err("Private intermediate aliases should block resolution");
2781 assert!(err.contains("hidden_alias") && err.contains("private"));
2782 }
2783
2784 #[test]
2785 fn test_plain_import_and_alias_to_same_name_is_rejected() {
2786 let result = analyze_multifile(vec![
2787 ("libs/lib/A.simf", "pub fn foo() {}"),
2788 ("libs/lib/B.simf", "pub fn foo() {}"),
2789 (
2790 "main.simf",
2791 "use lib::A::foo; use lib::B::foo as foo; fn main() {}",
2792 ),
2793 ]);
2794
2795 let err = result.expect_err("Duplicate names in scope should fail");
2796 assert!(err.contains("foo") && err.contains("multiple times"));
2797 }
2798
2799 #[test]
2800 fn test_alias_cannot_reuse_local_definition_name() {
2801 let result = analyze_multifile(vec![
2802 ("libs/lib/A.simf", "pub fn bar() {}"),
2803 (
2804 "main.simf",
2805 "pub fn foo() {} use lib::A::bar as foo; fn main() {}",
2806 ),
2807 ]);
2808
2809 let err = result.expect_err("Alias reusing a local name should fail");
2810 assert!(err.contains("foo") && err.contains("multiple times"));
2811 }
2812
2813 #[test]
2814 #[ignore = "Pending better error handler:private item errors currently mask duplicate imports"]
2815 fn test_private_alias_error_does_not_mask_duplicate_function_import() {
2816 let result = analyze_multifile(vec![
2819 ("libs/lib/A.simf", "pub fn foo() {}"),
2820 ("libs/lib/B.simf", "pub fn foo() {} type foo = u32;"),
2821 (
2822 "main.simf",
2823 "use lib::A::foo; use lib::B::foo; fn main() {}",
2824 ),
2825 ]);
2826
2827 let err = result.expect_err("Duplicate function import should fail");
2828
2829 assert!(err.contains("foo") && err.contains("multiple times"));
2832 }
2833
2834 #[test]
2835 fn test_failed_alias_import_does_not_poison_following_imports() {
2836 let result = analyze_multifile(vec![
2837 ("libs/lib/A.simf", "pub fn nope() {}"),
2838 ("libs/lib/B.simf", "pub fn bar() {}"),
2839 (
2840 "main.simf",
2841 "use lib::A::missing as foo; use lib::B::bar as foo; fn main() {}",
2842 ),
2843 ]);
2844
2845 let err = result.expect_err("Build should fail on the unresolved import");
2846
2847 assert!(err.contains("missing") || err.contains("not found"));
2850 assert!(!err.contains("multiple times"));
2851 }
2852
2853 #[test]
2854 fn test_local_function_cannot_reuse_alias_name() {
2855 let result = analyze_multifile(vec![
2856 ("libs/lib/A.simf", "pub fn bar() {}"),
2857 (
2858 "main.simf",
2859 "use lib::A::bar as foo; pub fn foo() {} fn main() {}",
2860 ),
2861 ]);
2862
2863 let err =
2864 result.expect_err("Build should fail when a local definition reuses an alias name");
2865 assert!(err.contains("foo") && err.contains("multiple times"));
2866 }
2867
2868 #[test]
2869 fn test_local_type_alias_cannot_reuse_alias_name() {
2870 let result = analyze_multifile(vec![
2871 ("libs/lib/A.simf", "pub type bar = u32;"),
2872 (
2873 "main.simf",
2874 "use lib::A::bar as foo; type foo = u64; fn main() {}",
2875 ),
2876 ]);
2877
2878 let err =
2879 result.expect_err("Build should fail when a local definition reuses an alias name");
2880 assert!(err.contains("foo") && err.contains("multiple times"));
2881 }
2882}
2883
2884#[cfg(test)]
2885mod module_tests {
2886 use crate::ast::scope_resolution_tests::analyze_multifile;
2887
2888 #[test]
2889 fn test_public_nested_modules_are_accessible() {
2890 let result = analyze_multifile(vec![
2891 (
2892 "libs/lib/A.simf",
2893 "pub mod outer { pub mod inner { pub fn target() {} } }",
2894 ),
2895 (
2896 "main.simf",
2897 "use lib::A::outer::inner::target; fn main() {}",
2898 ),
2899 ]);
2900
2901 assert!(
2902 result.is_ok(),
2903 "Deeply nested public modules should be accessible: {result:?}"
2904 );
2905 }
2906
2907 #[test]
2908 fn test_private_inner_module_blocks_external_access() {
2909 let result = analyze_multifile(vec![
2910 (
2913 "libs/lib/A.simf",
2914 "pub mod outer { mod inner { pub fn target() {} } }",
2915 ),
2916 (
2917 "main.simf",
2918 "use lib::A::outer::inner::target; fn main() {}",
2919 ),
2920 ]);
2921
2922 let err = result.expect_err("Private inner module must block access");
2923 assert!(err.contains("inner") && err.contains("private"));
2924 }
2925
2926 #[test]
2927 #[ignore = "Not implemented now"]
2928 fn test_importing_a_whole_module_allows_path_traversal() {
2929 let result = analyze_multifile(vec![
2932 ("libs/lib/A.simf", "pub mod math { pub fn add() {} }"),
2933 ("main.simf", "use lib::A::math; fn main() { math::add(); }"),
2934 ]);
2935
2936 assert!(
2937 result.is_ok(),
2938 "Importing a module should bring its namespace into scope: {result:?}"
2939 );
2940 }
2941
2942 #[test]
2943 fn test_duplicate_module_blocks_are_rejected() {
2944 let result = analyze_multifile(vec![(
2945 "main.simf",
2946 "mod inner {} mod inner {} fn main() {}",
2947 )]);
2948
2949 let err = result.expect_err("Duplicate mod blocks must fail");
2950 assert!(err.contains("inner") && err.contains("multiple times"));
2951 }
2952
2953 #[test]
2954 fn test_sibling_modules_can_access_each_others_public_items() {
2955 let result = analyze_multifile(vec![(
2958 "main.simf",
2959 "
2960 mod brother { pub fn toy() {} }
2961 mod sister { use crate::brother::toy; }
2962 fn main() {}
2963 ",
2964 )]);
2965
2966 assert!(
2967 result.is_ok(),
2968 "Sibling modules should be able to import from each other: {result:?}"
2969 );
2970 }
2971
2972 #[test]
2973 fn test_inline_module_can_import_global_item() {
2974 let result = analyze_multifile(vec![(
2977 "main.simf",
2978 "
2979 pub fn global_func() {}
2980 mod inner {
2981 use crate::global_func;
2982 pub fn call_it() { global_func(); }
2983 }
2984 fn main() {}
2985 ",
2986 )]);
2987
2988 assert!(
2989 result.is_ok(),
2990 "Nested modules must be able to import global items: {result:?}"
2991 );
2992 }
2993
2994 #[test]
2995 fn test_deeply_nested_inline_modules() {
2996 let result = analyze_multifile(vec![(
2998 "main.simf",
2999 "
3000 mod level1 {
3001 pub mod level2 {
3002 pub fn treasure() {}
3003 }
3004 }
3005 mod explorer {
3006 use crate::level1::level2::treasure;
3007 }
3008 fn main() {}
3009 ",
3010 )]);
3011
3012 assert!(
3013 result.is_ok(),
3014 "Deeply nested inline modules must resolve correctly: {result:?}"
3015 );
3016 }
3017
3018 #[test]
3019 fn test_inline_module_privacy_is_enforced_between_siblings() {
3020 let result = analyze_multifile(vec![(
3022 "main.simf",
3023 "
3024 mod brother {
3025 fn secret_toy() {} // Missing 'pub'
3026 }
3027 mod sister {
3028 use crate::brother::secret_toy;
3029 }
3030 fn main() {}
3031 ",
3032 )]);
3033
3034 let err = result.expect_err("Private inline items must remain hidden from siblings");
3035 assert!(err.contains("secret_toy") && err.contains("private"));
3036 }
3037
3038 #[test]
3039 fn test_main_scope_cannot_access_private_inline_items() {
3040 let result = analyze_multifile(vec![(
3042 "main.simf",
3043 "
3044 mod child {
3045 fn hidden() {}
3046 }
3047 use crate::child::hidden;
3048 fn main() {}
3049 ",
3050 )]);
3051
3052 let err = result.expect_err("The root file scope must respect inline module privacy");
3053 assert!(err.contains("hidden") && err.contains("private"));
3054 }
3055
3056 #[test]
3057 fn test_inline_module_alias_import() {
3058 let result = analyze_multifile(vec![(
3060 "main.simf",
3061 "
3062 mod supplier {
3063 pub fn raw_material() {}
3064 }
3065 mod factory {
3066 use crate::supplier::raw_material as finished_product;
3067 pub fn produce() { finished_product(); }
3068 }
3069 fn main() {}
3070 ",
3071 )]);
3072
3073 assert!(
3074 result.is_ok(),
3075 "Inline imports must support aliasing: {result:?}"
3076 );
3077 }
3078}
3079
3080#[cfg(test)]
3081mod enum_tests {
3082 use crate::ast::ElementsJetHinter;
3083 use crate::{TemplateProgram, UnstableFeatures};
3084
3085 fn analyze(src: &str) -> Result<(), String> {
3086 TemplateProgram::new_with_unstable(
3087 src,
3088 &UnstableFeatures::all(),
3089 Box::new(ElementsJetHinter::new()),
3090 )
3091 .map(|_| ())
3092 .map_err(|e| e.to_string())
3093 }
3094
3095 #[test]
3096 fn enum_declaration_registers_type_alias() {
3097 let result = analyze(
3098 "enum Color { Red, Green }
3099 fn main() { let _x: Color = witness::C; }",
3100 );
3101 assert!(
3102 result.is_ok(),
3103 "enum name should resolve as a type: {result:?}"
3104 );
3105 }
3106
3107 #[test]
3108 fn enum_duplicate_variant_name_is_error() {
3109 let result = analyze("enum Color { Red, Red }\nfn main() {}");
3110 assert!(result.is_err());
3111 assert!(result.unwrap_err().contains("duplicate variant name"));
3112 }
3113
3114 #[test]
3115 fn enum_variant_named_after_builtin_pattern_is_ok() {
3116 let result = analyze(
3120 "enum Action { None, Some, Other, }
3121 fn main() {
3122 match witness::W {
3123 Action::None => {},
3124 Action::Some => {},
3125 Action::Other => {},
3126 }
3127 }",
3128 );
3129 assert!(
3130 result.is_ok(),
3131 "builtin-named variants should work: {result:?}"
3132 );
3133 }
3134
3135 #[test]
3136 fn enum_empty_is_error() {
3137 let result = analyze("enum Color { }\nfn main() {}");
3138 assert!(result.is_err());
3139 assert!(result.unwrap_err().contains("at least one variant"));
3140 }
3141
3142 #[test]
3143 fn enum_duplicate_name_is_error() {
3144 let result = analyze(
3145 "enum Color { Red, Green }
3146 enum Color { Blue, Cyan }
3147 fn main() {}",
3148 );
3149 assert!(result.is_err(), "redefined enum name should error");
3150 }
3151
3152 #[test]
3153 fn enum_declaration_inside_module_errors() {
3154 let result = analyze(
3156 "mod m {
3157 pub enum Choice { X, Y, }
3158 }
3159 fn main() {}",
3160 );
3161 let err = result.expect_err("enum inside `mod` must be rejected");
3162 assert!(
3163 err.contains("top level"),
3164 "error should say enums are top-level only: {err}"
3165 );
3166 }
3167
3168 #[test]
3169 fn enum_declaration_in_dependency_errors() {
3170 use crate::ast::scope_resolution_tests::analyze_multifile;
3171
3172 let result = analyze_multifile(vec![
3174 (
3175 "main.simf",
3176 "use lib::A::helper;
3177 fn main() { helper(); }",
3178 ),
3179 (
3180 "libs/lib/A.simf",
3181 "pub enum Status { On, Off, } pub fn helper() {}",
3182 ),
3183 ]);
3184 let err = result.expect_err("enums in dependency files must be rejected");
3185 assert!(
3186 err.contains("dependency"),
3187 "error should say enums cannot live in dependency files: {err}"
3188 );
3189 }
3190
3191 #[test]
3192 fn enum_payload_match_binds_payload() {
3193 let result = analyze(
3194 "enum Action { Refresh(u32, bool), Cold, }
3195 fn main() {
3196 match witness::W {
3197 Action::Refresh(n: u32, b: bool) => {
3198 assert!(jet::is_zero_32(n));
3199 assert!(b);
3200 },
3201 Action::Cold => {},
3202 }
3203 }",
3204 );
3205 assert!(
3206 result.is_ok(),
3207 "payload bindings should analyze: {result:?}"
3208 );
3209 }
3210
3211 #[test]
3212 fn enum_payload_binding_type_mismatch_is_error() {
3213 let result = analyze(
3214 "enum Action { Refresh(u32), Cold, }
3215 fn main() {
3216 match witness::W {
3217 Action::Refresh(n: u16) => { assert!(jet::is_zero_16(n)); },
3218 Action::Cold => {},
3219 }
3220 }",
3221 );
3222 assert!(
3223 result.is_err(),
3224 "binding type must equal the declared payload type"
3225 );
3226 }
3227
3228 #[test]
3229 fn enum_payload_binding_arity_mismatch_is_error() {
3230 let result = analyze(
3231 "enum Action { Refresh(u32, bool), Cold, }
3232 fn main() {
3233 match witness::W {
3234 Action::Refresh(n: u32) => { assert!(jet::is_zero_32(n)); },
3235 Action::Cold => {},
3236 }
3237 }",
3238 );
3239 assert!(result.is_err());
3240 assert!(
3241 result.unwrap_err().contains("payload value"),
3242 "error should describe the arity mismatch"
3243 );
3244 }
3245
3246 #[test]
3247 fn enum_single_variant_matches() {
3248 let result = analyze(
3250 "enum Marker { Only }
3251 fn main() {
3252 match witness::M {
3253 Marker::Only => {},
3254 }
3255 }",
3256 );
3257 assert!(
3258 result.is_ok(),
3259 "single-variant enum should work: {result:?}"
3260 );
3261 }
3262
3263 #[test]
3264 fn enum_match_undefined_enum_is_error() {
3265 let result = analyze(
3266 "fn main() {
3267 match witness::P {
3268 Unknown::A => {},
3269 Unknown::B => {},
3270 }
3271 }",
3272 );
3273 assert!(result.is_err(), "undefined enum should error");
3274 }
3275
3276 #[test]
3277 fn enum_match_mixed_enum_names_is_error() {
3278 let result = analyze(
3279 "enum A { X, Y }
3280 enum B { P, Q }
3281 fn main() {
3282 match witness::W {
3283 A::X => {},
3284 B::Q => {},
3285 }
3286 }",
3287 );
3288 assert!(result.is_err());
3289 assert!(result.unwrap_err().contains("same enum"));
3290 }
3291
3292 #[test]
3293 fn enum_match_unknown_variant_is_error() {
3294 let result = analyze(
3295 "enum A { X, Y }
3296 fn main() {
3297 match witness::W {
3298 A::X => {},
3299 A::Z => {},
3300 }
3301 }",
3302 );
3303 assert!(result.is_err());
3304 assert!(result.unwrap_err().contains("not defined"));
3305 }
3306
3307 #[test]
3308 fn enum_match_duplicate_arm_is_error() {
3309 let result = analyze(
3310 "enum A { X, Y }
3311 fn main() {
3312 match witness::W {
3313 A::X => {},
3314 A::X => {},
3315 A::Y => {},
3316 }
3317 }",
3318 );
3319 assert!(result.is_err());
3320 assert!(result.unwrap_err().contains("duplicate arm"));
3321 }
3322
3323 #[test]
3324 fn enum_match_missing_arm_is_error() {
3325 let result = analyze(
3326 "enum A { X, Y, Z }
3327 fn main() {
3328 match witness::W {
3329 A::X => {},
3330 A::Y => {},
3331 }
3332 }",
3333 );
3334 assert!(result.is_err());
3335 assert!(result.unwrap_err().contains("must cover all 3 variants"));
3336 }
3337
3338 #[test]
3339 fn enum_match_rejects_scrutinee_of_different_enum() {
3340 let result = analyze(
3341 "enum A { X, Y, }
3342 enum B { P, Q, }
3343 fn main() {
3344 let v: A = witness::V;
3345 match v {
3346 B::P => {},
3347 B::Q => {},
3348 }
3349 }",
3350 );
3351 assert!(
3352 result.is_err(),
3353 "matching a value of enum A against B's variants must be a type error"
3354 );
3355 }
3356
3357 #[test]
3358 fn enum_match_rejects_plain_u8_scrutinee() {
3359 let result = analyze(
3360 "enum Action { A, B, }
3361 fn main() {
3362 let v: u8 = witness::V;
3363 match v {
3364 Action::A => {},
3365 Action::B => {},
3366 }
3367 }",
3368 );
3369 assert!(
3370 result.is_err(),
3371 "matching a u8 against enum variants must be a type error"
3372 );
3373 }
3374
3375 #[test]
3376 fn enum_match_rejects_same_shaped_enum() {
3377 let result = analyze(
3380 "enum AChoice { X, Y, }
3381 enum BChoice { X, Y, }
3382 fn main() {
3383 let v: AChoice = witness::V;
3384 match v {
3385 BChoice::X => {},
3386 BChoice::Y => {},
3387 }
3388 }",
3389 );
3390 assert!(
3391 result.is_err(),
3392 "structurally identical enums must not be interchangeable"
3393 );
3394 }
3395
3396 #[test]
3397 fn enum_match_on_non_enum_alias_is_error() {
3398 let result = analyze(
3399 "type Foo = u32;
3400 fn main() {
3401 match witness::W {
3402 Foo::A => {},
3403 Foo::B => {},
3404 }
3405 }",
3406 );
3407 assert!(result.is_err());
3408 assert!(
3409 result.unwrap_err().contains("not an enum"),
3410 "a defined non-enum alias should not report an undefined alias"
3411 );
3412 }
3413
3414 #[test]
3415 fn enum_cast_to_same_shaped_enum_is_rejected() {
3416 let result = analyze(
3420 "enum Source { Allow, Deny, }
3421 enum Target { Deny, Allow, }
3422 fn main() {
3423 let s: Source = Source::Allow;
3424 let _t: Target = <Source>::into(s);
3425 }",
3426 );
3427 assert!(
3428 result.is_err(),
3429 "same-shaped enums must not cast into each other"
3430 );
3431 }
3432
3433 #[test]
3434 fn enum_cast_to_structural_sum_is_rejected() {
3435 let result = analyze(
3436 "enum Source { Allow, Deny, }
3437 fn main() {
3438 let s: Source = Source::Allow;
3439 let _e: Either<(), ()> = <Source>::into(s);
3440 }",
3441 );
3442 assert!(
3443 result.is_err(),
3444 "an enum must not cast to its structural sum"
3445 );
3446
3447 let result = analyze(
3448 "enum Source { Allow, Deny, }
3449 fn main() {
3450 let e: Either<(), ()> = Left(());
3451 let _s: Source = <Either<(), ()>>::into(e);
3452 }",
3453 );
3454 assert!(result.is_err(), "a structural sum must not cast to an enum");
3455 }
3456
3457 #[test]
3458 fn enum_cast_reshaping_enum_free_siblings_is_ok() {
3459 let result = analyze(
3462 "enum E { A, B, }
3463 fn main() {
3464 let x: (E, (u16, u16)) = (E::A, (1, 2));
3465 let _y: (E, u32) = <(E, (u16, u16))>::into(x);
3466 }",
3467 );
3468 assert!(
3469 result.is_ok(),
3470 "reshaping enum-free siblings must stay castable: {result:?}"
3471 );
3472 }
3473
3474 #[test]
3475 fn enum_cast_to_itself_is_ok() {
3476 let result = analyze(
3477 "enum Source { Allow, Deny, }
3478 fn main() {
3479 let s: Source = Source::Allow;
3480 let _t: Source = <Source>::into(s);
3481 }",
3482 );
3483 assert!(
3484 result.is_ok(),
3485 "nominally identical cast should stay allowed: {result:?}"
3486 );
3487 }
3488
3489 #[test]
3490 fn enum_named_after_builtin_type_is_rejected() {
3491 for name in crate::str::ALIAS_RESERVED {
3495 let result = analyze(&format!("enum {name} {{ A, B, }}\nfn main() {{}}"));
3496 assert!(result.is_err(), "enum named `{name}` must be rejected");
3497 }
3498 }
3499
3500 #[test]
3501 fn enum_alias_named_after_pattern_is_matchable() {
3502 let result = analyze(
3506 "enum Action { A, B, }
3507 type Left = Action;
3508 fn main() {
3509 let v: Left = Action::A;
3510 match v {
3511 Left::A => {},
3512 Left::B => {},
3513 }
3514 }",
3515 );
3516 assert!(
3517 result.is_ok(),
3518 "an enum alias shadowing a pattern name must be matchable: {result:?}"
3519 );
3520 }
3521
3522 #[test]
3523 fn enum_alias_named_none_is_constructable() {
3524 let result = analyze(
3528 "enum Action { A, B, }
3529 type None = Action;
3530 fn main() {
3531 let _x: None = None::A;
3532 }",
3533 );
3534 assert!(
3535 result.is_ok(),
3536 "`None::A` must parse as enum construction: {result:?}"
3537 );
3538 }
3539
3540 #[test]
3541 fn alias_named_after_pattern_stays_valid_without_enums() {
3542 let result = TemplateProgram::new_with_unstable(
3545 "type Left = u32;\nfn main() { let _x: Left = 1; }",
3546 &UnstableFeatures::none(),
3547 Box::new(ElementsJetHinter::new()),
3548 );
3549 assert!(
3550 result.is_ok(),
3551 "stable alias names must stay valid without -Z enums"
3552 );
3553 }
3554
3555 #[test]
3556 fn enum_construction_follows_lexical_scope_in_source() {
3557 let result = analyze(
3561 "pub enum E { A, B, }
3562 mod m {
3563 use crate::E as Choice;
3564 pub fn make() -> Choice {
3565 E::A
3566 }
3567 }
3568 use crate::m::make;
3569 fn main() {
3570 let _x: E = make();
3571 }",
3572 );
3573 assert!(
3574 result.is_err(),
3575 "an out-of-scope declared name must not construct"
3576 );
3577
3578 let result = analyze(
3579 "pub enum E { A, B, }
3580 mod m {
3581 use crate::E as Choice;
3582 pub fn make() -> Choice {
3583 Choice::A
3584 }
3585 }
3586 use crate::m::make;
3587 fn main() {
3588 let _x: E = make();
3589 }",
3590 );
3591 assert!(
3592 result.is_ok(),
3593 "the imported alias must construct: {result:?}"
3594 );
3595 }
3596
3597 #[test]
3598 fn enum_requires_unstable_feature() {
3599 let result = TemplateProgram::new_with_unstable(
3600 "enum Color { Red, Green }\nfn main() {}",
3601 &UnstableFeatures::none(),
3602 Box::new(ElementsJetHinter::new()),
3603 );
3604 assert!(result.is_err(), "enum syntax is gated behind -Z enums");
3605 }
3606}
3607
3608#[cfg(feature = "fmt")]
3609#[cfg(test)]
3610mod literal_tests {
3611 use crate::parse::ParseFromStr;
3612 use crate::value::{UIntValue, Value};
3613
3614 use super::*;
3615
3616 #[test]
3617 fn analyzed_numeric_literals_accept_digit_separators() {
3618 let cases = [
3619 ("1_337", UIntType::U16, Value::from(UIntValue::U16(1_337))),
3620 (
3621 "0b1010_0101",
3622 UIntType::U8,
3623 Value::from(UIntValue::U8(0b1010_0101)),
3624 ),
3625 (
3626 "0xDE_AD_BE_EF",
3627 UIntType::U32,
3628 Value::from(UIntValue::U32(0xdead_beef)),
3629 ),
3630 ];
3631
3632 for (source, integer_type, expected) in cases {
3633 let parsed = parse::Expression::parse_from_str(source).expect("literal parses");
3634 let analyzed =
3635 Expression::analyze_const(&parsed, &integer_type.into()).expect("literal analyzes");
3636
3637 let ExpressionInner::Single(single) = analyzed.inner() else {
3638 panic!("expected a single expression")
3639 };
3640 let SingleExpressionInner::Constant(value) = single.inner() else {
3641 panic!("expected a constant expression")
3642 };
3643
3644 assert_eq!(value, &expected, "unexpected value for {source:?}");
3645 assert_eq!(single.span().to_slice(source), Some(source));
3646 }
3647 }
3648}