1use crate::error::{DirectiveError, DirectiveErrors};
2use crate::spectest::spectest_importobject;
3use anyhow::{Result, anyhow, bail};
4use std::collections::{HashMap, HashSet};
5use std::path::Path;
6use std::str;
7use wasmer::*;
8use wast::core::{AbstractHeapType, HeapType, WastArgCore, WastRetCore};
9use wast::token::{F32, F64};
10use wast::{QuoteWat, Wast as WWast, WastArg};
11use wast::{lexer::Lexer, parser};
12
13#[allow(dead_code)]
16pub struct Wast {
17 current: Option<Instance>,
20 import_object: Imports,
22 instances: HashMap<String, Instance>,
24 allowed_instantiation_failures: HashSet<String>,
26 match_trap_messages: HashMap<String, String>,
29 current_is_allowed_failure: bool,
31 store: Store,
33 pub fail_fast: bool,
35 disable_assert_trap_exhaustion: bool,
38
39 disable_assert_exception: bool,
41}
42
43impl Wast {
44 pub fn new(store: Store, import_object: Imports) -> Self {
46 Self {
47 current: None,
48 store,
49 import_object,
50 allowed_instantiation_failures: HashSet::new(),
51 match_trap_messages: HashMap::new(),
52 current_is_allowed_failure: false,
53 instances: HashMap::new(),
54 fail_fast: true,
55 disable_assert_trap_exhaustion: false,
56 disable_assert_exception: false,
57 }
58 }
59
60 pub fn allow_instantiation_failures(&mut self, failures: &[&str]) {
62 for &failure_str in failures.iter() {
63 self.allowed_instantiation_failures
64 .insert(failure_str.to_string());
65 }
66 }
67
68 pub fn allow_trap_message(&mut self, expected: &str, allowed: &str) {
70 self.match_trap_messages
71 .insert(expected.into(), allowed.into());
72 }
73
74 pub fn disable_assert_and_exhaustion(&mut self) {
76 self.disable_assert_trap_exhaustion = true;
77 }
78
79 pub fn disable_assert_exception(&mut self) {
81 self.disable_assert_exception = true;
82 }
83
84 pub fn new_with_spectest(mut store: Store) -> Self {
86 let import_object = spectest_importobject(&mut store);
87 Self::new(store, import_object)
88 }
89
90 fn get_instance(&self, instance_name: Option<&str>) -> Result<Instance> {
91 match instance_name {
92 Some(name) => self
93 .instances
94 .get(name)
95 .cloned()
96 .ok_or_else(|| anyhow!("failed to find instance named `{name}`")),
97 None => self
98 .current
99 .clone()
100 .ok_or_else(|| anyhow!("no previous instance found")),
101 }
102 }
103
104 fn perform_execute(&mut self, exec: wast::WastExecute<'_>) -> Result<Vec<Value>> {
106 match exec {
107 wast::WastExecute::Invoke(invoke) => self.perform_invoke(invoke),
108 wast::WastExecute::Wat(mut module) => {
109 let binary = module.encode()?;
110 let result = self.instantiate(&binary);
111 result.map(|_| Vec::new())
112 }
113 wast::WastExecute::Get { module, global, .. } => {
114 self.get(module.map(|s| s.name()), global)
115 }
116 }
117 }
118
119 fn perform_invoke(&mut self, exec: wast::WastInvoke<'_>) -> Result<Vec<Value>> {
120 let values = exec
121 .args
122 .iter()
123 .map(|v| match v {
124 WastArg::Core(v) => self.runtime_value(v),
125 WastArg::Component(_) => bail!("expected component function, found core"),
126 _ => todo!(),
127 })
128 .collect::<Result<Vec<_>>>()?;
129 self.invoke(exec.module.map(|i| i.name()), exec.name, &values)
130 }
131
132 fn assert_return(
133 &self,
134 result: Result<Vec<Value>>,
135 results: &[wast::WastRet<'_>],
136 ) -> Result<()> {
137 let values = result?;
138 for (v, e) in values.iter().zip(results) {
139 match e {
140 wast::WastRet::Core(e) => {
141 if self.val_matches(v, e)? {
142 continue;
143 }
144 }
145 wast::WastRet::Component(_) => anyhow::bail!("Components not supported yet!"),
146 _ => todo!(),
147 }
148
149 if let Value::V128(bits) = v
150 && let wast::WastRet::Core(WastRetCore::V128(pattern)) = e
151 {
152 let formatted = v128_format(*bits, pattern);
153 bail!("expected {e:?}, got {formatted:?} (v128 bits: {bits})");
154 }
155 if let Some(f) = v.f64() {
156 if let wast::WastRet::Core(WastRetCore::F64(wast::core::NanPattern::Value(f1))) = e
157 {
158 let f = f64::from_bits(f1.bits);
159 let f_bits = f.to_bits();
160 bail!("expected {f:?} ({e:?}), got {v:?} ({f_bits})")
161 } else {
162 let f_bits = f.to_bits();
163 bail!("expected {e:?}, got {v:?} ({f_bits})")
164 }
165 } else if let Some(f) = v.f32() {
166 if let wast::WastRet::Core(WastRetCore::F32(wast::core::NanPattern::Value(f1))) = e
167 {
168 let f = f32::from_bits(f1.bits);
169 let f_bits = f.to_bits();
170 bail!("expected {f:?} ({e:?}), got {v:?} ({f_bits})")
171 } else {
172 let f_bits = f.to_bits();
173 bail!("expected {e:?}, got {v:?} ({f_bits})")
174 }
175 } else {
176 bail!("expected {e:?}, got {v:?}")
177 }
178 }
179 Ok(())
180 }
181 fn wat(&mut self, mut wat: QuoteWat<'_>) -> Result<()> {
183 let (is_module, name) = match &wat {
184 QuoteWat::Wat(wast::Wat::Module(m)) => (true, m.id.map(|v| v.name())),
185 QuoteWat::QuoteModule(..) => (true, None),
186 QuoteWat::Wat(wast::Wat::Component(m)) => (false, m.id.map(|v| v.name())),
187 QuoteWat::QuoteComponent(..) => (false, None),
188 };
189 let bytes = wat.encode()?;
190 if is_module {
191 self.module(name, &bytes)?;
192 } else {
193 bail!("component-model support not enabled");
194 }
195 Ok(())
196 }
197
198 fn assert_trap(&self, result: Result<Vec<Value>>, expected: &str) -> Result<()> {
199 let actual = match result {
200 Ok(values) => bail!("expected trap, got {values:?}"),
201 Err(t) => format!("{t}"),
202 };
203 if self.matches_message_assert_trap(expected, &actual) {
204 return Ok(());
205 }
206 bail!("expected '{expected}', got '{actual}'")
207 }
208
209 fn run_directive(&mut self, _test: &Path, directive: wast::WastDirective) -> Result<()> {
210 use wast::WastDirective::*;
211
212 match directive {
213 ModuleDefinition(module) => self.wat(module)?,
214 Module(module) => self.wat(module)?,
215 Register {
216 span: _,
217 name,
218 module,
219 } => {
220 self.register(module.map(|s| s.name()), name)?;
221 }
222 Invoke(i) => {
223 self.perform_invoke(i)?;
224 }
225 AssertReturn {
226 span: _,
227 exec,
228 results,
229 } => {
230 let result = self.perform_execute(exec);
231 self.assert_return(result, &results)?;
232 }
233 AssertTrap {
234 span: _,
235 exec,
236 message,
237 } => {
238 if !self.disable_assert_trap_exhaustion {
239 let result = self.perform_execute(exec);
240 self.assert_trap(result, message)?;
241 }
242 }
243 AssertExhaustion {
244 span: _,
245 call,
246 message,
247 } => {
248 if !self.disable_assert_trap_exhaustion {
249 let result = self.perform_invoke(call);
250 self.assert_trap(result, message)?;
251 }
252 }
253 AssertInvalid {
254 span: _,
255 module,
256 message,
257 } => {
258 let err = match self.wat(module) {
259 Ok(()) => bail!("expected module to fail to build"),
260 Err(e) => e,
261 };
262 let error_message = format!("{err:?}");
263 if !Self::matches_message_assert_invalid(message, &error_message) {
264 bail!("assert_invalid: expected \"{message}\", got \"{error_message}\"")
265 }
266 }
267 AssertException { span: _, exec } => {
268 if !self.disable_assert_exception {
269 let result = self.perform_execute(exec);
270 self.assert_exception(result)?;
271 }
272 }
273 AssertMalformed {
274 module,
275 span: _,
276 message: _,
277 } => {
278 let mut module = match module {
279 wast::QuoteWat::Wat(m) => m,
280 wast::QuoteWat::QuoteModule(_, _) => return Ok(()),
283 wast::QuoteWat::QuoteComponent(_, _) => {
284 anyhow::bail!("Components not supported!")
285 }
286 };
287 let bytes = module.encode()?;
288 if self.module(None, &bytes).is_ok() {
289 bail!("expected malformed module to fail to instantiate");
290 }
291 }
292 AssertUnlinkable {
293 span: _,
294 mut module,
295 message,
296 } => {
297 let bytes = module.encode()?;
298 let err = match self.module(None, &bytes) {
299 Ok(()) => bail!("expected module to fail to link"),
300 Err(e) => e,
301 };
302 let error_message = format!("{err:?}");
303 if !Self::matches_message_assert_unlinkable(message, &error_message) {
304 bail!("assert_unlinkable: expected {message}, got {error_message}")
305 }
306 }
307 Thread(_) => anyhow::bail!("`thread` directives not implemented yet!"),
308 Wait { .. } => anyhow::bail!("`wait` directives not implemented yet!"),
309 ModuleInstance { .. } => {
310 anyhow::bail!("module instance directive not implemented yet!")
311 }
312 AssertSuspension { .. } => {
313 anyhow::bail!("`assert suspension` directive not implemented yet!")
314 }
315 }
316
317 Ok(())
318 }
319
320 pub fn run_buffer(&mut self, test: &Path, wast: &[u8]) -> Result<()> {
322 let wast = str::from_utf8(wast)?;
323 let filename = test.to_str().unwrap();
324 let adjust_wast = |mut err: wast::Error| {
325 err.set_path(filename.as_ref());
326 err.set_text(wast);
327 err
328 };
329
330 let mut lexer = Lexer::new(wast);
331 lexer.allow_confusing_unicode(filename.ends_with("names.wast"));
332 let buf = wast::parser::ParseBuffer::new_with_lexer(lexer).map_err(adjust_wast)?;
333 let ast = parser::parse::<WWast>(&buf).map_err(adjust_wast)?;
334
335 let mut errors = Vec::with_capacity(ast.directives.len());
336 for directive in ast.directives {
337 let sp = directive.span();
338 if let Err(e) = self.run_directive(test, directive) {
339 let message = format!("{e}");
340 if message.contains("no previous instance found") {
342 continue;
343 }
344 if self.current.is_none() && self.current_is_allowed_failure {
347 continue;
348 }
349 let (line, col) = sp.linecol_in(wast);
350 errors.push(DirectiveError {
351 line: line + 1,
352 col,
353 message,
354 });
355 if self.fail_fast {
356 break;
357 }
358 }
359 }
360 if !errors.is_empty() {
361 return Err(DirectiveErrors {
362 filename: filename.to_string(),
363 errors,
364 }
365 .into());
366 }
367 Ok(())
368 }
369
370 pub fn run_file(&mut self, path: &Path) -> Result<()> {
395 let bytes = std::fs::read(path)?;
396 self.run_buffer(path, &bytes)
397 }
398}
399
400impl Wast {
402 fn module(&mut self, instance_name: Option<&str>, module: &[u8]) -> Result<()> {
404 let instance = match self.instantiate(module) {
405 Ok(i) => i,
406 Err(e) => {
407 self.current = None;
410 let error_message = format!("{e}");
411 self.current_is_allowed_failure = false;
412 for allowed_failure in self.allowed_instantiation_failures.iter() {
413 if error_message.contains(allowed_failure) {
414 self.current_is_allowed_failure = true;
415 break;
416 }
417 }
418 bail!("instantiation failed with: {e}")
419 }
420 };
421 if let Some(name) = instance_name {
422 self.instances.insert(name.to_string(), instance.clone());
423 }
424 self.current = Some(instance);
425 self.current_is_allowed_failure = false;
426 Ok(())
427 }
428
429 fn instantiate(&mut self, module: &[u8]) -> Result<Instance> {
430 let module = Module::new(&self.store, module)?;
431 let mut imports = self.import_object.clone();
432
433 for import in module.imports() {
434 let module_name = import.module();
435 if imports.contains_namespace(module_name) {
436 continue;
437 }
438 let instance = self
439 .instances
440 .get(module_name)
441 .ok_or_else(|| anyhow!("constant expression required"))?;
442 imports.register_namespace(module_name, instance.exports.clone());
443 }
444
445 let instance = Instance::new(&mut self.store, &module, &imports)?;
446 Ok(instance)
447 }
448
449 fn register(&mut self, name: Option<&str>, as_name: &str) -> Result<()> {
451 let instance = self.get_instance(name)?;
452 self.instances.insert(as_name.to_string(), instance);
453 Ok(())
454 }
455
456 fn invoke(
458 &mut self,
459 instance_name: Option<&str>,
460 field: &str,
461 args: &[Value],
462 ) -> Result<Vec<Value>> {
463 let instance = self.get_instance(instance_name)?;
464 let func: &Function = instance.exports.get(field)?;
465 match func.call(&mut self.store, args) {
466 Ok(result) => Ok(result.into()),
467 Err(e) => Err(e.into()),
468 }
469 }
470
471 fn get(&mut self, instance_name: Option<&str>, field: &str) -> Result<Vec<Value>> {
473 let instance = self.get_instance(instance_name)?;
474 let global: &Global = instance.exports.get(field)?;
475 Ok(vec![global.get(&mut self.store)])
476 }
477
478 fn runtime_value(&mut self, v: &WastArgCore) -> Result<Value> {
480 use wast::core::WastArgCore::*;
481
482 Ok(match v {
483 I32(x) => Value::I32(*x),
484 I64(x) => Value::I64(*x),
485 F32(x) => Value::F32(f32::from_bits(x.bits)),
486 F64(x) => Value::F64(f64::from_bits(x.bits)),
487 V128(x) => Value::V128(u128::from_le_bytes(x.to_le_bytes())),
488 RefNull(HeapType::Abstract {
489 ty: AbstractHeapType::Func,
490 ..
491 }) => Value::FuncRef(None),
492 RefNull(HeapType::Abstract {
493 ty: AbstractHeapType::Extern,
494 ..
495 }) => Value::null(),
496 RefExtern(number) => Value::ExternRef(Some(ExternRef::new(&mut self.store, *number))),
497 other => bail!("couldn't convert {other:?} to a runtime value"),
498 })
499 }
500
501 fn matches_message_assert_unlinkable(expected: &str, actual: &str) -> bool {
503 actual.contains(expected)
504 || (expected.contains("incompatible import type")
505 && actual.contains("instantiation failed with: constant expression required"))
506 || (expected.contains("unknown import")
507 && actual.contains("instantiation failed with: constant expression required"))
508 }
509
510 fn matches_message_assert_invalid(expected: &str, actual: &str) -> bool {
512 actual.contains(expected)
513 || (expected.contains("unknown table") && actual.contains("unknown elem"))
516 || (expected.contains("unknown memory") && actual.contains("no linear memories are present"))
518 || (expected.contains("out of bounds") && actual.contains("does not fit"))
521 || (expected.contains("unknown global") && actual.contains("unknown global"))
523 || (expected.contains("unknown memory") && actual.contains("unknown memory"))
525 || (expected.contains("unknown memory") && actual.contains("Data segment extends past end of the data section"))
526 || (expected.contains("unknown elem segment") && actual.contains("unknown element segment"))
527 || (expected.contains("memory size must be at most 65536 pages") && actual.contains("invalid u32 number"))
532 || (expected == "unknown global" && actual.contains("global.get of locally defined global"))
535 || (expected == "immutable global" && actual.contains("global is immutable: cannot modify it with `global.set`"))
536 || (expected.contains("type mismatch: instruction requires") && actual.contains("instantiation failed with: Validation error: type mismatch: expected"))
537 || (expected.contains("alignment must not be larger than natural") && actual.contains("malformed memop alignment: alignment too large"))
538 || (expected.contains("type mismatch") && actual.contains("malformed memop alignment: alignment too large"))
539 || (expected.contains("type mismatch") && actual.contains("Validation error: gc support is not enabled"))
540 }
541
542 fn matches_message_assert_trap(&self, expected: &str, actual: &str) -> bool {
544 actual.contains(expected)
545 || self
546 .match_trap_messages
547 .get(expected)
548 .is_some_and(|alternative| actual.contains(alternative))
549 }
550
551 fn assert_exception(&self, result: Result<Vec<Value>>) -> Result<()> {
552 if result.is_ok() {
553 anyhow::bail!("Expected exception to be thrown, returned {result:?} instead");
554 }
555 Ok(())
556 }
557
558 fn val_matches(&self, actual: &Value, expected: &WastRetCore) -> Result<bool> {
559 Ok(match (actual, expected) {
560 (Value::I32(a), WastRetCore::I32(b)) => a == b,
561 (Value::I64(a), WastRetCore::I64(b)) => a == b,
562 (Value::F32(a), WastRetCore::F32(b)) => f32_matches(*a, b),
565 (Value::F64(a), WastRetCore::F64(b)) => f64_matches(*a, b),
566 (Value::V128(a), WastRetCore::V128(b)) => v128_matches(*a, b),
567 (actual, WastRetCore::Either(cases)) => cases
568 .iter()
569 .any(|case| self.val_matches(actual, case).unwrap_or(false)),
570 (
571 Value::FuncRef(None),
572 WastRetCore::RefNull(Some(wast::core::HeapType::Abstract {
573 ty: AbstractHeapType::Func,
574 ..
575 })),
576 ) => true,
577 (Value::FuncRef(Some(_)), WastRetCore::RefNull(_)) => false,
578 (Value::FuncRef(Some(_)), WastRetCore::RefFunc(_)) => true,
580 (Value::FuncRef(None), WastRetCore::RefFunc(None)) => true,
581 (Value::FuncRef(None), WastRetCore::RefFunc(Some(_))) => false,
582 (Value::FuncRef(None), WastRetCore::RefNull(_)) => true,
583 (
584 Value::ExternRef(None),
585 WastRetCore::RefNull(Some(wast::core::HeapType::Abstract {
586 ty: AbstractHeapType::Extern,
587 ..
588 })),
589 ) => true,
590 (Value::ExternRef(None), WastRetCore::RefExtern(_)) => false,
591 (Value::ExceptionRef(None), WastRetCore::RefNull(_)) => true,
592 (Value::ExternRef(Some(_)), WastRetCore::RefNull(_)) => false,
593 (Value::ExternRef(Some(extern_ref)), WastRetCore::RefExtern(num)) => {
594 let x = extern_ref.downcast::<u32>(&self.store).cloned();
595 x == *num
596 }
597 _ => bail!("don't know how to compare {actual:?} and {expected:?} yet"),
598 })
599 }
600}
601
602fn extract_lane_as_i8(bytes: u128, lane: usize) -> i8 {
603 (bytes >> (lane * 8)) as i8
604}
605
606fn extract_lane_as_i16(bytes: u128, lane: usize) -> i16 {
607 (bytes >> (lane * 16)) as i16
608}
609
610fn extract_lane_as_i32(bytes: u128, lane: usize) -> i32 {
611 (bytes >> (lane * 32)) as i32
612}
613
614fn extract_lane_as_i64(bytes: u128, lane: usize) -> i64 {
615 (bytes >> (lane * 64)) as i64
616}
617
618fn f32_matches(actual: f32, expected: &wast::core::NanPattern<F32>) -> bool {
619 match expected {
620 wast::core::NanPattern::CanonicalNan => actual.is_canonical_nan(),
621 wast::core::NanPattern::ArithmeticNan => actual.is_arithmetic_nan(),
622 wast::core::NanPattern::Value(expected_value) => actual.to_bits() == expected_value.bits,
623 }
624}
625
626fn f64_matches(actual: f64, expected: &wast::core::NanPattern<F64>) -> bool {
627 match expected {
628 wast::core::NanPattern::CanonicalNan => actual.is_canonical_nan(),
629 wast::core::NanPattern::ArithmeticNan => actual.is_arithmetic_nan(),
630 wast::core::NanPattern::Value(expected_value) => actual.to_bits() == expected_value.bits,
631 }
632}
633
634fn v128_matches(actual: u128, expected: &wast::core::V128Pattern) -> bool {
635 match expected {
636 wast::core::V128Pattern::I8x16(b) => b
637 .iter()
638 .enumerate()
639 .all(|(i, b)| *b == extract_lane_as_i8(actual, i)),
640 wast::core::V128Pattern::I16x8(b) => b
641 .iter()
642 .enumerate()
643 .all(|(i, b)| *b == extract_lane_as_i16(actual, i)),
644 wast::core::V128Pattern::I32x4(b) => b
645 .iter()
646 .enumerate()
647 .all(|(i, b)| *b == extract_lane_as_i32(actual, i)),
648 wast::core::V128Pattern::I64x2(b) => b
649 .iter()
650 .enumerate()
651 .all(|(i, b)| *b == extract_lane_as_i64(actual, i)),
652 wast::core::V128Pattern::F32x4(b) => b.iter().enumerate().all(|(i, b)| {
653 let a = extract_lane_as_i32(actual, i) as u32;
654 f32_matches(f32::from_bits(a), b)
655 }),
656 wast::core::V128Pattern::F64x2(b) => b.iter().enumerate().all(|(i, b)| {
657 let a = extract_lane_as_i64(actual, i) as u64;
658 f64_matches(f64::from_bits(a), b)
659 }),
660 }
661}
662
663fn v128_format(actual: u128, expected: &wast::core::V128Pattern) -> wast::core::V128Pattern {
664 match expected {
665 wast::core::V128Pattern::I8x16(_) => wast::core::V128Pattern::I8x16([
666 extract_lane_as_i8(actual, 0),
667 extract_lane_as_i8(actual, 1),
668 extract_lane_as_i8(actual, 2),
669 extract_lane_as_i8(actual, 3),
670 extract_lane_as_i8(actual, 4),
671 extract_lane_as_i8(actual, 5),
672 extract_lane_as_i8(actual, 6),
673 extract_lane_as_i8(actual, 7),
674 extract_lane_as_i8(actual, 8),
675 extract_lane_as_i8(actual, 9),
676 extract_lane_as_i8(actual, 10),
677 extract_lane_as_i8(actual, 11),
678 extract_lane_as_i8(actual, 12),
679 extract_lane_as_i8(actual, 13),
680 extract_lane_as_i8(actual, 14),
681 extract_lane_as_i8(actual, 15),
682 ]),
683 wast::core::V128Pattern::I16x8(_) => wast::core::V128Pattern::I16x8([
684 extract_lane_as_i16(actual, 0),
685 extract_lane_as_i16(actual, 1),
686 extract_lane_as_i16(actual, 2),
687 extract_lane_as_i16(actual, 3),
688 extract_lane_as_i16(actual, 4),
689 extract_lane_as_i16(actual, 5),
690 extract_lane_as_i16(actual, 6),
691 extract_lane_as_i16(actual, 7),
692 ]),
693 wast::core::V128Pattern::I32x4(_) => wast::core::V128Pattern::I32x4([
694 extract_lane_as_i32(actual, 0),
695 extract_lane_as_i32(actual, 1),
696 extract_lane_as_i32(actual, 2),
697 extract_lane_as_i32(actual, 3),
698 ]),
699 wast::core::V128Pattern::I64x2(_) => wast::core::V128Pattern::I64x2([
700 extract_lane_as_i64(actual, 0),
701 extract_lane_as_i64(actual, 1),
702 ]),
703 wast::core::V128Pattern::F32x4(_) => wast::core::V128Pattern::F32x4([
704 wast::core::NanPattern::Value(F32 {
705 bits: extract_lane_as_i32(actual, 0) as _,
706 }),
707 wast::core::NanPattern::Value(F32 {
708 bits: extract_lane_as_i32(actual, 1) as _,
709 }),
710 wast::core::NanPattern::Value(F32 {
711 bits: extract_lane_as_i32(actual, 2) as _,
712 }),
713 wast::core::NanPattern::Value(F32 {
714 bits: extract_lane_as_i32(actual, 3) as _,
715 }),
716 ]),
717 wast::core::V128Pattern::F64x2(_) => wast::core::V128Pattern::F64x2([
718 wast::core::NanPattern::Value(F64 {
719 bits: extract_lane_as_i64(actual, 0) as _,
720 }),
721 wast::core::NanPattern::Value(F64 {
722 bits: extract_lane_as_i64(actual, 1) as _,
723 }),
724 ]),
725 }
726}
727
728pub trait NaNCheck {
729 fn is_arithmetic_nan(&self) -> bool;
730 fn is_canonical_nan(&self) -> bool;
731}
732
733impl NaNCheck for f32 {
734 fn is_arithmetic_nan(&self) -> bool {
735 const AF32_NAN: u32 = 0x0040_0000;
736 (self.to_bits() & AF32_NAN) == AF32_NAN
737 }
738
739 fn is_canonical_nan(&self) -> bool {
740 (self.to_bits() & 0x7fff_ffff) == 0x7fc0_0000
741 }
742}
743
744impl NaNCheck for f64 {
745 fn is_arithmetic_nan(&self) -> bool {
746 const AF64_NAN: u64 = 0x0008_0000_0000_0000;
747 (self.to_bits() & AF64_NAN) == AF64_NAN
748 }
749
750 fn is_canonical_nan(&self) -> bool {
751 (self.to_bits() & 0x7fff_ffff_ffff_ffff) == 0x7ff8_0000_0000_0000
752 }
753}