<commit_before>extern crate concurrent;

pub use self::concurrent::collections::BoundedBlockingQueue;
pub use self::concurrent::collections::UnboundedBlockingQueue;

pub use std::sync::Arc;
pub use std::sync::atomic::{AtomicBool, Ordering};
pub use std::time::Duration;

pub use std::thread;
pub use std::sync::mpsc;

describe! bounded_blocking_queue_test {

    before_each {
        const CAPACITY : usize = 16;
        let queue: BoundedBlockingQueue<i32> = BoundedBlockingQueue::with_capacity(CAPACITY);
    }

    it "should create a new queue with default capacity" {
        let queue: BoundedBlockingQueue<i32> = BoundedBlockingQueue::new();
        assert_eq!(queue.remaning_capacity(), 15);
    }

    it "should create a new empty queue" {
        assert!(queue.is_empty());
        assert_eq!(queue.size(), 0);
    }

    it "should have capacity that is always highest power of two minus one" {
        let queue: BoundedBlockingQueue<i32> = BoundedBlockingQueue::with_capacity(6);
        assert_eq!(queue.remaning_capacity(), 7);

        let queue: BoundedBlockingQueue<i32> = BoundedBlockingQueue::with_capacity(10);
        assert_eq!(queue.remaning_capacity(), 15);

        let queue: BoundedBlockingQueue<i32> = BoundedBlockingQueue::with_capacity(20);
        assert_eq!(queue.remaning_capacity(), 31);

        let queue: BoundedBlockingQueue<i32> = BoundedBlockingQueue::with_capacity(40);
        assert_eq!(queue.remaning_capacity(), 63);
    }

    it "should increase size when insert into queue" {
        let old_size = queue.size();
        queue.enqueue(1);

        assert_eq!(queue.size(), old_size + 1);
    }

    it "should contain value that was equeued" {
        queue.enqueue(1);

        assert!(queue.contains(1));
    }

    it "should contain values that were enqueued" {
        queue.enqueue(10);
        queue.enqueue(20);
        queue.enqueue(30);
        queue.enqueue(40);

        assert!(queue.contains(10));
        assert!(queue.contains(20));
        assert!(queue.contains(30));
        assert!(queue.contains(40));
    }

    it "should not contain value that was not enqueued" {
        assert!(!queue.contains(10));
    }

    it "should decrise size when remove from queue" {
        queue.enqueue(1);
        let old_size = queue.size();

        queue.dequeue();
        assert_eq!(queue.size(), old_size - 1);
    }

    it "should dequeue first enqueued value" {
        queue.enqueue(10);
        queue.enqueue(20);
        queue.enqueue(30);

        assert_eq!(queue.dequeue(), 10);
        assert_eq!(queue.dequeue(), 20);
        assert_eq!(queue.dequeue(), 30);
    }

    it "should insert offered value if queue not full" {
        assert!(queue.offer(1));
        assert!(queue.contains(1));
    }

    it "should peek first element but not delete" {
        queue.enqueue(1);
        queue.enqueue(2);
        queue.enqueue(3);

        assert_eq!(queue.peek(), Some(1));
        assert_eq!(queue.peek(), Some(1));
    }

    it "should calculate correct size when alot insertions and deletions" {
        let size = queue.size();
        for i in 1..8 {
            queue.enqueue(i);
            assert_eq!(queue.size(), size + (i as usize));
        }

        let size = queue.size();
        for i in 1..6 {
            queue.dequeue();
            assert_eq!(queue.size(), size - (i as usize));
        }

        let size = queue.size();
        for i in 1..8 {
            queue.enqueue(i);
            assert_eq!(queue.size(), size + (i as usize));
        }

        let size = queue.size();
        for i in 1..6 {
            queue.dequeue();
            assert_eq!(queue.size(), size - (i as usize));
        }

        let size = queue.size();
        for i in 1..8 {
            queue.enqueue(i);
            assert_eq!(queue.size(), size + (i as usize));
        }

        let size = queue.size();
        for i in 1..6 {
            queue.dequeue();
            assert_eq!(queue.size(), size - (i as usize));
        }

        let size = queue.size();
        for i in 1..8 {
            queue.enqueue(i);
            assert_eq!(queue.size(), size + (i as usize));
        }

        let size = queue.size();
        for i in 1..6 {
            queue.dequeue();
            assert_eq!(queue.size(), size - (i as usize));
        }
    }

    //FAILED test
    //some times threads get stuck
    /*
    it "should threads wait each other when queue is empty or full" {
        let arc = Arc::new(queue);
        let flag = Arc::new(AtomicBool::new(false));

        let data = arc.clone();
        let ready = flag.clone();
        let jh = thread::spawn(
            move || {
                let mut counter = 0;
                let mut sum = 0;
                while !data.is_empty() || !ready.load(Ordering::Relaxed) {
                    let datum = data.dequeue();
                    assert_eq!(datum, counter);
                    counter += 1;
                    sum += datum;
                }
                assert_eq!(sum, 4950);
            }
        );

        for i in 0..100 {
            arc.enqueue(i);
        }

        flag.store(true, Ordering::Relaxed);

        assert!(jh.join().is_ok());
    }
    */
}

describe! unbounded_blocking_queue_test {

    before_each {
        let mut queue = UnboundedBlockingQueue::new();
    }

    it "should create new empty unbounded queue" {
        assert_eq!(queue.size(), 0);
        assert!(queue.is_empty());
    }

    it "should increase queue size when enqueue value" {
        let old_size = queue.size();
        queue.enqueue(1);

        assert_eq!(queue.size(), old_size + 1);
    }

    it "should decrease queue size when dequeue value" {
        queue.enqueue(1);
        let old_size = queue.size();
        queue.dequeue();

        assert_eq!(queue.size(), old_size - 1);
    }

    it "should contain value that was enqueued" {
        queue.enqueue(1);
        assert!(queue.contains(1));
    }

    it "should contain values that were enqueued" {
        queue.enqueue(10);
        queue.enqueue(20);
        queue.enqueue(30);
        queue.enqueue(40);

        assert!(queue.contains(10));
        assert!(queue.contains(20));
        assert!(queue.contains(30));
        assert!(queue.contains(40));
    }

    it "should not contain value that was not enqueued" {
        assert!(!queue.contains(10));
    }
}
<commit_msg>rewrite test for enq deq value<commit_after>extern crate concurrent;

pub use self::concurrent::collections::BoundedBlockingQueue;
pub use self::concurrent::collections::UnboundedBlockingQueue;

pub use std::sync::Arc;
pub use std::sync::atomic::{AtomicBool, Ordering};
pub use std::time::Duration;

pub use std::thread;
pub use std::sync::mpsc;

describe! bounded_blocking_queue_test {

    before_each {
        const CAPACITY : usize = 16;
        let queue: BoundedBlockingQueue<i32> = BoundedBlockingQueue::with_capacity(CAPACITY);
    }

    it "should create a new queue with default capacity" {
        let queue: BoundedBlockingQueue<i32> = BoundedBlockingQueue::new();
        assert_eq!(queue.remaning_capacity(), 15);
    }

    it "should create a new empty queue" {
        assert!(queue.is_empty());
        assert_eq!(queue.size(), 0);
    }

    it "should have capacity that is always highest power of two minus one" {
        let queue: BoundedBlockingQueue<i32> = BoundedBlockingQueue::with_capacity(6);
        assert_eq!(queue.remaning_capacity(), 7);

        let queue: BoundedBlockingQueue<i32> = BoundedBlockingQueue::with_capacity(10);
        assert_eq!(queue.remaning_capacity(), 15);

        let queue: BoundedBlockingQueue<i32> = BoundedBlockingQueue::with_capacity(20);
        assert_eq!(queue.remaning_capacity(), 31);

        let queue: BoundedBlockingQueue<i32> = BoundedBlockingQueue::with_capacity(40);
        assert_eq!(queue.remaning_capacity(), 63);
    }

    it "should increase size when insert into queue" {
        let old_size = queue.size();
        queue.enqueue(1);

        assert_eq!(queue.size(), old_size + 1);
    }

    it "should contain value that was equeued" {
        queue.enqueue(1);

        assert!(queue.contains(1));
    }

    it "should contain values that were enqueued" {
        queue.enqueue(10);
        queue.enqueue(20);
        queue.enqueue(30);
        queue.enqueue(40);

        assert!(queue.contains(10));
        assert!(queue.contains(20));
        assert!(queue.contains(30));
        assert!(queue.contains(40));
    }

    it "should not contain value that was not enqueued" {
        assert!(!queue.contains(10));
    }

    it "should decrise size when remove from queue" {
        queue.enqueue(1);
        let old_size = queue.size();

        queue.dequeue();
        assert_eq!(queue.size(), old_size - 1);
    }

    it "should dequeue first enqueued value" {
        queue.enqueue(10);
        queue.enqueue(20);
        queue.enqueue(30);

        assert_eq!(queue.dequeue(), 10);
        assert_eq!(queue.dequeue(), 20);
        assert_eq!(queue.dequeue(), 30);
    }

    it "should insert offered value if queue not full" {
        assert!(queue.offer(1));
        assert!(queue.contains(1));
    }

    it "should peek first element but not delete" {
        queue.enqueue(1);
        queue.enqueue(2);
        queue.enqueue(3);

        assert_eq!(queue.peek(), Some(1));
        assert_eq!(queue.peek(), Some(1));
    }

    it "should calculate correct size when alot insertions and deletions" {
        let size = queue.size();
        for i in 1..8 {
            queue.enqueue(i);
            assert_eq!(queue.size(), size + (i as usize));
        }

        let size = queue.size();
        for i in 1..6 {
            queue.dequeue();
            assert_eq!(queue.size(), size - (i as usize));
        }

        let size = queue.size();
        for i in 1..8 {
            queue.enqueue(i);
            assert_eq!(queue.size(), size + (i as usize));
        }

        let size = queue.size();
        for i in 1..6 {
            queue.dequeue();
            assert_eq!(queue.size(), size - (i as usize));
        }

        let size = queue.size();
        for i in 1..8 {
            queue.enqueue(i);
            assert_eq!(queue.size(), size + (i as usize));
        }

        let size = queue.size();
        for i in 1..6 {
            queue.dequeue();
            assert_eq!(queue.size(), size - (i as usize));
        }

        let size = queue.size();
        for i in 1..8 {
            queue.enqueue(i);
            assert_eq!(queue.size(), size + (i as usize));
        }

        let size = queue.size();
        for i in 1..6 {
            queue.dequeue();
            assert_eq!(queue.size(), size - (i as usize));
        }
    }

    it "should threads wait each other when queue is empty or full" {
        const NUMBER_OF_THREADS: usize = 10;
        let arc = Arc::new(queue);
        let mut results = Vec::with_capacity(NUMBER_OF_THREADS);

        for _ in 0..NUMBER_OF_THREADS {
            let data = arc.clone();
            let jh = thread::spawn(
                move || {
                    assert_eq!(data.dequeue(), 1);
                    data.enqueue(1);
                }
            );
            results.push(jh);
        }

        arc.enqueue(1);

        for jh in results {
            assert!(jh.join().is_ok());
        }

        assert_eq!(arc.dequeue(), 1);
    }
}

describe! unbounded_blocking_queue_test {

    before_each {
        let mut queue = UnboundedBlockingQueue::new();
    }

    it "should create new empty unbounded queue" {
        assert_eq!(queue.size(), 0);
        assert!(queue.is_empty());
    }

    it "should increase queue size when enqueue value" {
        let old_size = queue.size();
        queue.enqueue(1);

        assert_eq!(queue.size(), old_size + 1);
    }

    it "should decrease queue size when dequeue value" {
        queue.enqueue(1);
        let old_size = queue.size();
        queue.dequeue();

        assert_eq!(queue.size(), old_size - 1);
    }

    it "should contain value that was enqueued" {
        queue.enqueue(1);
        assert!(queue.contains(1));
    }

    it "should contain values that were enqueued" {
        queue.enqueue(10);
        queue.enqueue(20);
        queue.enqueue(30);
        queue.enqueue(40);

        assert!(queue.contains(10));
        assert!(queue.contains(20));
        assert!(queue.contains(30));
        assert!(queue.contains(40));
    }

    it "should not contain value that was not enqueued" {
        assert!(!queue.contains(10));
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

#![deny(warnings)]
// Enable all clippy lints except for many of the pedantic ones. It's a shame this needs to be copied and pasted across crates, but there doesn't appear to be a way to include inner attributes from a common source.
#![deny(
  clippy::all,
  clippy::default_trait_access,
  clippy::expl_impl_clone_on_copy,
  clippy::if_not_else,
  clippy::needless_continue,
  clippy::single_match_else,
  clippy::unseparated_literal_suffix,
  clippy::used_underscore_binding
)]
// It is often more clear to show that nothing is being moved.
#![allow(clippy::match_ref_pats)]
// Subjective style.
#![allow(
  clippy::len_without_is_empty,
  clippy::redundant_field_names,
  clippy::too_many_arguments
)]
// Default isn't as big a deal as people seem to think it is.
#![allow(
  clippy::new_without_default,
  clippy::new_without_default_derive,
  clippy::new_ret_no_self
)]
// Arc<Mutex> can be more clear than needing to grok Orderings:
#![allow(clippy::mutex_atomic)]

use std::sync::Arc;

use parking_lot::RwLock;

///
/// Resettable is a lazily computed value which can be reset, so that it can be lazily computed
/// again next time it is needed.
///
/// This is useful because we fork without execing a lot in the engine, and before doing so, we need
/// to reset any references which hide background threads, so that forked processes don't inherit
/// pointers to threads from the parent process which will not exist in the forked process.
///
pub struct Resettable<T> {
  val: Arc<RwLock<Option<T>>>,
  make: Arc<dyn Fn() -> T>,
}

// Sadly we need to manualy implement Clone because of https://github.com/rust-lang/rust/issues/26925
impl<T> Clone for Resettable<T> {
  fn clone(&self) -> Self {
    Resettable {
      val: self.val.clone(),
      make: self.make.clone(),
    }
  }
}

unsafe impl<T> Send for Resettable<T> {}
unsafe impl<T> Sync for Resettable<T> {}

impl<T> Resettable<T>
where
  T: Send + Sync,
{
  pub fn new<F: Fn() -> T + 'static>(make: F) -> Resettable<T> {
    let val = (make)();
    Resettable {
      val: Arc::new(RwLock::new(Some(val))),
      make: Arc::new(make),
    }
  }

  pub fn with<O, F: FnOnce(&T) -> O>(&self, f: F) -> O {
    let val_opt = self.val.read();
    let val = val_opt
      .as_ref()
      .unwrap_or_else(|| panic!("A Resettable value cannot be used while it is shutdown."));
    f(val)
  }

  ///
  /// Run a function while the Resettable resource is cleared, and then recreate it afterward.
  ///
  pub fn with_reset<F, O>(&self, f: F) -> O
  where
    F: FnOnce() -> O,
  {
    let mut val = self.val.write();
    *val = None;
    let t = f();
    *val = Some((self.make)());
    t
  }
}

impl<T> Resettable<T>
where
  T: Clone + Send + Sync,
{
  ///
  /// Callers should probably use `with` rather than `get`.
  /// Having an externalized `get` and using Clone like this is problematic: if there
  /// might be references/Clones of the field outside of the lock on the resource, then we can't
  /// be sure that dropping it will actually deallocate the resource.
  ///
  pub fn get(&self) -> T {
    let val_opt = self.val.read();
    let val = val_opt
      .as_ref()
      .unwrap_or_else(|| panic!("A Resettable value cannot be used while it is shutdown."));
    val.clone()
  }
}
<commit_msg>Make Resettable lazy again (#7222)<commit_after>// Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

#![deny(warnings)]
// Enable all clippy lints except for many of the pedantic ones. It's a shame this needs to be copied and pasted across crates, but there doesn't appear to be a way to include inner attributes from a common source.
#![deny(
  clippy::all,
  clippy::default_trait_access,
  clippy::expl_impl_clone_on_copy,
  clippy::if_not_else,
  clippy::needless_continue,
  clippy::single_match_else,
  clippy::unseparated_literal_suffix,
  clippy::used_underscore_binding
)]
// It is often more clear to show that nothing is being moved.
#![allow(clippy::match_ref_pats)]
// Subjective style.
#![allow(
  clippy::len_without_is_empty,
  clippy::redundant_field_names,
  clippy::too_many_arguments
)]
// Default isn't as big a deal as people seem to think it is.
#![allow(
  clippy::new_without_default,
  clippy::new_without_default_derive,
  clippy::new_ret_no_self
)]
// Arc<Mutex> can be more clear than needing to grok Orderings:
#![allow(clippy::mutex_atomic)]

use std::sync::Arc;

use parking_lot::RwLock;

///
/// Resettable is a lazily computed value which can be reset, so that it can be lazily computed
/// again next time it is needed.
///
/// This is useful because we fork without execing a lot in the engine, and before doing so, we need
/// to reset any references which hide background threads, so that forked processes don't inherit
/// pointers to threads from the parent process which will not exist in the forked process.
///
pub struct Resettable<T> {
  val: Arc<RwLock<Option<T>>>,
  make: Arc<dyn Fn() -> T>,
}

// Sadly we need to manualy implement Clone because of https://github.com/rust-lang/rust/issues/26925
impl<T> Clone for Resettable<T> {
  fn clone(&self) -> Self {
    Resettable {
      val: self.val.clone(),
      make: self.make.clone(),
    }
  }
}

unsafe impl<T> Send for Resettable<T> {}
unsafe impl<T> Sync for Resettable<T> {}

impl<T> Resettable<T>
where
  T: Send + Sync,
{
  pub fn new<F: Fn() -> T + 'static>(make: F) -> Resettable<T> {
    Resettable {
      val: Arc::new(RwLock::new(None)),
      make: Arc::new(make),
    }
  }

  ///
  /// Execute f with the value in the Resettable.
  /// May lazily initialize the value in the Resettable.
  ///
  /// TODO Explore the use of parking_lot::RWLock::upgradable_read
  /// to avoid reacquiring the lock for initialization.
  /// This can be used if we are sure that a deadlock won't happen
  /// when two readers are trying to upgrade at the same time.
  ///
  pub fn with<O, F: FnOnce(&T) -> O>(&self, f: F) -> O {
    {
      let val_opt = self.val.read();
      if let Some(val) = val_opt.as_ref() {
        return f(val);
      }
    }
    let mut val_write_opt = self.val.write();
    if val_write_opt.as_ref().is_none() {
      *val_write_opt = Some((self.make)())
    }
    f(val_write_opt.as_ref().unwrap())
  }

  ///
  /// Run a function while the Resettable resource is cleared, and then recreate it afterward.
  ///
  pub fn with_reset<F, O>(&self, f: F) -> O
  where
    F: FnOnce() -> O,
  {
    let mut val = self.val.write();
    *val = None;
    f()
  }
}

impl<T> Resettable<T>
where
  T: Clone + Send + Sync,
{
  ///
  /// Callers should probably use `with` rather than `get`.
  /// Having an externalized `get` and using Clone like this is problematic: if there
  /// might be references/Clones of the field outside of the lock on the resource, then we can't
  /// be sure that dropping it will actually deallocate the resource.
  ///
  pub fn get(&self) -> T {
    self.with(T::clone)
  }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions, TargetResult};
use spec::abi::{Abi};

pub fn target() -> TargetResult {
    Ok(Target {
        data_layout: "e-m:e-p:32:32-i64:64-n32-S128".to_string(),
        llvm_target: "riscv32".to_string(),
        target_endian: "little".to_string(),
        target_pointer_width: "32".to_string(),
        target_c_int_width: "32".to_string(),
        target_os: "none".to_string(),
        target_env: "".to_string(),
        target_vendor: "unknown".to_string(),
        arch: "riscv32".to_string(),
        linker_flavor: LinkerFlavor::Ld,

        options: TargetOptions {
            linker: Some("riscv32-unknown-elf-ld".to_string()),
            cpu: "generic-rv32".to_string(),
            max_atomic_width: Some(32),
            atomic_cas: false, // incomplete +a extension
            features: "+m,+a".to_string(), // disable +c extension
            executables: true,
            panic_strategy: PanicStrategy::Abort,
            relocation_model: "static".to_string(),
            abi_blacklist: vec![
                Abi::Cdecl,
                Abi::Stdcall,
                Abi::Fastcall,
                Abi::Vectorcall,
                Abi::Thiscall,
                Abi::Aapcs,
                Abi::Win64,
                Abi::SysV64,
                Abi::PtxKernel,
                Abi::Msp430Interrupt,
                Abi::X86Interrupt,
            ],
            .. Default::default()
        },
    })
}
<commit_msg>set emit_debug_gdb_scripts: false for riscv32imac-unknown-none target<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions, TargetResult};
use spec::abi::{Abi};

pub fn target() -> TargetResult {
    Ok(Target {
        data_layout: "e-m:e-p:32:32-i64:64-n32-S128".to_string(),
        llvm_target: "riscv32".to_string(),
        target_endian: "little".to_string(),
        target_pointer_width: "32".to_string(),
        target_c_int_width: "32".to_string(),
        target_os: "none".to_string(),
        target_env: "".to_string(),
        target_vendor: "unknown".to_string(),
        arch: "riscv32".to_string(),
        linker_flavor: LinkerFlavor::Ld,

        options: TargetOptions {
            linker: Some("riscv32-unknown-elf-ld".to_string()),
            cpu: "generic-rv32".to_string(),
            max_atomic_width: Some(32),
            atomic_cas: false, // incomplete +a extension
            features: "+m,+a".to_string(), // disable +c extension
            executables: true,
            panic_strategy: PanicStrategy::Abort,
            relocation_model: "static".to_string(),
            emit_debug_gdb_scripts: false,
            abi_blacklist: vec![
                Abi::Cdecl,
                Abi::Stdcall,
                Abi::Fastcall,
                Abi::Vectorcall,
                Abi::Thiscall,
                Abi::Aapcs,
                Abi::Win64,
                Abi::SysV64,
                Abi::PtxKernel,
                Abi::Msp430Interrupt,
                Abi::X86Interrupt,
            ],
            .. Default::default()
        },
    })
}
<|endoftext|>


<commit_before>use crate::mir::interpret::{sign_extend, truncate, InterpErrorInfo, InterpResult};
use crate::throw_ub;
use rustc_apfloat::ieee::{Double, Single};
use rustc_apfloat::Float;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use rustc_target::abi::{Size, TargetDataLayout};
use std::convert::{TryFrom, TryInto};
use std::fmt;

#[derive(Copy, Clone)]
/// A type for representing any integer. Only used for printing.
pub struct ConstInt {
    /// The "untyped" variant of `ConstInt`.
    int: ScalarInt,
    /// Whether the value is of a signed integer type.
    signed: bool,
    /// Whether the value is a `usize` or `isize` type.
    is_ptr_sized_integral: bool,
}

impl ConstInt {
    pub fn new(int: ScalarInt, signed: bool, is_ptr_sized_integral: bool) -> Self {
        Self { int, signed, is_ptr_sized_integral }
    }
}

impl std::fmt::Debug for ConstInt {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let Self { int, signed, is_ptr_sized_integral } = *self;
        let size = int.size().bytes();
        let raw = int.data;
        if signed {
            let bit_size = size * 8;
            let min = 1u128 << (bit_size - 1);
            let max = min - 1;
            if raw == min {
                match (size, is_ptr_sized_integral) {
                    (_, true) => write!(fmt, "isize::MIN"),
                    (1, _) => write!(fmt, "i8::MIN"),
                    (2, _) => write!(fmt, "i16::MIN"),
                    (4, _) => write!(fmt, "i32::MIN"),
                    (8, _) => write!(fmt, "i64::MIN"),
                    (16, _) => write!(fmt, "i128::MIN"),
                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
                }
            } else if raw == max {
                match (size, is_ptr_sized_integral) {
                    (_, true) => write!(fmt, "isize::MAX"),
                    (1, _) => write!(fmt, "i8::MAX"),
                    (2, _) => write!(fmt, "i16::MAX"),
                    (4, _) => write!(fmt, "i32::MAX"),
                    (8, _) => write!(fmt, "i64::MAX"),
                    (16, _) => write!(fmt, "i128::MAX"),
                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
                }
            } else {
                match size {
                    1 => write!(fmt, "{}", raw as i8)?,
                    2 => write!(fmt, "{}", raw as i16)?,
                    4 => write!(fmt, "{}", raw as i32)?,
                    8 => write!(fmt, "{}", raw as i64)?,
                    16 => write!(fmt, "{}", raw as i128)?,
                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
                }
                if fmt.alternate() {
                    match (size, is_ptr_sized_integral) {
                        (_, true) => write!(fmt, "_isize")?,
                        (1, _) => write!(fmt, "_i8")?,
                        (2, _) => write!(fmt, "_i16")?,
                        (4, _) => write!(fmt, "_i32")?,
                        (8, _) => write!(fmt, "_i64")?,
                        (16, _) => write!(fmt, "_i128")?,
                        _ => bug!(),
                    }
                }
                Ok(())
            }
        } else {
            let max = truncate(u128::MAX, Size::from_bytes(size));
            if raw == max {
                match (size, is_ptr_sized_integral) {
                    (_, true) => write!(fmt, "usize::MAX"),
                    (1, _) => write!(fmt, "u8::MAX"),
                    (2, _) => write!(fmt, "u16::MAX"),
                    (4, _) => write!(fmt, "u32::MAX"),
                    (8, _) => write!(fmt, "u64::MAX"),
                    (16, _) => write!(fmt, "u128::MAX"),
                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
                }
            } else {
                match size {
                    1 => write!(fmt, "{}", raw as u8)?,
                    2 => write!(fmt, "{}", raw as u16)?,
                    4 => write!(fmt, "{}", raw as u32)?,
                    8 => write!(fmt, "{}", raw as u64)?,
                    16 => write!(fmt, "{}", raw as u128)?,
                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
                }
                if fmt.alternate() {
                    match (size, is_ptr_sized_integral) {
                        (_, true) => write!(fmt, "_usize")?,
                        (1, _) => write!(fmt, "_u8")?,
                        (2, _) => write!(fmt, "_u16")?,
                        (4, _) => write!(fmt, "_u32")?,
                        (8, _) => write!(fmt, "_u64")?,
                        (16, _) => write!(fmt, "_u128")?,
                        _ => bug!(),
                    }
                }
                Ok(())
            }
        }
    }
}

// FIXME: reuse in `super::int::ConstInt` and `Scalar::Bits`
/// The raw bytes of a simple value.
///
/// This is a packed struct in order to allow this type to be optimally embedded in enums
/// (like Scalar).
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(packed)]
pub struct ScalarInt {
    /// The first `size` bytes of `data` are the value.
    /// Do not try to read less or more bytes than that. The remaining bytes must be 0.
    data: u128,
    size: u8,
}

// Cannot derive these, as the derives take references to the fields, and we
// can't take references to fields of packed structs.
impl<CTX> crate::ty::HashStable<CTX> for ScalarInt {
    fn hash_stable(&self, hcx: &mut CTX, hasher: &mut crate::ty::StableHasher) {
        // Using a block `{self.data}` here to force a copy instead of using `self.data`
        // directly, because `hash_stable` takes `&self` and would thus borrow `self.data`.
        // Since `Self` is a packed struct, that would create a possibly unaligned reference,
        // which is UB on a lot of platforms.
        { self.data }.hash_stable(hcx, hasher);
        self.size.hash_stable(hcx, hasher);
    }
}

impl<S: Encoder> Encodable<S> for ScalarInt {
    fn encode(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_u128(self.data)?;
        s.emit_u8(self.size)
    }
}

impl<D: Decoder> Decodable<D> for ScalarInt {
    fn decode(d: &mut D) -> Result<ScalarInt, D::Error> {
        Ok(ScalarInt { data: d.read_u128()?, size: d.read_u8()? })
    }
}

impl ScalarInt {
    pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: 1 };

    pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: 1 };

    pub const ZST: ScalarInt = ScalarInt { data: 0_u128, size: 0 };

    #[inline]
    pub fn size(self) -> Size {
        Size::from_bytes(self.size)
    }

    /// Make sure the `data` fits in `size`.
    /// This is guaranteed by all constructors here, but since the enum variants are public,
    /// it could still be violated (even though no code outside this file should
    /// construct `Scalar`s).
    #[inline(always)]
    fn check_data(self) {
        // Using a block `{self.data}` here to force a copy instead of using `self.data`
        // directly, because `assert_eq` takes references to its arguments and formatting
        // arguments and would thus borrow `self.data`. Since `Self`
        // is a packed struct, that would create a possibly unaligned reference, which
        // is UB on a lot of platforms.
        debug_assert_eq!(
            truncate(self.data, self.size()),
            { self.data },
            "Scalar value {:#x} exceeds size of {} bytes",
            { self.data },
            self.size
        );
    }

    #[inline]
    pub fn zst() -> Self {
        Self::null(Size::ZERO)
    }

    #[inline]
    pub fn null(size: Size) -> Self {
        Self { data: 0, size: size.bytes() as u8 }
    }

    pub(crate) fn ptr_sized_op<'tcx>(
        self,
        dl: &TargetDataLayout,
        f_int: impl FnOnce(u64) -> InterpResult<'tcx, u64>,
    ) -> InterpResult<'tcx, Self> {
        assert_eq!(u64::from(self.size), dl.pointer_size.bytes());
        Ok(Self { data: u128::from(f_int(u64::try_from(self.data).unwrap())?), size: self.size })
    }

    #[inline]
    pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
        let data = i.into();
        if truncate(data, size) == data {
            Some(Self { data, size: size.bytes() as u8 })
        } else {
            None
        }
    }

    #[inline]
    pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
        let i = i.into();
        // `into` performed sign extension, we have to truncate
        let truncated = truncate(i as u128, size);
        if sign_extend(truncated, size) as i128 == i {
            Some(Self { data: truncated, size: size.bytes() as u8 })
        } else {
            None
        }
    }

    #[inline]
    pub fn assert_bits(self, target_size: Size) -> u128 {
        assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
        assert_eq!(target_size.bytes(), u64::from(self.size));
        self.check_data();
        self.data
    }

    #[inline]
    pub fn to_bits(self, target_size: Size) -> InterpResult<'static, u128> {
        assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
        if target_size.bytes() != u64::from(self.size) {
            throw_ub!(ScalarSizeMismatch {
                target_size: target_size.bytes(),
                data_size: u64::from(self.size),
            });
        }
        self.check_data();
        Ok(self.data)
    }
}

macro_rules! from {
    ($($ty:ty),*) => {
        $(
            impl From<$ty> for ScalarInt {
                #[inline]
                fn from(u: $ty) -> Self {
                    Self {
                        data: u128::from(u),
                        size: std::mem::size_of::<$ty>() as u8,
                    }
                }
            }
        )*
    }
}

macro_rules! try_from {
    ($($ty:ty),*) => {
        $(
            impl TryFrom<ScalarInt> for $ty {
                type Error = InterpErrorInfo<'static>;
                #[inline]
                fn try_from(int: ScalarInt) -> InterpResult<'static, Self> {
                    int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>())).map(|u| u.try_into().unwrap())
                }
            }
        )*
    }
}

from!(u8, u16, u32, u64, u128, bool);
try_from!(u8, u16, u32, u64, u128);

impl From<char> for ScalarInt {
    #[inline]
    fn from(c: char) -> Self {
        Self { data: c as u128, size: std::mem::size_of::<char>() as u8 }
    }
}

impl TryFrom<ScalarInt> for char {
    type Error = InterpErrorInfo<'static>;
    #[inline]
    fn try_from(int: ScalarInt) -> InterpResult<'static, Self> {
        int.to_bits(Size::from_bytes(std::mem::size_of::<char>()))
            .map(|u| char::from_u32(u.try_into().unwrap()).unwrap())
    }
}

impl From<Single> for ScalarInt {
    #[inline]
    fn from(f: Single) -> Self {
        // We trust apfloat to give us properly truncated data.
        Self { data: f.to_bits(), size: 4 }
    }
}

impl TryFrom<ScalarInt> for Single {
    type Error = InterpErrorInfo<'static>;
    #[inline]
    fn try_from(int: ScalarInt) -> InterpResult<'static, Self> {
        int.to_bits(Size::from_bytes(4)).map(Self::from_bits)
    }
}

impl From<Double> for ScalarInt {
    #[inline]
    fn from(f: Double) -> Self {
        // We trust apfloat to give us properly truncated data.
        Self { data: f.to_bits(), size: 8 }
    }
}

impl TryFrom<ScalarInt> for Double {
    type Error = InterpErrorInfo<'static>;
    #[inline]
    fn try_from(int: ScalarInt) -> InterpResult<'static, Self> {
        int.to_bits(Size::from_bytes(8)).map(Self::from_bits)
    }
}

impl fmt::Debug for ScalarInt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.size == 0 {
            self.check_data();
            write!(f, "<ZST>")
        } else {
            write!(f, "0x{:x}", self)
        }
    }
}

impl fmt::LowerHex for ScalarInt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.check_data();
        // Format as hex number wide enough to fit any value of the given `size`.
        // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
        // Using a block `{self.data}` here to force a copy instead of using `self.data`
        // directly, because `write!` takes references to its formatting arguments and
        // would thus borrow `self.data`. Since `Self`
        // is a packed struct, that would create a possibly unaligned reference, which
        // is UB on a lot of platforms.
        write!(f, "{:01$x}", { self.data }, self.size as usize * 2)
    }
}

impl fmt::UpperHex for ScalarInt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.check_data();
        // Format as hex number wide enough to fit any value of the given `size`.
        // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
        // Using a block `{self.data}` here to force a copy instead of using `self.data`
        // directly, because `write!` takes references to its formatting arguments and
        // would thus borrow `self.data`. Since `Self`
        // is a packed struct, that would create a possibly unaligned reference, which
        // is UB on a lot of platforms.
        write!(f, "{:01$X}", { self.data }, self.size as usize * 2)
    }
}
<commit_msg>Remove outdated FIXME<commit_after>use crate::mir::interpret::{sign_extend, truncate, InterpErrorInfo, InterpResult};
use crate::throw_ub;
use rustc_apfloat::ieee::{Double, Single};
use rustc_apfloat::Float;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use rustc_target::abi::{Size, TargetDataLayout};
use std::convert::{TryFrom, TryInto};
use std::fmt;

#[derive(Copy, Clone)]
/// A type for representing any integer. Only used for printing.
pub struct ConstInt {
    /// The "untyped" variant of `ConstInt`.
    int: ScalarInt,
    /// Whether the value is of a signed integer type.
    signed: bool,
    /// Whether the value is a `usize` or `isize` type.
    is_ptr_sized_integral: bool,
}

impl ConstInt {
    pub fn new(int: ScalarInt, signed: bool, is_ptr_sized_integral: bool) -> Self {
        Self { int, signed, is_ptr_sized_integral }
    }
}

impl std::fmt::Debug for ConstInt {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let Self { int, signed, is_ptr_sized_integral } = *self;
        let size = int.size().bytes();
        let raw = int.data;
        if signed {
            let bit_size = size * 8;
            let min = 1u128 << (bit_size - 1);
            let max = min - 1;
            if raw == min {
                match (size, is_ptr_sized_integral) {
                    (_, true) => write!(fmt, "isize::MIN"),
                    (1, _) => write!(fmt, "i8::MIN"),
                    (2, _) => write!(fmt, "i16::MIN"),
                    (4, _) => write!(fmt, "i32::MIN"),
                    (8, _) => write!(fmt, "i64::MIN"),
                    (16, _) => write!(fmt, "i128::MIN"),
                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
                }
            } else if raw == max {
                match (size, is_ptr_sized_integral) {
                    (_, true) => write!(fmt, "isize::MAX"),
                    (1, _) => write!(fmt, "i8::MAX"),
                    (2, _) => write!(fmt, "i16::MAX"),
                    (4, _) => write!(fmt, "i32::MAX"),
                    (8, _) => write!(fmt, "i64::MAX"),
                    (16, _) => write!(fmt, "i128::MAX"),
                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
                }
            } else {
                match size {
                    1 => write!(fmt, "{}", raw as i8)?,
                    2 => write!(fmt, "{}", raw as i16)?,
                    4 => write!(fmt, "{}", raw as i32)?,
                    8 => write!(fmt, "{}", raw as i64)?,
                    16 => write!(fmt, "{}", raw as i128)?,
                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
                }
                if fmt.alternate() {
                    match (size, is_ptr_sized_integral) {
                        (_, true) => write!(fmt, "_isize")?,
                        (1, _) => write!(fmt, "_i8")?,
                        (2, _) => write!(fmt, "_i16")?,
                        (4, _) => write!(fmt, "_i32")?,
                        (8, _) => write!(fmt, "_i64")?,
                        (16, _) => write!(fmt, "_i128")?,
                        _ => bug!(),
                    }
                }
                Ok(())
            }
        } else {
            let max = truncate(u128::MAX, Size::from_bytes(size));
            if raw == max {
                match (size, is_ptr_sized_integral) {
                    (_, true) => write!(fmt, "usize::MAX"),
                    (1, _) => write!(fmt, "u8::MAX"),
                    (2, _) => write!(fmt, "u16::MAX"),
                    (4, _) => write!(fmt, "u32::MAX"),
                    (8, _) => write!(fmt, "u64::MAX"),
                    (16, _) => write!(fmt, "u128::MAX"),
                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
                }
            } else {
                match size {
                    1 => write!(fmt, "{}", raw as u8)?,
                    2 => write!(fmt, "{}", raw as u16)?,
                    4 => write!(fmt, "{}", raw as u32)?,
                    8 => write!(fmt, "{}", raw as u64)?,
                    16 => write!(fmt, "{}", raw as u128)?,
                    _ => bug!("ConstInt 0x{:x} with size = {} and signed = {}", raw, size, signed),
                }
                if fmt.alternate() {
                    match (size, is_ptr_sized_integral) {
                        (_, true) => write!(fmt, "_usize")?,
                        (1, _) => write!(fmt, "_u8")?,
                        (2, _) => write!(fmt, "_u16")?,
                        (4, _) => write!(fmt, "_u32")?,
                        (8, _) => write!(fmt, "_u64")?,
                        (16, _) => write!(fmt, "_u128")?,
                        _ => bug!(),
                    }
                }
                Ok(())
            }
        }
    }
}

/// The raw bytes of a simple value.
///
/// This is a packed struct in order to allow this type to be optimally embedded in enums
/// (like Scalar).
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(packed)]
pub struct ScalarInt {
    /// The first `size` bytes of `data` are the value.
    /// Do not try to read less or more bytes than that. The remaining bytes must be 0.
    data: u128,
    size: u8,
}

// Cannot derive these, as the derives take references to the fields, and we
// can't take references to fields of packed structs.
impl<CTX> crate::ty::HashStable<CTX> for ScalarInt {
    fn hash_stable(&self, hcx: &mut CTX, hasher: &mut crate::ty::StableHasher) {
        // Using a block `{self.data}` here to force a copy instead of using `self.data`
        // directly, because `hash_stable` takes `&self` and would thus borrow `self.data`.
        // Since `Self` is a packed struct, that would create a possibly unaligned reference,
        // which is UB on a lot of platforms.
        { self.data }.hash_stable(hcx, hasher);
        self.size.hash_stable(hcx, hasher);
    }
}

impl<S: Encoder> Encodable<S> for ScalarInt {
    fn encode(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_u128(self.data)?;
        s.emit_u8(self.size)
    }
}

impl<D: Decoder> Decodable<D> for ScalarInt {
    fn decode(d: &mut D) -> Result<ScalarInt, D::Error> {
        Ok(ScalarInt { data: d.read_u128()?, size: d.read_u8()? })
    }
}

impl ScalarInt {
    pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: 1 };

    pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: 1 };

    pub const ZST: ScalarInt = ScalarInt { data: 0_u128, size: 0 };

    #[inline]
    pub fn size(self) -> Size {
        Size::from_bytes(self.size)
    }

    /// Make sure the `data` fits in `size`.
    /// This is guaranteed by all constructors here, but since the enum variants are public,
    /// it could still be violated (even though no code outside this file should
    /// construct `Scalar`s).
    #[inline(always)]
    fn check_data(self) {
        // Using a block `{self.data}` here to force a copy instead of using `self.data`
        // directly, because `assert_eq` takes references to its arguments and formatting
        // arguments and would thus borrow `self.data`. Since `Self`
        // is a packed struct, that would create a possibly unaligned reference, which
        // is UB on a lot of platforms.
        debug_assert_eq!(
            truncate(self.data, self.size()),
            { self.data },
            "Scalar value {:#x} exceeds size of {} bytes",
            { self.data },
            self.size
        );
    }

    #[inline]
    pub fn zst() -> Self {
        Self::null(Size::ZERO)
    }

    #[inline]
    pub fn null(size: Size) -> Self {
        Self { data: 0, size: size.bytes() as u8 }
    }

    pub(crate) fn ptr_sized_op<'tcx>(
        self,
        dl: &TargetDataLayout,
        f_int: impl FnOnce(u64) -> InterpResult<'tcx, u64>,
    ) -> InterpResult<'tcx, Self> {
        assert_eq!(u64::from(self.size), dl.pointer_size.bytes());
        Ok(Self { data: u128::from(f_int(u64::try_from(self.data).unwrap())?), size: self.size })
    }

    #[inline]
    pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
        let data = i.into();
        if truncate(data, size) == data {
            Some(Self { data, size: size.bytes() as u8 })
        } else {
            None
        }
    }

    #[inline]
    pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
        let i = i.into();
        // `into` performed sign extension, we have to truncate
        let truncated = truncate(i as u128, size);
        if sign_extend(truncated, size) as i128 == i {
            Some(Self { data: truncated, size: size.bytes() as u8 })
        } else {
            None
        }
    }

    #[inline]
    pub fn assert_bits(self, target_size: Size) -> u128 {
        assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
        assert_eq!(target_size.bytes(), u64::from(self.size));
        self.check_data();
        self.data
    }

    #[inline]
    pub fn to_bits(self, target_size: Size) -> InterpResult<'static, u128> {
        assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
        if target_size.bytes() != u64::from(self.size) {
            throw_ub!(ScalarSizeMismatch {
                target_size: target_size.bytes(),
                data_size: u64::from(self.size),
            });
        }
        self.check_data();
        Ok(self.data)
    }
}

macro_rules! from {
    ($($ty:ty),*) => {
        $(
            impl From<$ty> for ScalarInt {
                #[inline]
                fn from(u: $ty) -> Self {
                    Self {
                        data: u128::from(u),
                        size: std::mem::size_of::<$ty>() as u8,
                    }
                }
            }
        )*
    }
}

macro_rules! try_from {
    ($($ty:ty),*) => {
        $(
            impl TryFrom<ScalarInt> for $ty {
                type Error = InterpErrorInfo<'static>;
                #[inline]
                fn try_from(int: ScalarInt) -> InterpResult<'static, Self> {
                    int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>())).map(|u| u.try_into().unwrap())
                }
            }
        )*
    }
}

from!(u8, u16, u32, u64, u128, bool);
try_from!(u8, u16, u32, u64, u128);

impl From<char> for ScalarInt {
    #[inline]
    fn from(c: char) -> Self {
        Self { data: c as u128, size: std::mem::size_of::<char>() as u8 }
    }
}

impl TryFrom<ScalarInt> for char {
    type Error = InterpErrorInfo<'static>;
    #[inline]
    fn try_from(int: ScalarInt) -> InterpResult<'static, Self> {
        int.to_bits(Size::from_bytes(std::mem::size_of::<char>()))
            .map(|u| char::from_u32(u.try_into().unwrap()).unwrap())
    }
}

impl From<Single> for ScalarInt {
    #[inline]
    fn from(f: Single) -> Self {
        // We trust apfloat to give us properly truncated data.
        Self { data: f.to_bits(), size: 4 }
    }
}

impl TryFrom<ScalarInt> for Single {
    type Error = InterpErrorInfo<'static>;
    #[inline]
    fn try_from(int: ScalarInt) -> InterpResult<'static, Self> {
        int.to_bits(Size::from_bytes(4)).map(Self::from_bits)
    }
}

impl From<Double> for ScalarInt {
    #[inline]
    fn from(f: Double) -> Self {
        // We trust apfloat to give us properly truncated data.
        Self { data: f.to_bits(), size: 8 }
    }
}

impl TryFrom<ScalarInt> for Double {
    type Error = InterpErrorInfo<'static>;
    #[inline]
    fn try_from(int: ScalarInt) -> InterpResult<'static, Self> {
        int.to_bits(Size::from_bytes(8)).map(Self::from_bits)
    }
}

impl fmt::Debug for ScalarInt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.size == 0 {
            self.check_data();
            write!(f, "<ZST>")
        } else {
            write!(f, "0x{:x}", self)
        }
    }
}

impl fmt::LowerHex for ScalarInt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.check_data();
        // Format as hex number wide enough to fit any value of the given `size`.
        // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
        // Using a block `{self.data}` here to force a copy instead of using `self.data`
        // directly, because `write!` takes references to its formatting arguments and
        // would thus borrow `self.data`. Since `Self`
        // is a packed struct, that would create a possibly unaligned reference, which
        // is UB on a lot of platforms.
        write!(f, "{:01$x}", { self.data }, self.size as usize * 2)
    }
}

impl fmt::UpperHex for ScalarInt {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.check_data();
        // Format as hex number wide enough to fit any value of the given `size`.
        // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
        // Using a block `{self.data}` here to force a copy instead of using `self.data`
        // directly, because `write!` takes references to its formatting arguments and
        // would thus borrow `self.data`. Since `Self`
        // is a packed struct, that would create a possibly unaligned reference, which
        // is UB on a lot of platforms.
        write!(f, "{:01$X}", { self.data }, self.size as usize * 2)
    }
}
<|endoftext|>


<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Matcher
//!
//! The Matcher trait is implemented by objects that can match patterns against the left-hand side of a stream. It's a fairly
//! baseline implementation: it's up to the caller to implement things like rewinding in order to perform tokenisation. That is,
//! Matchers are greedy and may (indeed, are likely to) consume more characters than the longest match while trying to find
//! a longer one.
//!

///
/// Matcher that can read an input stream of type `Symbol` and find the longest matching pattern, which it will identify using
/// `OutputSymbol`
///
pub trait PatternMatcher<'a, InputSymbol, OutputSymbol> {
    type State: MatchingState<'a, InputSymbol, OutputSymbol>;

    ///
    /// Creates a state that begins matching this pattern
    ///
    fn start(&'a self) -> MatchAction<'a, OutputSymbol, Self::State>;
}

///
/// Action to be taken after a matcher receives a symbol
///
pub enum MatchAction<'a, OutputSymbol: 'static, State: ?Sized> {
    // State is also always: MatchingState<InputSymbol, OutputSymbol> (important to know that as its how More is used)
    //
    // However, rust complains that InputSymbol is unused if we declare it in MatchAction and that it is undeclared if we don't
    // If this wasn't an enum, could make it an associated type? But this is an enum so we can't do that.
    // Ie, MatchAction<InputSymbol, OutputSymbol, State: MatchingState<InputSymbol, OutputSymbol>> = Nope (InputSymbol is unused apparently even though it's required which seems to me to mean it's used)
    // and also MatchAction<OutputSymbol, State: MatchingState<DontCare, OutputSymbol> = Nope (DontCare is not declared)

    /// The pattern does not match
    Reject,

    /// The pattern matched a certain number of symbols (which may be fewer than were passed to the matcher)
    Accept(usize, &'a OutputSymbol),

    /// The matcher needs more symbols to decide if the pattern matches and has entered a new state
    More(State)
}

///
/// Represents a state during a pattern matching operation
///
pub trait MatchingState<'a, InputSymbol, OutputSymbol> {
    ///
    /// Matches the next symbol
    ///
    fn next(self, symbol: InputSymbol) -> MatchAction<'a, OutputSymbol, Self>;

    ///
    /// There are no more symbols available (this can only return `Reject` or `Accept`)
    ///
    fn finish(self) -> MatchAction<'a, OutputSymbol, Self>;
}

pub use MatchAction::*;
<commit_msg>The pattern matcher trait isn’t used any more, so remove it<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Pattern matcher
//!
//! The Matcher trait is implemented by objects that can match patterns against the left-hand side of a stream. It's a fairly
//! baseline implementation: it's up to the caller to implement things like rewinding in order to perform tokenisation. That is,
//! Matchers are greedy and may (indeed, are likely to) consume more characters than the longest match while trying to find
//! a longer one.
//!

/*
 * Trait not implemented due to Rust type system limitations
 *
 * We want to write type State<'a> and have that in MatchAction, otherwise states need to live too long to be useful.
 *

///
/// Matcher that can read an input stream of type `Symbol` and find the longest matching pattern, which it will identify using
/// `OutputSymbol`
///
pub trait PatternMatcher<InputSymbol, OutputSymbol> {
    type State;

    ///
    /// Creates a state that begins matching this pattern
    ///
    fn start<'a>(&'a self) -> MatchAction<'a, OutputSymbol, Self::State>;
}
*/

///
/// Action to be taken after a matcher receives a symbol
///
pub enum MatchAction<'a, OutputSymbol: 'static, State: ?Sized> {
    // State is also always: MatchingState<InputSymbol, OutputSymbol> (important to know that as its how More is used)
    //
    // However, rust complains that InputSymbol is unused if we declare it in MatchAction and that it is undeclared if we don't
    // If this wasn't an enum, could make it an associated type? But this is an enum so we can't do that.
    // Ie, MatchAction<InputSymbol, OutputSymbol, State: MatchingState<InputSymbol, OutputSymbol>> = Nope (InputSymbol is unused apparently even though it's required which seems to me to mean it's used)
    // and also MatchAction<OutputSymbol, State: MatchingState<DontCare, OutputSymbol> = Nope (DontCare is not declared)

    /// The pattern does not match
    Reject,

    /// The pattern matched a certain number of symbols (which may be fewer than were passed to the matcher)
    Accept(usize, &'a OutputSymbol),

    /// The matcher needs more symbols to decide if the pattern matches and has entered a new state
    More(State)
}

///
/// Represents a state during a pattern matching operation
///
pub trait MatchingState<'a, InputSymbol, OutputSymbol> {
    ///
    /// Matches the next symbol
    ///
    fn next(self, symbol: InputSymbol) -> MatchAction<'a, OutputSymbol, Self>;

    ///
    /// There are no more symbols available (this can only return `Reject` or `Accept`)
    ///
    fn finish(self) -> MatchAction<'a, OutputSymbol, Self>;
}

pub use MatchAction::*;
<|endoftext|>


<commit_before><commit_msg>Fix broken emscripten JS Bindings<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

mod fetchable_query_generator;
mod node_query_generator;
mod query_query_generator;
mod refetchable_directive;
mod utils;
mod viewer_query_generator;

use crate::{
    connections::{extract_connection_metadata_from_directive, ConnectionConstants},
    relay_directive::{PLURAL_ARG_NAME, RELAY_DIRECTIVE_NAME},
    root_variables::{InferVariablesVisitor, VariableMap},
};

use common::{Diagnostic, DiagnosticsResult, NamedItem, WithLocation};
use errors::validate_map;
use fetchable_query_generator::FETCHABLE_QUERY_GENERATOR;
use graphql_ir::{
    Directive, FragmentDefinition, OperationDefinition, Program, Selection, ValidationMessage,
    VariableDefinition,
};
use graphql_syntax::OperationKind;
use intern::string_key::{StringKey, StringKeyMap, StringKeySet};
use node_query_generator::NODE_QUERY_GENERATOR;
use query_query_generator::QUERY_QUERY_GENERATOR;
use schema::{SDLSchema, Schema};
use std::{fmt::Write, sync::Arc};
use utils::*;
pub use utils::{RefetchableDerivedFromMetadata, RefetchableMetadata, CONSTANTS};
use viewer_query_generator::VIEWER_QUERY_GENERATOR;

use self::refetchable_directive::RefetchableDirective;
pub use self::refetchable_directive::REFETCHABLE_NAME;

/// This transform synthesizes "refetch" queries for fragments that
/// are trivially refetchable. This is comprised of three main stages:
///
/// 1. Validating that fragments marked with @refetchable qualify for
///    refetch query generation; mainly this means that the fragment
///    type is able to be refetched in some canonical way.
/// 2. Determining the variable definitions to use for each generated
///    query. GraphQL does not have a notion of fragment-local variables
///    at all, and although Relay adds this concept developers are still
///    allowed to reference global variables. This necessitates a
///    visiting all reachable fragments for each @refetchable fragment,
///    and finding the union of all global variables expceted to be defined.
/// 3. Building the refetch queries, a straightforward copying transform from
///    Fragment to Root IR nodes.
pub fn transform_refetchable_fragment(
    program: &Program,
    base_fragment_names: &'_ StringKeySet,
    for_typegen: bool,
) -> DiagnosticsResult<Program> {
    let mut next_program = Program::new(Arc::clone(&program.schema));
    let query_type = program.schema.query_type().unwrap();

    let mut transformer = RefetchableFragment::new(program, for_typegen);

    for operation in program.operations() {
        next_program.insert_operation(Arc::clone(operation));
    }

    validate_map(program.fragments(), |fragment| {
        let operation_result = transformer.transform_refetch_fragment(fragment)?;
        if let Some((refetchable_directive, operation_result)) = operation_result {
            next_program.insert_fragment(operation_result.fragment);
            if !base_fragment_names.contains(&fragment.name.item) {
                let mut directives = refetchable_directive.directives;
                directives.push(RefetchableDerivedFromMetadata(fragment.name.item).into());

                next_program.insert_operation(Arc::new(OperationDefinition {
                    kind: OperationKind::Query,
                    name: WithLocation::new(
                        fragment.name.location,
                        refetchable_directive.query_name.item,
                    ),
                    type_: query_type,
                    variable_definitions: operation_result.variable_definitions,
                    directives,
                    selections: operation_result.selections,
                }));
            }
        } else {
            next_program.insert_fragment(Arc::clone(fragment));
        }
        Ok(())
    })?;

    Ok(next_program)
}

type ExistingRefetchOperations = StringKeyMap<WithLocation<StringKey>>;

pub struct RefetchableFragment<'program> {
    connection_constants: ConnectionConstants,
    existing_refetch_operations: ExistingRefetchOperations,
    for_typegen: bool,
    program: &'program Program,
}

impl<'program> RefetchableFragment<'program> {
    pub fn new(program: &'program Program, for_typegen: bool) -> Self {
        RefetchableFragment {
            connection_constants: Default::default(),
            existing_refetch_operations: Default::default(),
            for_typegen,
            program,
        }
    }

    fn transform_refetch_fragment(
        &mut self,
        fragment: &Arc<FragmentDefinition>,
    ) -> DiagnosticsResult<Option<(RefetchableDirective, RefetchRoot)>> {
        fragment
            .directives
            .named(*REFETCHABLE_NAME)
            .map(|refetchable_directive| {
                self.transform_refetch_fragment_with_refetchable_directive(
                    fragment,
                    refetchable_directive,
                )
            })
            .transpose()
    }

    pub fn transform_refetch_fragment_with_refetchable_directive(
        &mut self,
        fragment: &Arc<FragmentDefinition>,
        directive: &Directive,
    ) -> DiagnosticsResult<(RefetchableDirective, RefetchRoot)> {
        let refetchable_directive =
            RefetchableDirective::from_directive(&self.program.schema, directive)?;
        self.validate_sibling_directives(fragment)?;
        self.validate_refetch_name(fragment, &refetchable_directive)?;
        let variables_map =
            InferVariablesVisitor::new(self.program).infer_fragment_variables(fragment);

        for generator in GENERATORS.iter() {
            if let Some(refetch_root) = (generator.build_refetch_operation)(
                &self.program.schema,
                fragment,
                refetchable_directive.query_name.item,
                &variables_map,
            )? {
                if !self.for_typegen {
                    self.validate_connection_metadata(refetch_root.fragment.as_ref())?;
                }
                return Ok((refetchable_directive, refetch_root));
            }
        }
        let mut descriptions = String::new();
        for generator in GENERATORS.iter() {
            writeln!(descriptions, " - {}", generator.description).unwrap();
        }
        descriptions.pop();
        Err(vec![Diagnostic::error(
            ValidationMessage::UnsupportedRefetchableFragment {
                fragment_name: fragment.name.item,
                descriptions,
            },
            fragment.name.location,
        )])
    }

    fn validate_sibling_directives(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> DiagnosticsResult<()> {
        let relay_directive = fragment.directives.named(*RELAY_DIRECTIVE_NAME);
        let plural_directive = relay_directive
            .filter(|directive| directive.arguments.named(*PLURAL_ARG_NAME).is_some());
        if let Some(directive) = plural_directive {
            Err(vec![Diagnostic::error(
                ValidationMessage::InvalidRefetchableFragmentWithRelayPlural {
                    fragment_name: fragment.name.item,
                },
                directive.name.location,
            )])
        } else {
            Ok(())
        }
    }

    fn validate_refetch_name(
        &mut self,
        fragment: &FragmentDefinition,
        refetchable_directive: &RefetchableDirective,
    ) -> DiagnosticsResult<()> {
        // check for conflict with other @refetchable names
        if let Some(previous_fragment) = self
            .existing_refetch_operations
            .insert(refetchable_directive.query_name.item, fragment.name)
        {
            let (first_fragment, second_fragment) = if fragment.name.item > previous_fragment.item {
                (previous_fragment, fragment.name)
            } else {
                (fragment.name, previous_fragment)
            };
            return Err(vec![
                Diagnostic::error(
                    ValidationMessage::DuplicateRefetchableOperation {
                        query_name: refetchable_directive.query_name.item,
                        first_fragment_name: first_fragment.item,
                        second_fragment_name: second_fragment.item,
                    },
                    first_fragment.location,
                )
                .annotate("also defined here", second_fragment.location),
            ]);
        }

        // check for conflict with operations
        if let Some(existing_query) = self
            .program
            .operation(refetchable_directive.query_name.item)
        {
            return Err(vec![
                Diagnostic::error(
                    ValidationMessage::RefetchableQueryConflictWithQuery {
                        query_name: refetchable_directive.query_name.item,
                    },
                    refetchable_directive.query_name.location,
                )
                .annotate(
                    "an operation with that name is already defined here",
                    existing_query.name.location,
                ),
            ]);
        }

        Ok(())
    }

    /// Validate that any @connection usage is valid for refetching:
    /// - Variables are used for both the "count" and "cursor" arguments
    ///   (after/first or before/last)
    /// - Exactly one connection
    /// - Has a stable path to the connection data
    ///
    /// Connection metadata is extracted in `transform_connection`
    fn validate_connection_metadata(&self, fragment: &FragmentDefinition) -> DiagnosticsResult<()> {
        if let Some(metadatas) = extract_connection_metadata_from_directive(&fragment.directives) {
            // TODO: path or connection field locations in the error messages
            if metadatas.len() > 1 {
                return Err(vec![Diagnostic::error(
                    ValidationMessage::RefetchableWithMultipleConnections {
                        fragment_name: fragment.name.item,
                    },
                    fragment.name.location,
                )]);
            } else if metadatas.len() == 1 {
                let metadata = &metadatas[0];
                if metadata.path.is_none() {
                    return Err(vec![Diagnostic::error(
                        ValidationMessage::RefetchableWithConnectionInPlural {
                            fragment_name: fragment.name.item,
                        },
                        fragment.name.location,
                    )]);
                }
                if (metadata.after.is_none() || metadata.first.is_none())
                    && metadata.direction != self.connection_constants.direction_backward
                {
                    return Err(vec![Diagnostic::error(
                        ValidationMessage::RefetchableWithConstConnectionArguments {
                            fragment_name: fragment.name.item,
                            arguments: "after and first",
                        },
                        fragment.name.location,
                    )]);
                } else if (metadata.before.is_none() || metadata.last.is_none())
                    && metadata.direction != self.connection_constants.direction_forward
                {
                    return Err(vec![Diagnostic::error(
                        ValidationMessage::RefetchableWithConstConnectionArguments {
                            fragment_name: fragment.name.item,
                            arguments: "before and last",
                        },
                        fragment.name.location,
                    )]);
                }
            }
        }
        Ok(())
    }
}

type BuildRefetchOperationFn = fn(
    schema: &SDLSchema,
    fragment: &Arc<FragmentDefinition>,
    query_name: StringKey,
    variables_map: &VariableMap,
) -> DiagnosticsResult<Option<RefetchRoot>>;
/// A strategy to generate queries for a given fragment. Multiple stategies
/// can be tried, such as generating a `node(id: ID)` query or a query directly
/// on the root query type.
pub struct QueryGenerator {
    /// Used to describe what fragments this QueryGenerator applies to, used in
    /// error messages.
    pub description: &'static str,

    /// Returns RefetchRoot or null if not applicable. Might throw a user error
    /// for an invalid schema or other problems.
    pub build_refetch_operation: BuildRefetchOperationFn,
}

const GENERATORS: [QueryGenerator; 4] = [
    VIEWER_QUERY_GENERATOR,
    QUERY_QUERY_GENERATOR,
    NODE_QUERY_GENERATOR,
    FETCHABLE_QUERY_GENERATOR,
];

pub struct RefetchRoot {
    pub fragment: Arc<FragmentDefinition>,
    pub selections: Vec<Selection>,
    pub variable_definitions: Vec<VariableDefinition>,
}
<commit_msg>do not panic if query_type is not defined<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

mod fetchable_query_generator;
mod node_query_generator;
mod query_query_generator;
mod refetchable_directive;
mod utils;
mod viewer_query_generator;

use crate::{
    connections::{extract_connection_metadata_from_directive, ConnectionConstants},
    relay_directive::{PLURAL_ARG_NAME, RELAY_DIRECTIVE_NAME},
    root_variables::{InferVariablesVisitor, VariableMap},
};

use common::{Diagnostic, DiagnosticsResult, NamedItem, WithLocation};
use errors::validate_map;
use fetchable_query_generator::FETCHABLE_QUERY_GENERATOR;
use graphql_ir::{
    Directive, FragmentDefinition, OperationDefinition, Program, Selection, ValidationMessage,
    VariableDefinition,
};
use graphql_syntax::OperationKind;
use intern::string_key::{StringKey, StringKeyMap, StringKeySet};
use node_query_generator::NODE_QUERY_GENERATOR;
use query_query_generator::QUERY_QUERY_GENERATOR;
use schema::{SDLSchema, Schema};
use std::{fmt::Write, sync::Arc};
use utils::*;
pub use utils::{RefetchableDerivedFromMetadata, RefetchableMetadata, CONSTANTS};
use viewer_query_generator::VIEWER_QUERY_GENERATOR;

use self::refetchable_directive::RefetchableDirective;
pub use self::refetchable_directive::REFETCHABLE_NAME;

/// This transform synthesizes "refetch" queries for fragments that
/// are trivially refetchable. This is comprised of three main stages:
///
/// 1. Validating that fragments marked with @refetchable qualify for
///    refetch query generation; mainly this means that the fragment
///    type is able to be refetched in some canonical way.
/// 2. Determining the variable definitions to use for each generated
///    query. GraphQL does not have a notion of fragment-local variables
///    at all, and although Relay adds this concept developers are still
///    allowed to reference global variables. This necessitates a
///    visiting all reachable fragments for each @refetchable fragment,
///    and finding the union of all global variables expected to be defined.
/// 3. Building the refetch queries, a straightforward copying transform from
///    Fragment to Root IR nodes.
pub fn transform_refetchable_fragment(
    program: &Program,
    base_fragment_names: &'_ StringKeySet,
    for_typegen: bool,
) -> DiagnosticsResult<Program> {
    let mut next_program = Program::new(Arc::clone(&program.schema));

    let mut transformer = RefetchableFragment::new(program, for_typegen);

    for operation in program.operations() {
        next_program.insert_operation(Arc::clone(operation));
    }

    validate_map(program.fragments(), |fragment| {
        let operation_result = transformer.transform_refetch_fragment(fragment)?;
        if let Some((refetchable_directive, operation_result)) = operation_result {
            next_program.insert_fragment(operation_result.fragment);
            if !base_fragment_names.contains(&fragment.name.item) {
                let mut directives = refetchable_directive.directives;
                directives.push(RefetchableDerivedFromMetadata(fragment.name.item).into());

                next_program.insert_operation(Arc::new(OperationDefinition {
                    kind: OperationKind::Query,
                    name: WithLocation::new(
                        fragment.name.location,
                        refetchable_directive.query_name.item,
                    ),
                    type_: program.schema.query_type().unwrap(),
                    variable_definitions: operation_result.variable_definitions,
                    directives,
                    selections: operation_result.selections,
                }));
            }
        } else {
            next_program.insert_fragment(Arc::clone(fragment));
        }
        Ok(())
    })?;

    Ok(next_program)
}

type ExistingRefetchOperations = StringKeyMap<WithLocation<StringKey>>;

pub struct RefetchableFragment<'program> {
    connection_constants: ConnectionConstants,
    existing_refetch_operations: ExistingRefetchOperations,
    for_typegen: bool,
    program: &'program Program,
}

impl<'program> RefetchableFragment<'program> {
    pub fn new(program: &'program Program, for_typegen: bool) -> Self {
        RefetchableFragment {
            connection_constants: Default::default(),
            existing_refetch_operations: Default::default(),
            for_typegen,
            program,
        }
    }

    fn transform_refetch_fragment(
        &mut self,
        fragment: &Arc<FragmentDefinition>,
    ) -> DiagnosticsResult<Option<(RefetchableDirective, RefetchRoot)>> {
        let refetchable_directive = fragment.directives.named(*REFETCHABLE_NAME);
        if refetchable_directive.is_some() && self.program.schema.query_type().is_none() {
            return Err(vec![Diagnostic::error(
                "Unable to use @refetchable directive. The `Query` type is not defined on the schema.",
                refetchable_directive.unwrap().name.location,
            )]);
        }

        refetchable_directive
            .map(|refetchable_directive| {
                self.transform_refetch_fragment_with_refetchable_directive(
                    fragment,
                    refetchable_directive,
                )
            })
            .transpose()
    }

    pub fn transform_refetch_fragment_with_refetchable_directive(
        &mut self,
        fragment: &Arc<FragmentDefinition>,
        directive: &Directive,
    ) -> DiagnosticsResult<(RefetchableDirective, RefetchRoot)> {
        let refetchable_directive =
            RefetchableDirective::from_directive(&self.program.schema, directive)?;
        self.validate_sibling_directives(fragment)?;
        self.validate_refetch_name(fragment, &refetchable_directive)?;
        let variables_map =
            InferVariablesVisitor::new(self.program).infer_fragment_variables(fragment);

        for generator in GENERATORS.iter() {
            if let Some(refetch_root) = (generator.build_refetch_operation)(
                &self.program.schema,
                fragment,
                refetchable_directive.query_name.item,
                &variables_map,
            )? {
                if !self.for_typegen {
                    self.validate_connection_metadata(refetch_root.fragment.as_ref())?;
                }
                return Ok((refetchable_directive, refetch_root));
            }
        }
        let mut descriptions = String::new();
        for generator in GENERATORS.iter() {
            writeln!(descriptions, " - {}", generator.description).unwrap();
        }
        descriptions.pop();
        Err(vec![Diagnostic::error(
            ValidationMessage::UnsupportedRefetchableFragment {
                fragment_name: fragment.name.item,
                descriptions,
            },
            fragment.name.location,
        )])
    }

    fn validate_sibling_directives(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> DiagnosticsResult<()> {
        let relay_directive = fragment.directives.named(*RELAY_DIRECTIVE_NAME);
        let plural_directive = relay_directive
            .filter(|directive| directive.arguments.named(*PLURAL_ARG_NAME).is_some());
        if let Some(directive) = plural_directive {
            Err(vec![Diagnostic::error(
                ValidationMessage::InvalidRefetchableFragmentWithRelayPlural {
                    fragment_name: fragment.name.item,
                },
                directive.name.location,
            )])
        } else {
            Ok(())
        }
    }

    fn validate_refetch_name(
        &mut self,
        fragment: &FragmentDefinition,
        refetchable_directive: &RefetchableDirective,
    ) -> DiagnosticsResult<()> {
        // check for conflict with other @refetchable names
        if let Some(previous_fragment) = self
            .existing_refetch_operations
            .insert(refetchable_directive.query_name.item, fragment.name)
        {
            let (first_fragment, second_fragment) = if fragment.name.item > previous_fragment.item {
                (previous_fragment, fragment.name)
            } else {
                (fragment.name, previous_fragment)
            };
            return Err(vec![
                Diagnostic::error(
                    ValidationMessage::DuplicateRefetchableOperation {
                        query_name: refetchable_directive.query_name.item,
                        first_fragment_name: first_fragment.item,
                        second_fragment_name: second_fragment.item,
                    },
                    first_fragment.location,
                )
                .annotate("also defined here", second_fragment.location),
            ]);
        }

        // check for conflict with operations
        if let Some(existing_query) = self
            .program
            .operation(refetchable_directive.query_name.item)
        {
            return Err(vec![
                Diagnostic::error(
                    ValidationMessage::RefetchableQueryConflictWithQuery {
                        query_name: refetchable_directive.query_name.item,
                    },
                    refetchable_directive.query_name.location,
                )
                .annotate(
                    "an operation with that name is already defined here",
                    existing_query.name.location,
                ),
            ]);
        }

        Ok(())
    }

    /// Validate that any @connection usage is valid for refetching:
    /// - Variables are used for both the "count" and "cursor" arguments
    ///   (after/first or before/last)
    /// - Exactly one connection
    /// - Has a stable path to the connection data
    ///
    /// Connection metadata is extracted in `transform_connection`
    fn validate_connection_metadata(&self, fragment: &FragmentDefinition) -> DiagnosticsResult<()> {
        if let Some(metadatas) = extract_connection_metadata_from_directive(&fragment.directives) {
            // TODO: path or connection field locations in the error messages
            if metadatas.len() > 1 {
                return Err(vec![Diagnostic::error(
                    ValidationMessage::RefetchableWithMultipleConnections {
                        fragment_name: fragment.name.item,
                    },
                    fragment.name.location,
                )]);
            } else if metadatas.len() == 1 {
                let metadata = &metadatas[0];
                if metadata.path.is_none() {
                    return Err(vec![Diagnostic::error(
                        ValidationMessage::RefetchableWithConnectionInPlural {
                            fragment_name: fragment.name.item,
                        },
                        fragment.name.location,
                    )]);
                }
                if (metadata.after.is_none() || metadata.first.is_none())
                    && metadata.direction != self.connection_constants.direction_backward
                {
                    return Err(vec![Diagnostic::error(
                        ValidationMessage::RefetchableWithConstConnectionArguments {
                            fragment_name: fragment.name.item,
                            arguments: "after and first",
                        },
                        fragment.name.location,
                    )]);
                } else if (metadata.before.is_none() || metadata.last.is_none())
                    && metadata.direction != self.connection_constants.direction_forward
                {
                    return Err(vec![Diagnostic::error(
                        ValidationMessage::RefetchableWithConstConnectionArguments {
                            fragment_name: fragment.name.item,
                            arguments: "before and last",
                        },
                        fragment.name.location,
                    )]);
                }
            }
        }
        Ok(())
    }
}

type BuildRefetchOperationFn = fn(
    schema: &SDLSchema,
    fragment: &Arc<FragmentDefinition>,
    query_name: StringKey,
    variables_map: &VariableMap,
) -> DiagnosticsResult<Option<RefetchRoot>>;
/// A strategy to generate queries for a given fragment. Multiple stategies
/// can be tried, such as generating a `node(id: ID)` query or a query directly
/// on the root query type.
pub struct QueryGenerator {
    /// Used to describe what fragments this QueryGenerator applies to, used in
    /// error messages.
    pub description: &'static str,

    /// Returns RefetchRoot or null if not applicable. Might throw a user error
    /// for an invalid schema or other problems.
    pub build_refetch_operation: BuildRefetchOperationFn,
}

const GENERATORS: [QueryGenerator; 4] = [
    VIEWER_QUERY_GENERATOR,
    QUERY_QUERY_GENERATOR,
    NODE_QUERY_GENERATOR,
    FETCHABLE_QUERY_GENERATOR,
];

pub struct RefetchRoot {
    pub fragment: Arc<FragmentDefinition>,
    pub selections: Vec<Selection>,
    pub variable_definitions: Vec<VariableDefinition>,
}
<|endoftext|>