1use rucc_base::Symbol;
16use rucc_diag::Span;
17
18use crate::ast::{AttrList, EnumeratorList, MemberList};
19use crate::decl::TypeNameId;
20use crate::expr::ExprId;
21
22pub type DeclSpecsId = rucc_base::Idx<DeclSpecs>;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct DeclSpecs {
28 pub storage: Option<StorageClass>,
30 pub thread_local: bool,
33 pub constexpr: bool,
37 pub ty: TypeSpec,
39 pub quals: Quals,
41 pub func: FuncSpecs,
43 pub align: Option<AlignSpec>,
46 pub attrs: AttrList,
48 pub span: Span,
50}
51
52impl DeclSpecs {
53 #[must_use]
55 pub const fn empty(span: Span) -> DeclSpecs {
56 DeclSpecs {
57 storage: None,
58 thread_local: false,
59 constexpr: false,
60 ty: TypeSpec::None,
61 quals: Quals::NONE,
62 func: FuncSpecs::NONE,
63 align: None,
64 attrs: AttrList::EMPTY,
65 span,
66 }
67 }
68
69 #[must_use]
71 pub const fn is_typedef(&self) -> bool {
72 matches!(self.storage, Some(StorageClass::Typedef))
73 }
74
75 #[must_use]
77 pub const fn deduces(&self) -> Option<Deduction> {
78 match self.ty {
79 TypeSpec::Auto(which) => Some(which),
80 _ => None,
81 }
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub enum StorageClass {
88 Typedef,
90 Extern,
92 Static,
94 Auto,
96 Register,
98}
99
100impl StorageClass {
101 #[must_use]
103 pub const fn spelling(self) -> &'static str {
104 match self {
105 StorageClass::Typedef => "typedef",
106 StorageClass::Extern => "extern",
107 StorageClass::Static => "static",
108 StorageClass::Auto => "auto",
109 StorageClass::Register => "register",
110 }
111 }
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
121pub struct Quals(u8);
122
123impl Quals {
124 pub const NONE: Quals = Quals(0);
126 pub const CONST: Quals = Quals(1);
128 pub const VOLATILE: Quals = Quals(2);
130 pub const RESTRICT: Quals = Quals(4);
132 pub const ATOMIC: Quals = Quals(8);
134
135 #[inline]
137 #[must_use]
138 pub const fn has(self, other: Quals) -> bool {
139 self.0 & other.0 == other.0
140 }
141
142 #[inline]
144 #[must_use]
145 pub const fn with(self, other: Quals) -> Quals {
146 Quals(self.0 | other.0)
147 }
148
149 #[inline]
151 #[must_use]
152 pub const fn is_none(self) -> bool {
153 self.0 == 0
154 }
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
159pub struct FuncSpecs(u8);
160
161impl FuncSpecs {
162 pub const NONE: FuncSpecs = FuncSpecs(0);
164 pub const INLINE: FuncSpecs = FuncSpecs(1);
166 pub const NORETURN: FuncSpecs = FuncSpecs(2);
169
170 #[inline]
172 #[must_use]
173 pub const fn has(self, other: FuncSpecs) -> bool {
174 self.0 & other.0 == other.0
175 }
176
177 #[inline]
179 #[must_use]
180 pub const fn with(self, other: FuncSpecs) -> FuncSpecs {
181 FuncSpecs(self.0 | other.0)
182 }
183
184 #[inline]
186 #[must_use]
187 pub const fn is_none(self) -> bool {
188 self.0 == 0
189 }
190}
191
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194pub enum AlignSpec {
195 Type(TypeNameId),
197 Expr(ExprId),
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq)]
203pub enum TypeSpec {
204 None,
206 Builtin(Builtin),
208 Record {
210 kind: RecordKind,
212 tag: Option<Symbol>,
214 fields: Option<MemberList>,
218 attrs: AttrList,
220 pack: Option<u32>,
223 },
224 Enum {
226 tag: Option<Symbol>,
228 enumerators: Option<EnumeratorList>,
230 underlying: Option<TypeNameId>,
233 attrs: AttrList,
235 },
236 Typedef(Symbol),
238 Typeof {
240 unqual: bool,
242 operand: TypeofArg,
245 },
246 Atomic(TypeNameId),
248 Auto(Deduction),
250 VaList,
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq)]
265pub enum Deduction {
266 Auto,
268 AutoType,
270}
271
272impl Deduction {
273 #[must_use]
275 pub const fn spelling(self) -> &'static str {
276 match self {
277 Deduction::Auto => "auto",
278 Deduction::AutoType => "__auto_type",
279 }
280 }
281}
282
283#[derive(Debug, Clone, Copy, PartialEq, Eq)]
285pub enum RecordKind {
286 Struct,
288 Union,
290}
291
292impl RecordKind {
293 #[must_use]
295 pub const fn spelling(self) -> &'static str {
296 match self {
297 RecordKind::Struct => "struct",
298 RecordKind::Union => "union",
299 }
300 }
301}
302
303#[derive(Debug, Clone, Copy, PartialEq, Eq)]
305pub enum TypeofArg {
306 Expr(ExprId),
308 Type(TypeNameId),
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
319pub struct Builtin {
320 pub set: BuiltinSet,
322 pub longs: u8,
324 pub width: Option<ExprId>,
326}
327
328#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
330pub struct BuiltinSet(u32);
331
332impl BuiltinSet {
333 pub const NONE: BuiltinSet = BuiltinSet(0);
335 pub const VOID: BuiltinSet = BuiltinSet(1 << 0);
337 pub const BOOL: BuiltinSet = BuiltinSet(1 << 1);
339 pub const CHAR: BuiltinSet = BuiltinSet(1 << 2);
341 pub const SHORT: BuiltinSet = BuiltinSet(1 << 3);
343 pub const INT: BuiltinSet = BuiltinSet(1 << 4);
345 pub const LONG: BuiltinSet = BuiltinSet(1 << 5);
347 pub const SIGNED: BuiltinSet = BuiltinSet(1 << 6);
349 pub const UNSIGNED: BuiltinSet = BuiltinSet(1 << 7);
351 pub const FLOAT: BuiltinSet = BuiltinSet(1 << 8);
353 pub const DOUBLE: BuiltinSet = BuiltinSet(1 << 9);
355 pub const COMPLEX: BuiltinSet = BuiltinSet(1 << 10);
357 pub const IMAGINARY: BuiltinSet = BuiltinSet(1 << 11);
359 pub const INT128: BuiltinSet = BuiltinSet(1 << 12);
361 pub const FLOAT16: BuiltinSet = BuiltinSet(1 << 13);
363 pub const FLOAT32: BuiltinSet = BuiltinSet(1 << 14);
365 pub const FLOAT64: BuiltinSet = BuiltinSet(1 << 15);
367 pub const FLOAT128: BuiltinSet = BuiltinSet(1 << 16);
369 pub const FLOAT32X: BuiltinSet = BuiltinSet(1 << 17);
371 pub const FLOAT64X: BuiltinSet = BuiltinSet(1 << 18);
373 pub const FLOAT128X: BuiltinSet = BuiltinSet(1 << 19);
375 pub const FLOAT80: BuiltinSet = BuiltinSet(1 << 20);
377 pub const DECIMAL32: BuiltinSet = BuiltinSet(1 << 21);
379 pub const DECIMAL64: BuiltinSet = BuiltinSet(1 << 22);
381 pub const DECIMAL128: BuiltinSet = BuiltinSet(1 << 23);
383 pub const BIT_INT: BuiltinSet = BuiltinSet(1 << 24);
385
386 const DECIMALS: BuiltinSet =
388 BuiltinSet(Self::DECIMAL32.0 | Self::DECIMAL64.0 | Self::DECIMAL128.0);
389 const EXTENDED: BuiltinSet = BuiltinSet(
391 Self::FLOAT16.0
392 | Self::FLOAT32.0
393 | Self::FLOAT64.0
394 | Self::FLOAT128.0
395 | Self::FLOAT32X.0
396 | Self::FLOAT64X.0
397 | Self::FLOAT128X.0
398 | Self::FLOAT80.0,
399 );
400 const INTEGER: BuiltinSet =
402 BuiltinSet(Self::SHORT.0 | Self::INT.0 | Self::LONG.0 | Self::SIGNED.0 | Self::UNSIGNED.0);
403
404 #[inline]
406 #[must_use]
407 pub const fn has(self, other: BuiltinSet) -> bool {
408 self.0 & other.0 == other.0
409 }
410
411 #[inline]
413 #[must_use]
414 pub const fn has_any(self, other: BuiltinSet) -> bool {
415 self.0 & other.0 != 0
416 }
417
418 #[inline]
420 #[must_use]
421 pub const fn with(self, other: BuiltinSet) -> BuiltinSet {
422 BuiltinSet(self.0 | other.0)
423 }
424
425 #[inline]
427 #[must_use]
428 pub const fn without(self, other: BuiltinSet) -> BuiltinSet {
429 BuiltinSet(self.0 & !other.0)
430 }
431
432 #[inline]
434 #[must_use]
435 pub const fn is_none(self) -> bool {
436 self.0 == 0
437 }
438}
439
440#[derive(Debug, Clone, Copy, PartialEq, Eq)]
442pub enum BuiltinError {
443 Duplicate,
445 TooManyLongs,
447}
448
449impl Builtin {
450 pub const NONE: Builtin = Builtin { set: BuiltinSet::NONE, longs: 0, width: None };
452
453 pub const fn add(self, which: BuiltinSet) -> Result<Builtin, BuiltinError> {
464 if which.0 == BuiltinSet::LONG.0 {
465 if self.longs >= 2 {
466 return Err(BuiltinError::TooManyLongs);
467 }
468 let set = self.set.with(which);
469 return Ok(Builtin { set, longs: self.longs + 1, width: self.width });
470 }
471 if self.set.has(which) {
472 return Err(BuiltinError::Duplicate);
473 }
474 Ok(Builtin { set: self.set.with(which), longs: self.longs, width: self.width })
475 }
476
477 pub const fn add_bit_int(self, width: ExprId) -> Result<Builtin, BuiltinError> {
488 if self.set.has(BuiltinSet::BIT_INT) {
489 return Err(BuiltinError::Duplicate);
490 }
491 let set = self.set.with(BuiltinSet::BIT_INT);
492 Ok(Builtin { set, longs: self.longs, width: Some(width) })
493 }
494
495 #[must_use]
497 pub const fn is_none(self) -> bool {
498 self.set.is_none()
499 }
500
501 #[must_use]
507 pub fn resolve(self) -> Option<Basic> {
508 let set = self.set;
509 let longs = self.longs;
510
511 let complexity = match (set.has(BuiltinSet::COMPLEX), set.has(BuiltinSet::IMAGINARY)) {
512 (true, true) => return None,
513 (true, false) => Complexity::Complex,
514 (false, true) => Complexity::Imaginary,
515 (false, false) => Complexity::Real,
516 };
517 let set = set.without(BuiltinSet::COMPLEX.with(BuiltinSet::IMAGINARY));
518 let basic = |scalar| Some(Basic { scalar, complexity });
519 let real = |scalar| {
520 if complexity == Complexity::Real { Some(Basic { scalar, complexity }) } else { None }
521 };
522
523 if set.has(BuiltinSet::SIGNED) && set.has(BuiltinSet::UNSIGNED) {
524 return None;
525 }
526 let unsigned = set.has(BuiltinSet::UNSIGNED);
527 let signs = BuiltinSet::SIGNED.with(BuiltinSet::UNSIGNED);
528
529 if set.has(BuiltinSet::VOID) {
532 return if set == BuiltinSet::VOID && longs == 0 { real(Scalar::Void) } else { None };
533 }
534 if set.has(BuiltinSet::BOOL) {
535 return if set == BuiltinSet::BOOL && longs == 0 { real(Scalar::Bool) } else { None };
536 }
537 if set.has(BuiltinSet::CHAR) {
538 if set.without(signs) != BuiltinSet::CHAR || longs != 0 {
539 return None;
540 }
541 return real(match (set.has(BuiltinSet::SIGNED), unsigned) {
542 (true, _) => Scalar::SignedChar,
543 (_, true) => Scalar::UnsignedChar,
544 _ => Scalar::Char,
545 });
546 }
547 if set.has(BuiltinSet::INT128) {
548 if set.without(signs) != BuiltinSet::INT128 || longs != 0 {
549 return None;
550 }
551 return real(if unsigned { Scalar::UnsignedInt128 } else { Scalar::Int128 });
552 }
553 if set.has(BuiltinSet::BIT_INT) {
554 if set.without(signs) != BuiltinSet::BIT_INT || longs != 0 {
555 return None;
556 }
557 let width = self.width?;
560 return real(Scalar::BitInt { width, unsigned });
561 }
562 if set.has_any(BuiltinSet::DECIMALS) {
563 if longs != 0 || complexity != Complexity::Real {
564 return None;
565 }
566 return match set {
567 s if s == BuiltinSet::DECIMAL32 => real(Scalar::Decimal32),
568 s if s == BuiltinSet::DECIMAL64 => real(Scalar::Decimal64),
569 s if s == BuiltinSet::DECIMAL128 => real(Scalar::Decimal128),
570 _ => None,
571 };
572 }
573 if set.has_any(BuiltinSet::EXTENDED) {
574 if longs != 0 {
575 return None;
576 }
577 return match set {
578 s if s == BuiltinSet::FLOAT16 => basic(Scalar::Float16),
579 s if s == BuiltinSet::FLOAT32 => basic(Scalar::Float32),
580 s if s == BuiltinSet::FLOAT64 => basic(Scalar::Float64),
581 s if s == BuiltinSet::FLOAT128 => basic(Scalar::Float128),
582 s if s == BuiltinSet::FLOAT32X => basic(Scalar::Float32x),
583 s if s == BuiltinSet::FLOAT64X => basic(Scalar::Float64x),
584 s if s == BuiltinSet::FLOAT128X => basic(Scalar::Float128x),
585 s if s == BuiltinSet::FLOAT80 => basic(Scalar::Float80),
586 _ => None,
587 };
588 }
589 if set.has(BuiltinSet::FLOAT) {
590 return if set == BuiltinSet::FLOAT && longs == 0 {
591 basic(Scalar::Float)
592 } else {
593 None
594 };
595 }
596 if set.has(BuiltinSet::DOUBLE) {
597 if set.without(BuiltinSet::LONG) != BuiltinSet::DOUBLE {
598 return None;
599 }
600 return match longs {
601 0 => basic(Scalar::Double),
602 1 => basic(Scalar::LongDouble),
603 _ => None,
604 };
605 }
606 if set.is_none() {
607 return None;
608 }
609 if !BuiltinSet::INTEGER.has(set) {
612 return None;
613 }
614 if set.has(BuiltinSet::SHORT) {
615 if longs != 0 {
616 return None;
617 }
618 return basic(if unsigned { Scalar::UnsignedShort } else { Scalar::Short });
619 }
620 match longs {
621 0 => basic(if unsigned { Scalar::UnsignedInt } else { Scalar::Int }),
622 1 => basic(if unsigned { Scalar::UnsignedLong } else { Scalar::Long }),
623 2 => basic(if unsigned { Scalar::UnsignedLongLong } else { Scalar::LongLong }),
624 _ => None,
625 }
626 }
627}
628
629#[derive(Debug, Clone, Copy, PartialEq, Eq)]
631pub struct Basic {
632 pub scalar: Scalar,
634 pub complexity: Complexity,
636}
637
638#[derive(Debug, Clone, Copy, PartialEq, Eq)]
640pub enum Complexity {
641 Real,
643 Complex,
645 Imaginary,
647}
648
649#[derive(Debug, Clone, Copy, PartialEq, Eq)]
654pub enum Scalar {
655 Void,
657 Bool,
659 Char,
661 SignedChar,
663 UnsignedChar,
665 Short,
667 UnsignedShort,
669 Int,
671 UnsignedInt,
673 Long,
675 UnsignedLong,
677 LongLong,
679 UnsignedLongLong,
681 Int128,
683 UnsignedInt128,
685 BitInt {
687 width: ExprId,
689 unsigned: bool,
692 },
693 Float,
695 Double,
697 LongDouble,
699 Float16,
701 Float32,
703 Float64,
705 Float128,
707 Float32x,
709 Float64x,
711 Float128x,
713 Float80,
715 Decimal32,
717 Decimal64,
719 Decimal128,
721}
722
723#[cfg(test)]
724mod tests {
725 use super::*;
726
727 fn resolve(keywords: &[BuiltinSet]) -> Option<Basic> {
728 let mut b = Builtin::NONE;
729 for &k in keywords {
730 b = b.add(k).expect("keyword rejected");
731 }
732 b.resolve()
733 }
734
735 fn real(keywords: &[BuiltinSet]) -> Option<Scalar> {
736 resolve(keywords).filter(|b| b.complexity == Complexity::Real).map(|b| b.scalar)
737 }
738
739 #[test]
740 fn the_plain_integer_spellings() {
741 assert_eq!(real(&[BuiltinSet::INT]), Some(Scalar::Int));
742 assert_eq!(real(&[BuiltinSet::SIGNED]), Some(Scalar::Int));
743 assert_eq!(real(&[BuiltinSet::UNSIGNED]), Some(Scalar::UnsignedInt));
744 assert_eq!(real(&[BuiltinSet::SIGNED, BuiltinSet::INT]), Some(Scalar::Int));
745 assert_eq!(real(&[BuiltinSet::SHORT]), Some(Scalar::Short));
746 assert_eq!(real(&[BuiltinSet::SHORT, BuiltinSet::INT]), Some(Scalar::Short));
747 assert_eq!(
748 real(&[BuiltinSet::UNSIGNED, BuiltinSet::SHORT, BuiltinSet::INT]),
749 Some(Scalar::UnsignedShort)
750 );
751 }
752
753 #[test]
754 fn long_counts_rather_than_repeats() {
755 assert_eq!(real(&[BuiltinSet::LONG]), Some(Scalar::Long));
756 assert_eq!(real(&[BuiltinSet::LONG, BuiltinSet::LONG]), Some(Scalar::LongLong));
757 assert_eq!(
758 real(&[BuiltinSet::UNSIGNED, BuiltinSet::LONG, BuiltinSet::LONG, BuiltinSet::INT]),
759 Some(Scalar::UnsignedLongLong)
760 );
761 let three = Builtin::NONE
762 .add(BuiltinSet::LONG)
763 .and_then(|b| b.add(BuiltinSet::LONG))
764 .and_then(|b| b.add(BuiltinSet::LONG));
765 assert_eq!(three, Err(BuiltinError::TooManyLongs));
766 }
767
768 #[test]
769 fn a_repeated_keyword_is_caught_where_it_is_written() {
770 let twice = Builtin::NONE.add(BuiltinSet::INT).and_then(|b| b.add(BuiltinSet::INT));
771 assert_eq!(twice, Err(BuiltinError::Duplicate));
772 }
773
774 #[test]
775 fn plain_char_is_its_own_type() {
776 assert_eq!(real(&[BuiltinSet::CHAR]), Some(Scalar::Char));
777 assert_eq!(real(&[BuiltinSet::SIGNED, BuiltinSet::CHAR]), Some(Scalar::SignedChar));
778 assert_eq!(real(&[BuiltinSet::UNSIGNED, BuiltinSet::CHAR]), Some(Scalar::UnsignedChar));
779 assert_eq!(real(&[BuiltinSet::CHAR, BuiltinSet::INT]), None);
780 assert_eq!(real(&[BuiltinSet::CHAR, BuiltinSet::LONG]), None);
781 }
782
783 #[test]
784 fn long_double_is_a_double_with_one_long() {
785 assert_eq!(real(&[BuiltinSet::DOUBLE]), Some(Scalar::Double));
786 assert_eq!(real(&[BuiltinSet::LONG, BuiltinSet::DOUBLE]), Some(Scalar::LongDouble));
787 assert_eq!(real(&[BuiltinSet::LONG, BuiltinSet::LONG, BuiltinSet::DOUBLE]), None);
788 assert_eq!(real(&[BuiltinSet::LONG, BuiltinSet::FLOAT]), None);
789 assert_eq!(real(&[BuiltinSet::DOUBLE, BuiltinSet::INT]), None);
790 }
791
792 #[test]
793 fn complex_is_a_modifier_and_not_a_type() {
794 assert_eq!(
795 resolve(&[BuiltinSet::COMPLEX, BuiltinSet::DOUBLE]),
796 Some(Basic { scalar: Scalar::Double, complexity: Complexity::Complex })
797 );
798 assert_eq!(
799 resolve(&[BuiltinSet::LONG, BuiltinSet::DOUBLE, BuiltinSet::IMAGINARY]),
800 Some(Basic { scalar: Scalar::LongDouble, complexity: Complexity::Imaginary })
801 );
802 assert_eq!(
804 resolve(&[BuiltinSet::COMPLEX, BuiltinSet::INT]),
805 Some(Basic { scalar: Scalar::Int, complexity: Complexity::Complex })
806 );
807 assert_eq!(resolve(&[BuiltinSet::COMPLEX, BuiltinSet::IMAGINARY, BuiltinSet::FLOAT]), None);
808 assert_eq!(resolve(&[BuiltinSet::COMPLEX, BuiltinSet::DECIMAL64]), None);
810 }
811
812 #[test]
813 fn the_extended_types_stand_alone_or_with_complex() {
814 assert_eq!(real(&[BuiltinSet::FLOAT128]), Some(Scalar::Float128));
815 assert_eq!(
816 resolve(&[BuiltinSet::COMPLEX, BuiltinSet::FLOAT128]),
817 Some(Basic { scalar: Scalar::Float128, complexity: Complexity::Complex })
818 );
819 assert_eq!(real(&[BuiltinSet::FLOAT32X]), Some(Scalar::Float32x));
820 assert_eq!(real(&[BuiltinSet::FLOAT128X]), Some(Scalar::Float128x));
821 assert_eq!(real(&[BuiltinSet::FLOAT16, BuiltinSet::INT]), None);
822 assert_eq!(real(&[BuiltinSet::FLOAT32, BuiltinSet::FLOAT64]), None);
823 }
824
825 #[test]
826 fn the_wide_integers_take_a_sign_and_nothing_else() {
827 assert_eq!(real(&[BuiltinSet::INT128]), Some(Scalar::Int128));
828 assert_eq!(real(&[BuiltinSet::UNSIGNED, BuiltinSet::INT128]), Some(Scalar::UnsignedInt128));
829 assert_eq!(real(&[BuiltinSet::INT128, BuiltinSet::INT]), None);
830 }
831
832 #[test]
833 fn void_and_bool_take_nothing() {
834 assert_eq!(real(&[BuiltinSet::VOID]), Some(Scalar::Void));
835 assert_eq!(real(&[BuiltinSet::BOOL]), Some(Scalar::Bool));
836 assert_eq!(real(&[BuiltinSet::VOID, BuiltinSet::INT]), None);
837 assert_eq!(real(&[BuiltinSet::UNSIGNED, BuiltinSet::BOOL]), None);
838 }
839
840 #[test]
841 fn two_signs_name_no_type() {
842 assert_eq!(real(&[BuiltinSet::SIGNED, BuiltinSet::UNSIGNED]), None);
843 }
844
845 #[test]
846 fn no_keywords_at_all_names_no_type() {
847 assert_eq!(Builtin::NONE.resolve(), None);
848 assert!(Builtin::NONE.is_none());
849 }
850
851 #[test]
852 fn short_and_long_do_not_go_together() {
853 assert_eq!(real(&[BuiltinSet::SHORT, BuiltinSet::LONG]), None);
854 }
855
856 #[test]
857 fn qualifier_sets_add_up() {
858 let q = Quals::NONE.with(Quals::CONST).with(Quals::VOLATILE);
859 assert!(q.has(Quals::CONST));
860 assert!(q.has(Quals::VOLATILE));
861 assert!(!q.has(Quals::RESTRICT));
862 assert!(Quals::NONE.is_none());
863 assert!(!q.is_none());
864 }
865
866 #[test]
867 fn function_specifier_sets_add_up() {
868 let f = FuncSpecs::NONE.with(FuncSpecs::INLINE);
869 assert!(f.has(FuncSpecs::INLINE));
870 assert!(!f.has(FuncSpecs::NORETURN));
871 assert!(FuncSpecs::NONE.is_none());
872 }
873}