<commit_before>//! Lints concerned with the grouping of digits with underscores in integral or
//! floating-point literal expressions.

use rustc::lint::*;
use syntax::ast::*;
use syntax_pos;
use utils::{span_help_and_lint, snippet_opt, in_external_macro};

/// **What it does:** Warns if a long integral or floating-point constant does
/// not contain underscores.
///
/// **Why is this bad?** Reading long numbers is difficult without separators.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 61864918973511
/// ```
declare_lint! {
    pub UNREADABLE_LITERAL,
    Warn,
    "long integer literal without underscores"
}

/// **What it does:** Warns if an integral or floating-point constant is
/// grouped inconsistently with underscores.
///
/// **Why is this bad?** Readers may incorrectly interpret inconsistently
/// grouped digits.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 618_64_9189_73_511
/// ```
declare_lint! {
    pub INCONSISTENT_DIGIT_GROUPING,
    Warn,
    "integer literals with digits grouped inconsistently"
}

/// **What it does:** Warns if the digits of an integral or floating-point
/// constant are grouped into groups that
/// are too large.
///
/// **Why is this bad?** Negatively impacts readability.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 6186491_8973511
/// ```
declare_lint! {
    pub LARGE_DIGIT_GROUPS,
    Warn,
    "grouping digits into groups that are too large"
}

#[derive(Debug)]
enum Radix {
    Binary,
    Octal,
    Decimal,
    Hexadecimal,
}

impl Radix {
    /// Return a reasonable digit group size for this radix.
    pub fn suggest_grouping(&self) -> usize {
        match *self {
            Radix::Binary | Radix::Hexadecimal => 4,
            Radix::Octal | Radix::Decimal => 3,
        }
    }
}

#[derive(Debug)]
struct DigitInfo<'a> {
    /// Characters of a literal between the radix prefix and type suffix.
    pub digits: &'a str,
    /// Which radix the literal was represented in.
    pub radix: Radix,
    /// The radix prefix, if present.
    pub prefix: Option<&'a str>,
    /// The type suffix, including preceding underscore if present.
    pub suffix: Option<&'a str>,
    /// True for floating-point literals.
    pub float: bool,
}

impl<'a> DigitInfo<'a> {
    pub fn new(lit: &str, float: bool) -> DigitInfo {
        // Determine delimiter for radix prefix, if present, and radix.
        let radix = if lit.starts_with("0x") {
            Radix::Hexadecimal
        } else if lit.starts_with("0b") {
            Radix::Binary
        } else if lit.starts_with("0o") {
            Radix::Octal
        } else {
            Radix::Decimal
        };

        // Grab part of the literal after prefix, if present.
        let (prefix, sans_prefix) = if let Radix::Decimal = radix {
            (None, lit)
        } else {
            let (p, s) = lit.split_at(2);
            (Some(p), s)
        };

        let mut last_d = '\0';
        for (d_idx, d) in sans_prefix.char_indices() {
            if !float && (d == 'i' || d == 'u') || float && d == 'f' {
                let suffix_start = if last_d == '_' { d_idx - 1 } else { d_idx };
                let (digits, suffix) = sans_prefix.split_at(suffix_start);
                return DigitInfo {
                    digits: digits,
                    radix: radix,
                    prefix: prefix,
                    suffix: Some(suffix),
                    float: float,
                };
            }
            last_d = d
        }

        // No suffix found
        DigitInfo {
            digits: sans_prefix,
            radix: radix,
            prefix: prefix,
            suffix: None,
            float: float,
        }
    }

    /// Returns digits grouped in a sensible way.
    fn grouping_hint(&self) -> String {
        let group_size = self.radix.suggest_grouping();
        if self.digits.contains('.') {
            let mut parts = self.digits.split(".");
            let int_part_hint = parts
                .next()
                .unwrap()
                .chars()
                .rev()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().rev().collect())
                .rev()
                .collect::<Vec<String>>()
                .join("_");
            let frac_part_hint = parts
                .next()
                .unwrap()
                .chars()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().collect())
                .collect::<Vec<String>>()
                .join("_");
            format!("{}.{}{}", int_part_hint, frac_part_hint, self.suffix.unwrap_or(""))
        } else {
            let hint = self.digits
                .chars()
                .rev()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().rev().collect())
                .rev()
                .collect::<Vec<String>>()
                .join("_");
            format!("{}{}{}", self.prefix.unwrap_or(""), hint, self.suffix.unwrap_or(""))
        }
    }
}

enum WarningType {
    UnreadableLiteral,
    InconsistentDigitGrouping,
    LargeDigitGroups,
}

impl WarningType {
    pub fn display(&self, grouping_hint: &str, cx: &EarlyContext, span: &syntax_pos::Span) {
        match *self {
            WarningType::UnreadableLiteral => {
                span_help_and_lint(
                    cx,
                    UNREADABLE_LITERAL,
                    *span,
                    "long literal lacking separators",
                    &format!("consider: {}", grouping_hint),
                )
            },
            WarningType::LargeDigitGroups => {
                span_help_and_lint(
                    cx,
                    LARGE_DIGIT_GROUPS,
                    *span,
                    "digit groups should be smaller",
                    &format!("consider: {}", grouping_hint),
                )
            },
            WarningType::InconsistentDigitGrouping => {
                span_help_and_lint(
                    cx,
                    INCONSISTENT_DIGIT_GROUPING,
                    *span,
                    "digits grouped inconsistently by underscores",
                    &format!("consider: {}", grouping_hint),
                )
            },
        };
    }
}

#[derive(Copy, Clone)]
pub struct LiteralDigitGrouping;

impl LintPass for LiteralDigitGrouping {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNREADABLE_LITERAL, INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS)
    }
}

impl EarlyLintPass for LiteralDigitGrouping {
    fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
        if in_external_macro(cx, expr.span) {
            return;
        }

        if let ExprKind::Lit(ref lit) = expr.node {
            self.check_lit(cx, lit)
        }
    }
}

impl LiteralDigitGrouping {
    fn check_lit(&self, cx: &EarlyContext, lit: &Lit) {
        // Lint integral literals.
        if_let_chain! {[
            let LitKind::Int(..) = lit.node,
            let Some(src) = snippet_opt(cx, lit.span),
            let Some(firstch) = src.chars().next(),
            char::to_digit(firstch, 10).is_some()
        ], {
            let digit_info = DigitInfo::new(&src, false);
            let _ = LiteralDigitGrouping::do_lint(digit_info.digits).map_err(|warning_type| {
                warning_type.display(&digit_info.grouping_hint(), cx, &lit.span)
            });
        }}

        // Lint floating-point literals.
        if_let_chain! {[
            let LitKind::Float(..) = lit.node,
            let Some(src) = snippet_opt(cx, lit.span),
            let Some(firstch) = src.chars().next(),
            char::to_digit(firstch, 10).is_some()
        ], {
            let digit_info = DigitInfo::new(&src, true);
            // Separate digits into integral and fractional parts.
            let parts: Vec<&str> = digit_info
                .digits
                .split_terminator('.')
                .collect();

            // Lint integral and fractional parts separately, and then check consistency of digit
            // groups if both pass.
            let _ = LiteralDigitGrouping::do_lint(parts[0])
                .map(|integral_group_size| {
                    if parts.len() > 1 {
                        // Lint the fractional part of literal just like integral part, but reversed.
                        let fractional_part = &parts[1].chars().rev().collect::<String>();
                        let _ = LiteralDigitGrouping::do_lint(fractional_part)
                            .map(|fractional_group_size| {
                                let consistent = LiteralDigitGrouping::parts_consistent(integral_group_size, fractional_group_size, parts[0].len(), parts[1].len());
                                if !consistent {
                                    WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(), cx, &lit.span);
                                }
                            })
                            .map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, &lit.span));
                    }
                })
                .map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, &lit.span));
        }}
    }

    /// Given the sizes of the digit groups of both integral and fractional
    /// parts, and the length
    /// of both parts, determine if the digits have been grouped consistently.
    fn parts_consistent(int_group_size: usize, frac_group_size: usize, int_size: usize, frac_size: usize) -> bool {
        match (int_group_size, frac_group_size) {
            // No groups on either side of decimal point - trivially consistent.
            (0, 0) => true,
            // Integral part has grouped digits, fractional part does not.
            (_, 0) => frac_size <= int_group_size,
            // Fractional part has grouped digits, integral part does not.
            (0, _) => int_size <= frac_group_size,
            // Both parts have grouped digits. Groups should be the same size.
            (_, _) => int_group_size == frac_group_size,
        }
    }

    /// Performs lint on `digits` (no decimal point) and returns the group
    /// size on success or `WarningType` when emitting a warning.
    fn do_lint(digits: &str) -> Result<usize, WarningType> {
        // Grab underscore indices with respect to the units digit.
        let underscore_positions: Vec<usize> = digits
            .chars()
            .rev()
            .enumerate()
            .filter_map(|(idx, digit)| if digit == '_' { Some(idx) } else { None })
            .collect();

        if underscore_positions.is_empty() {
            // Check if literal needs underscores.
            if digits.len() > 4 {
                Err(WarningType::UnreadableLiteral)
            } else {
                Ok(0)
            }
        } else {
            // Check consistency and the sizes of the groups.
            let group_size = underscore_positions[0];
            let consistent = underscore_positions
                .windows(2)
                .all(|ps| ps[1] - ps[0] == group_size + 1)
                // number of digits to the left of the last group cannot be bigger than group size.
                && (digits.len() - underscore_positions.last().unwrap() <= group_size + 1);

            if !consistent {
                return Err(WarningType::InconsistentDigitGrouping);
            } else if group_size > 4 {
                return Err(WarningType::LargeDigitGroups);
            }
            Ok(group_size)
        }
    }
}
<commit_msg>More autoclippyification<commit_after>//! Lints concerned with the grouping of digits with underscores in integral or
//! floating-point literal expressions.

use rustc::lint::*;
use syntax::ast::*;
use syntax_pos;
use utils::{span_help_and_lint, snippet_opt, in_external_macro};

/// **What it does:** Warns if a long integral or floating-point constant does
/// not contain underscores.
///
/// **Why is this bad?** Reading long numbers is difficult without separators.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 61864918973511
/// ```
declare_lint! {
    pub UNREADABLE_LITERAL,
    Warn,
    "long integer literal without underscores"
}

/// **What it does:** Warns if an integral or floating-point constant is
/// grouped inconsistently with underscores.
///
/// **Why is this bad?** Readers may incorrectly interpret inconsistently
/// grouped digits.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 618_64_9189_73_511
/// ```
declare_lint! {
    pub INCONSISTENT_DIGIT_GROUPING,
    Warn,
    "integer literals with digits grouped inconsistently"
}

/// **What it does:** Warns if the digits of an integral or floating-point
/// constant are grouped into groups that
/// are too large.
///
/// **Why is this bad?** Negatively impacts readability.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 6186491_8973511
/// ```
declare_lint! {
    pub LARGE_DIGIT_GROUPS,
    Warn,
    "grouping digits into groups that are too large"
}

#[derive(Debug)]
enum Radix {
    Binary,
    Octal,
    Decimal,
    Hexadecimal,
}

impl Radix {
    /// Return a reasonable digit group size for this radix.
    pub fn suggest_grouping(&self) -> usize {
        match *self {
            Radix::Binary | Radix::Hexadecimal => 4,
            Radix::Octal | Radix::Decimal => 3,
        }
    }
}

#[derive(Debug)]
struct DigitInfo<'a> {
    /// Characters of a literal between the radix prefix and type suffix.
    pub digits: &'a str,
    /// Which radix the literal was represented in.
    pub radix: Radix,
    /// The radix prefix, if present.
    pub prefix: Option<&'a str>,
    /// The type suffix, including preceding underscore if present.
    pub suffix: Option<&'a str>,
    /// True for floating-point literals.
    pub float: bool,
}

impl<'a> DigitInfo<'a> {
    pub fn new(lit: &str, float: bool) -> DigitInfo {
        // Determine delimiter for radix prefix, if present, and radix.
        let radix = if lit.starts_with("0x") {
            Radix::Hexadecimal
        } else if lit.starts_with("0b") {
            Radix::Binary
        } else if lit.starts_with("0o") {
            Radix::Octal
        } else {
            Radix::Decimal
        };

        // Grab part of the literal after prefix, if present.
        let (prefix, sans_prefix) = if let Radix::Decimal = radix {
            (None, lit)
        } else {
            let (p, s) = lit.split_at(2);
            (Some(p), s)
        };

        let mut last_d = '\0';
        for (d_idx, d) in sans_prefix.char_indices() {
            if !float && (d == 'i' || d == 'u') || float && d == 'f' {
                let suffix_start = if last_d == '_' { d_idx - 1 } else { d_idx };
                let (digits, suffix) = sans_prefix.split_at(suffix_start);
                return DigitInfo {
                    digits: digits,
                    radix: radix,
                    prefix: prefix,
                    suffix: Some(suffix),
                    float: float,
                };
            }
            last_d = d
        }

        // No suffix found
        DigitInfo {
            digits: sans_prefix,
            radix: radix,
            prefix: prefix,
            suffix: None,
            float: float,
        }
    }

    /// Returns digits grouped in a sensible way.
    fn grouping_hint(&self) -> String {
        let group_size = self.radix.suggest_grouping();
        if self.digits.contains('.') {
            let mut parts = self.digits.split('.');
            let int_part_hint = parts
                .next()
                .unwrap()
                .chars()
                .rev()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().rev().collect())
                .rev()
                .collect::<Vec<String>>()
                .join("_");
            let frac_part_hint = parts
                .next()
                .unwrap()
                .chars()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().collect())
                .collect::<Vec<String>>()
                .join("_");
            format!("{}.{}{}", int_part_hint, frac_part_hint, self.suffix.unwrap_or(""))
        } else {
            let hint = self.digits
                .chars()
                .rev()
                .filter(|&c| c != '_')
                .collect::<Vec<_>>()
                .chunks(group_size)
                .map(|chunk| chunk.into_iter().rev().collect())
                .rev()
                .collect::<Vec<String>>()
                .join("_");
            format!("{}{}{}", self.prefix.unwrap_or(""), hint, self.suffix.unwrap_or(""))
        }
    }
}

enum WarningType {
    UnreadableLiteral,
    InconsistentDigitGrouping,
    LargeDigitGroups,
}

impl WarningType {
    pub fn display(&self, grouping_hint: &str, cx: &EarlyContext, span: &syntax_pos::Span) {
        match *self {
            WarningType::UnreadableLiteral => {
                span_help_and_lint(
                    cx,
                    UNREADABLE_LITERAL,
                    *span,
                    "long literal lacking separators",
                    &format!("consider: {}", grouping_hint),
                )
            },
            WarningType::LargeDigitGroups => {
                span_help_and_lint(
                    cx,
                    LARGE_DIGIT_GROUPS,
                    *span,
                    "digit groups should be smaller",
                    &format!("consider: {}", grouping_hint),
                )
            },
            WarningType::InconsistentDigitGrouping => {
                span_help_and_lint(
                    cx,
                    INCONSISTENT_DIGIT_GROUPING,
                    *span,
                    "digits grouped inconsistently by underscores",
                    &format!("consider: {}", grouping_hint),
                )
            },
        };
    }
}

#[derive(Copy, Clone)]
pub struct LiteralDigitGrouping;

impl LintPass for LiteralDigitGrouping {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNREADABLE_LITERAL, INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS)
    }
}

impl EarlyLintPass for LiteralDigitGrouping {
    fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
        if in_external_macro(cx, expr.span) {
            return;
        }

        if let ExprKind::Lit(ref lit) = expr.node {
            self.check_lit(cx, lit)
        }
    }
}

impl LiteralDigitGrouping {
    fn check_lit(&self, cx: &EarlyContext, lit: &Lit) {
        // Lint integral literals.
        if_let_chain! {[
            let LitKind::Int(..) = lit.node,
            let Some(src) = snippet_opt(cx, lit.span),
            let Some(firstch) = src.chars().next(),
            char::to_digit(firstch, 10).is_some()
        ], {
            let digit_info = DigitInfo::new(&src, false);
            let _ = LiteralDigitGrouping::do_lint(digit_info.digits).map_err(|warning_type| {
                warning_type.display(&digit_info.grouping_hint(), cx, &lit.span)
            });
        }}

        // Lint floating-point literals.
        if_let_chain! {[
            let LitKind::Float(..) = lit.node,
            let Some(src) = snippet_opt(cx, lit.span),
            let Some(firstch) = src.chars().next(),
            char::to_digit(firstch, 10).is_some()
        ], {
            let digit_info = DigitInfo::new(&src, true);
            // Separate digits into integral and fractional parts.
            let parts: Vec<&str> = digit_info
                .digits
                .split_terminator('.')
                .collect();

            // Lint integral and fractional parts separately, and then check consistency of digit
            // groups if both pass.
            let _ = LiteralDigitGrouping::do_lint(parts[0])
                .map(|integral_group_size| {
                    if parts.len() > 1 {
                        // Lint the fractional part of literal just like integral part, but reversed.
                        let fractional_part = &parts[1].chars().rev().collect::<String>();
                        let _ = LiteralDigitGrouping::do_lint(fractional_part)
                            .map(|fractional_group_size| {
                                let consistent = LiteralDigitGrouping::parts_consistent(integral_group_size, fractional_group_size, parts[0].len(), parts[1].len());
                                if !consistent {
                                    WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(), cx, &lit.span);
                                }
                            })
                            .map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, &lit.span));
                    }
                })
                .map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, &lit.span));
        }}
    }

    /// Given the sizes of the digit groups of both integral and fractional
    /// parts, and the length
    /// of both parts, determine if the digits have been grouped consistently.
    fn parts_consistent(int_group_size: usize, frac_group_size: usize, int_size: usize, frac_size: usize) -> bool {
        match (int_group_size, frac_group_size) {
            // No groups on either side of decimal point - trivially consistent.
            (0, 0) => true,
            // Integral part has grouped digits, fractional part does not.
            (_, 0) => frac_size <= int_group_size,
            // Fractional part has grouped digits, integral part does not.
            (0, _) => int_size <= frac_group_size,
            // Both parts have grouped digits. Groups should be the same size.
            (_, _) => int_group_size == frac_group_size,
        }
    }

    /// Performs lint on `digits` (no decimal point) and returns the group
    /// size on success or `WarningType` when emitting a warning.
    fn do_lint(digits: &str) -> Result<usize, WarningType> {
        // Grab underscore indices with respect to the units digit.
        let underscore_positions: Vec<usize> = digits
            .chars()
            .rev()
            .enumerate()
            .filter_map(|(idx, digit)| if digit == '_' { Some(idx) } else { None })
            .collect();

        if underscore_positions.is_empty() {
            // Check if literal needs underscores.
            if digits.len() > 4 {
                Err(WarningType::UnreadableLiteral)
            } else {
                Ok(0)
            }
        } else {
            // Check consistency and the sizes of the groups.
            let group_size = underscore_positions[0];
            let consistent = underscore_positions
                .windows(2)
                .all(|ps| ps[1] - ps[0] == group_size + 1)
                // number of digits to the left of the last group cannot be bigger than group size.
                && (digits.len() - underscore_positions.last().unwrap() <= group_size + 1);

            if !consistent {
                return Err(WarningType::InconsistentDigitGrouping);
            } else if group_size > 4 {
                return Err(WarningType::LargeDigitGroups);
            }
            Ok(group_size)
        }
    }
}
<|endoftext|>


<commit_before>//! Liveness verifier.

use crate::flowgraph::{BasicBlock, ControlFlowGraph};
use crate::ir::entities::AnyEntity;
use crate::ir::{ExpandedProgramPoint, Function, Inst, ProgramOrder, ProgramPoint, Value};
use crate::isa::TargetIsa;
use crate::regalloc::liveness::Liveness;
use crate::regalloc::liverange::LiveRange;
use crate::timing;
use crate::verifier::{VerifierErrors, VerifierStepResult};
use core::cmp::Ordering;

/// Verify liveness information for `func`.
///
/// The provided control flow graph is assumed to be sound.
///
/// - All values in the program must have a live range.
/// - The live range def point must match where the value is defined.
/// - The live range must reach all uses.
/// - When a live range is live-in to an EBB, it must be live at all the predecessors.
/// - The live range affinity must be compatible with encoding constraints.
///
/// We don't verify that live ranges are minimal. This would require recomputing live ranges for
/// all values.
pub fn verify_liveness(
    isa: &dyn TargetIsa,
    func: &Function,
    cfg: &ControlFlowGraph,
    liveness: &Liveness,
    errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
    let _tt = timing::verify_liveness();
    let verifier = LivenessVerifier {
        isa,
        func,
        cfg,
        liveness,
    };
    verifier.check_ebbs(errors)?;
    verifier.check_insts(errors)?;
    Ok(())
}

struct LivenessVerifier<'a> {
    isa: &'a dyn TargetIsa,
    func: &'a Function,
    cfg: &'a ControlFlowGraph,
    liveness: &'a Liveness,
}

impl<'a> LivenessVerifier<'a> {
    /// Check all EBB arguments.
    fn check_ebbs(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
        for ebb in self.func.layout.ebbs() {
            for &val in self.func.dfg.ebb_params(ebb) {
                let lr = match self.liveness.get(val) {
                    Some(lr) => lr,
                    None => return fatal!(errors, ebb, "EBB arg {} has no live range", val),
                };
                self.check_lr(ebb.into(), val, lr, errors)?;
            }
        }
        Ok(())
    }

    /// Check all instructions.
    fn check_insts(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
        for ebb in self.func.layout.ebbs() {
            for inst in self.func.layout.ebb_insts(ebb) {
                let encoding = self.func.encodings[inst];

                // Check the defs.
                for &val in self.func.dfg.inst_results(inst) {
                    let lr = match self.liveness.get(val) {
                        Some(lr) => lr,
                        None => return fatal!(errors, inst, "{} has no live range", val),
                    };
                    self.check_lr(inst.into(), val, lr, errors)?;

                    if encoding.is_legal() {
                        // A legal instruction is not allowed to define ghost values.
                        if lr.affinity.is_unassigned() {
                            return fatal!(
                                errors,
                                inst,
                                "{} is a ghost value defined by a real [{}] instruction",
                                val,
                                self.isa.encoding_info().display(encoding)
                            );
                        }
                    } else if !lr.affinity.is_unassigned() {
                        // A non-encoded instruction can only define ghost values.
                        return fatal!(
                            errors,
                            inst,
                            "{} is a real {} value defined by a ghost instruction",
                            val,
                            lr.affinity.display(&self.isa.register_info())
                        );
                    }
                }

                // Check the uses.
                for &val in self.func.dfg.inst_args(inst) {
                    let lr = match self.liveness.get(val) {
                        Some(lr) => lr,
                        None => return fatal!(errors, inst, "{} has no live range", val),
                    };
                    if !self.live_at_use(lr, inst) {
                        return fatal!(errors, inst, "{} is not live at this use", val);
                    }

                    // A legal instruction is not allowed to depend on ghost values.
                    if encoding.is_legal() && lr.affinity.is_unassigned() {
                        return fatal!(
                            errors,
                            inst,
                            "{} is a ghost value used by a real [{}] instruction",
                            val,
                            self.isa.encoding_info().display(encoding)
                        );
                    }
                }
            }
        }
        Ok(())
    }

    /// Is `lr` live at the use `inst`?
    fn live_at_use(&self, lr: &LiveRange, inst: Inst) -> bool {
        let ctx = self.liveness.context(&self.func.layout);

        // Check if `inst` is in the def range, not including the def itself.
        if ctx.order.cmp(lr.def(), inst) == Ordering::Less
            && ctx.order.cmp(inst, lr.def_local_end()) != Ordering::Greater
        {
            return true;
        }

        // Otherwise see if `inst` is in one of the live-in ranges.
        match lr.livein_local_end(ctx.order.inst_ebb(inst).unwrap(), ctx) {
            Some(end) => ctx.order.cmp(inst, end) != Ordering::Greater,
            None => false,
        }
    }

    /// Check the integrity of the live range `lr`.
    fn check_lr(
        &self,
        def: ProgramPoint,
        val: Value,
        lr: &LiveRange,
        errors: &mut VerifierErrors,
    ) -> VerifierStepResult<()> {
        let l = &self.func.layout;

        let loc: AnyEntity = match def.into() {
            ExpandedProgramPoint::Ebb(e) => e.into(),
            ExpandedProgramPoint::Inst(i) => i.into(),
        };
        if lr.def() != def {
            return fatal!(
                errors,
                loc,
                "Wrong live range def ({}) for {}",
                lr.def(),
                val
            );
        }
        if lr.is_dead() {
            if !lr.is_local() {
                return fatal!(errors, loc, "Dead live range {} should be local", val);
            } else {
                return Ok(());
            }
        }
        let def_ebb = match def.into() {
            ExpandedProgramPoint::Ebb(e) => e,
            ExpandedProgramPoint::Inst(i) => l.inst_ebb(i).unwrap(),
        };
        match lr.def_local_end().into() {
            ExpandedProgramPoint::Ebb(e) => {
                return fatal!(
                    errors,
                    loc,
                    "Def local range for {} can't end at {}",
                    val,
                    e
                );
            }
            ExpandedProgramPoint::Inst(i) => {
                if self.func.layout.inst_ebb(i) != Some(def_ebb) {
                    return fatal!(errors, loc, "Def local end for {} in wrong ebb", val);
                }
            }
        }

        // Now check the live-in intervals against the CFG.
        for (mut ebb, end) in lr.liveins(self.liveness.context(l)) {
            if !l.is_ebb_inserted(ebb) {
                return fatal!(
                    errors,
                    loc,
                    "{} livein at {} which is not in the layout",
                    val,
                    ebb
                );
            }
            let end_ebb = match l.inst_ebb(end) {
                Some(e) => e,
                None => {
                    return fatal!(
                        errors,
                        loc,
                        "{} livein for {} ends at {} which is not in the layout",
                        val,
                        ebb,
                        end
                    );
                }
            };

            // Check all the EBBs in the interval independently.
            loop {
                // If `val` is live-in at `ebb`, it must be live at all the predecessors.
                for BasicBlock { inst: pred, .. } in self.cfg.pred_iter(ebb) {
                    if !self.live_at_use(lr, pred) {
                        return fatal!(
                            errors,
                            pred,
                            "{} is live in to {} but not live at predecessor",
                            val,
                            ebb
                        );
                    }
                }

                if ebb == end_ebb {
                    break;
                }
                ebb = match l.next_ebb(ebb) {
                    Some(e) => e,
                    None => {
                        return fatal!(
                            errors,
                            loc,
                            "end of {} livein ({}) never reached",
                            val,
                            end_ebb
                        );
                    }
                };
            }
        }

        Ok(())
    }
}
<commit_msg>Use LiveRange::reaches_use instead of reimplementing it in liveness checks;<commit_after>//! Liveness verifier.

use crate::flowgraph::{BasicBlock, ControlFlowGraph};
use crate::ir::entities::AnyEntity;
use crate::ir::{ExpandedProgramPoint, Function, ProgramPoint, Value};
use crate::isa::TargetIsa;
use crate::regalloc::liveness::Liveness;
use crate::regalloc::liverange::LiveRange;
use crate::timing;
use crate::verifier::{VerifierErrors, VerifierStepResult};

/// Verify liveness information for `func`.
///
/// The provided control flow graph is assumed to be sound.
///
/// - All values in the program must have a live range.
/// - The live range def point must match where the value is defined.
/// - The live range must reach all uses.
/// - When a live range is live-in to an EBB, it must be live at all the predecessors.
/// - The live range affinity must be compatible with encoding constraints.
///
/// We don't verify that live ranges are minimal. This would require recomputing live ranges for
/// all values.
pub fn verify_liveness(
    isa: &dyn TargetIsa,
    func: &Function,
    cfg: &ControlFlowGraph,
    liveness: &Liveness,
    errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
    let _tt = timing::verify_liveness();
    let verifier = LivenessVerifier {
        isa,
        func,
        cfg,
        liveness,
    };
    verifier.check_ebbs(errors)?;
    verifier.check_insts(errors)?;
    Ok(())
}

struct LivenessVerifier<'a> {
    isa: &'a dyn TargetIsa,
    func: &'a Function,
    cfg: &'a ControlFlowGraph,
    liveness: &'a Liveness,
}

impl<'a> LivenessVerifier<'a> {
    /// Check all EBB arguments.
    fn check_ebbs(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
        for ebb in self.func.layout.ebbs() {
            for &val in self.func.dfg.ebb_params(ebb) {
                let lr = match self.liveness.get(val) {
                    Some(lr) => lr,
                    None => return fatal!(errors, ebb, "EBB arg {} has no live range", val),
                };
                self.check_lr(ebb.into(), val, lr, errors)?;
            }
        }
        Ok(())
    }

    /// Check all instructions.
    fn check_insts(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
        let lr_ctx = self.liveness.context(&self.func.layout);
        for ebb in self.func.layout.ebbs() {
            for inst in self.func.layout.ebb_insts(ebb) {
                let encoding = self.func.encodings[inst];

                // Check the defs.
                for &val in self.func.dfg.inst_results(inst) {
                    let lr = match self.liveness.get(val) {
                        Some(lr) => lr,
                        None => return fatal!(errors, inst, "{} has no live range", val),
                    };
                    self.check_lr(inst.into(), val, lr, errors)?;

                    if encoding.is_legal() {
                        // A legal instruction is not allowed to define ghost values.
                        if lr.affinity.is_unassigned() {
                            return fatal!(
                                errors,
                                inst,
                                "{} is a ghost value defined by a real [{}] instruction",
                                val,
                                self.isa.encoding_info().display(encoding)
                            );
                        }
                    } else if !lr.affinity.is_unassigned() {
                        // A non-encoded instruction can only define ghost values.
                        return fatal!(
                            errors,
                            inst,
                            "{} is a real {} value defined by a ghost instruction",
                            val,
                            lr.affinity.display(&self.isa.register_info())
                        );
                    }
                }

                // Check the uses.
                for &val in self.func.dfg.inst_args(inst) {
                    let lr = match self.liveness.get(val) {
                        Some(lr) => lr,
                        None => return fatal!(errors, inst, "{} has no live range", val),
                    };

                    debug_assert!(lr_ctx.order.inst_ebb(inst).unwrap() == ebb);
                    if !lr.reaches_use(inst, ebb, lr_ctx) {
                        return fatal!(errors, inst, "{} is not live at this use", val);
                    }

                    // A legal instruction is not allowed to depend on ghost values.
                    if encoding.is_legal() && lr.affinity.is_unassigned() {
                        return fatal!(
                            errors,
                            inst,
                            "{} is a ghost value used by a real [{}] instruction",
                            val,
                            self.isa.encoding_info().display(encoding)
                        );
                    }
                }
            }
        }
        Ok(())
    }

    /// Check the integrity of the live range `lr`.
    fn check_lr(
        &self,
        def: ProgramPoint,
        val: Value,
        lr: &LiveRange,
        errors: &mut VerifierErrors,
    ) -> VerifierStepResult<()> {
        let l = &self.func.layout;

        let loc: AnyEntity = match def.into() {
            ExpandedProgramPoint::Ebb(e) => e.into(),
            ExpandedProgramPoint::Inst(i) => i.into(),
        };
        if lr.def() != def {
            return fatal!(
                errors,
                loc,
                "Wrong live range def ({}) for {}",
                lr.def(),
                val
            );
        }
        if lr.is_dead() {
            if !lr.is_local() {
                return fatal!(errors, loc, "Dead live range {} should be local", val);
            } else {
                return Ok(());
            }
        }
        let def_ebb = match def.into() {
            ExpandedProgramPoint::Ebb(e) => e,
            ExpandedProgramPoint::Inst(i) => l.inst_ebb(i).unwrap(),
        };
        match lr.def_local_end().into() {
            ExpandedProgramPoint::Ebb(e) => {
                return fatal!(
                    errors,
                    loc,
                    "Def local range for {} can't end at {}",
                    val,
                    e
                );
            }
            ExpandedProgramPoint::Inst(i) => {
                if self.func.layout.inst_ebb(i) != Some(def_ebb) {
                    return fatal!(errors, loc, "Def local end for {} in wrong ebb", val);
                }
            }
        }

        // Now check the live-in intervals against the CFG.
        for (mut ebb, end) in lr.liveins(self.liveness.context(l)) {
            if !l.is_ebb_inserted(ebb) {
                return fatal!(
                    errors,
                    loc,
                    "{} livein at {} which is not in the layout",
                    val,
                    ebb
                );
            }
            let end_ebb = match l.inst_ebb(end) {
                Some(e) => e,
                None => {
                    return fatal!(
                        errors,
                        loc,
                        "{} livein for {} ends at {} which is not in the layout",
                        val,
                        ebb,
                        end
                    );
                }
            };

            let lr_ctx = self.liveness.context(&self.func.layout);

            // Check all the EBBs in the interval independently.
            loop {
                // If `val` is live-in at `ebb`, it must be live at all the predecessors.
                for BasicBlock { inst: pred, ebb } in self.cfg.pred_iter(ebb) {
                    if !lr.reaches_use(pred, ebb, lr_ctx) {
                        return fatal!(
                            errors,
                            pred,
                            "{} is live in to {} but not live at predecessor",
                            val,
                            ebb
                        );
                    }
                }

                if ebb == end_ebb {
                    break;
                }
                ebb = match l.next_ebb(ebb) {
                    Some(e) => e,
                    None => {
                        return fatal!(
                            errors,
                            loc,
                            "end of {} livein ({}) never reached",
                            val,
                            end_ebb
                        );
                    }
                };
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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.

use crate::lexer::Lexer;
use crate::lexer::TokenPosition;
use crate::parser::Parser;
use crate::rename::rename;
use crate::server::LspSender;
use crate::server::Message;
use crate::server::Read;
use crate::server::Request;
use crate::server::Server;
use crate::server::Write;
use lsp_types::Diagnostic;
use lsp_types::DiagnosticSeverity;
use lsp_types::DidChangeTextDocumentParams;
use lsp_types::DidOpenTextDocumentParams;
use lsp_types::DocumentHighlight;
use lsp_types::DocumentHighlightParams;
use lsp_types::Position;
use lsp_types::PublishDiagnosticsParams;
use lsp_types::RenameParams;
use lsp_types::WorkspaceEdit;

use lsp_types::Range;
use lsp_types::Url;
use serde_json::json;
use std::collections::HashMap;

/// Runs the main loop of the LSP server.
///
/// This method finishes when `exit` notification is received.
pub fn run<R: Read, W: Write + Send + 'static>(server: Server<R, W>) {
    let mut state = State {
        files: HashMap::new(),
        sender: server.sender(),
    };
    for msg in server {
        state.handle_message(msg);
    }
}

struct State {
    files: HashMap<String, String>,
    sender: LspSender,
}

fn token_position_to_range(position: &TokenPosition) -> Range {
    Range {
        start: Position {
            line: position.start.line as u64,
            character: position.start.character as u64,
        },
        end: Position {
            line: position.end.line as u64,
            character: position.end.character as u64,
        },
    }
}

impl State {
    fn handle_message(&mut self, msg: Message) {
        match msg {
            Message::Request(req) => match req.method.as_ref() {
                "initialize" => {
                    req.response_handle.respond(Ok(json!({"capabilities": {
                        "renameProvider": true,
                        "documentHighlightProvider": true,
                    }})));
                }
                "textDocument/rename" => {
                    self.handle_rename(req);
                }
                "textDocument/documentHighlight" => {
                    self.handle_document_highlight(req);
                }
                method => {
                    eprintln!("Unrecognized request: {}", method);
                }
            },
            Message::Notification(notification) => match notification.method.as_ref() {
                "initialized" => {}
                "textDocument/didOpen" => {
                    let params: DidOpenTextDocumentParams =
                        serde_json::from_value(notification.params.clone()).unwrap();
                    self.handle_did_open(params);
                }
                "textDocument/didChange" => {
                    let params: DidChangeTextDocumentParams =
                        serde_json::from_value(notification.params.clone()).unwrap();
                    self.handle_did_change(params);
                }
                method => {
                    eprintln!("Unrecognized notification: {}", method);
                }
            },
        }
    }

    fn handle_did_open(&mut self, params: DidOpenTextDocumentParams) {
        self.files.insert(
            params.text_document.uri.as_str().to_string(),
            params.text_document.text.to_string(),
        );

        publish_diagnostics(
            &params.text_document.text,
            params.text_document.uri,
            &self.sender,
        );
    }

    fn handle_did_change(&mut self, params: DidChangeTextDocumentParams) {
        // TODO: Add support for partial content changes
        if params.content_changes.len() != 1 {
            panic!("unsupported not one content changes");
        }
        if !params.content_changes[0].range.is_none() {
            panic!("unsupported partial content change");
        }
        self.files.insert(
            params.text_document.uri.as_str().to_string(),
            params.content_changes[0].text.to_string(),
        );
        publish_diagnostics(
            &params.content_changes[0].text,
            params.text_document.uri,
            &self.sender,
        );
    }

    fn handle_rename(&self, req: Request) {
        // TODO: This doesn't work yet, it is still WIP!
        let params: RenameParams = serde_json::from_value(req.params.clone()).unwrap();
        let content = self
            .files
            .get(params.text_document_position.text_document.uri.as_str())
            .unwrap();
        let edits = rename(
            content,
            params.text_document_position.position,
            &params.new_name,
        )
        .unwrap();
        let mut changes = HashMap::new();
        changes.insert(params.text_document_position.text_document.uri, edits);
        req.response_handle
            .respond(Ok(serde_json::to_value(WorkspaceEdit {
                changes: Some(changes),
                document_changes: None,
            })
            .unwrap()))
    }

    fn handle_document_highlight(&self, req: Request) {
        // TODO: This doesn't work yet, it is still WIP!
        let params: DocumentHighlightParams = serde_json::from_value(req.params.clone()).unwrap();
        let start = params.text_document_position_params.position;
        let mut end = params.text_document_position_params.position;
        end.character += 2;
        req.response_handle
            .respond(Ok(serde_json::to_value(vec![DocumentHighlight {
                kind: None,
                range: Range {
                    start: start,
                    end: end,
                },
            }])
            .unwrap()));
    }
}

fn publish_diagnostics(text: &str, uri: Url, sender: &LspSender) {
    let mut parser = Parser::new(Lexer::new(text));
    parser.parse();
    let mut diagnostics_params = PublishDiagnosticsParams {
        uri: uri,
        diagnostics: Vec::new(),
        version: None,
    };
    for error in parser.errors {
        diagnostics_params.diagnostics.push(Diagnostic {
            range: token_position_to_range(&error.position),
            message: error.message,
            code: None,
            related_information: None,
            severity: Some(DiagnosticSeverity::Error),
            source: None,
            tags: None,
        });
    }
    sender.send_notification(
        "textDocument/publishDiagnostics",
        serde_json::to_value(diagnostics_params).unwrap(),
    );
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io;
    use std::sync::mpsc::channel;
    use std::sync::mpsc::Receiver;
    use std::sync::mpsc::Sender;

    struct FakeReader {
        receiver: Receiver<String>,
    }

    impl FakeReader {
        fn new() -> (Sender<String>, FakeReader) {
            let (sender, receiver) = channel();
            return (sender, FakeReader { receiver: receiver });
        }
    }

    impl Read for FakeReader {
        fn read_packet(&mut self) -> Result<String, io::Error> {
            self.receiver
                .recv()
                .map_err(|_| io::Error::new(io::ErrorKind::UnexpectedEof, "EOF encountered"))
        }
    }
    struct FakeWriter {
        sender: Sender<String>,
    }

    impl FakeWriter {
        fn new() -> (Receiver<String>, FakeWriter) {
            let (sender, receiver) = channel();
            return (receiver, FakeWriter { sender: sender });
        }
    }

    impl Write for FakeWriter {
        fn write_packet(&self, packet: String) -> Result<(), io::Error> {
            self.sender.send(packet).unwrap();
            Ok(())
        }
    }

    struct Client {
        sender: Sender<String>,
        receiver: Receiver<String>,
    }

    impl Client {
        fn recv(&self) -> Result<serde_json::Value, ()> {
            Ok(self.receiver.recv().unwrap().parse().unwrap())
        }
        fn send(&self, req: serde_json::Value) -> Result<(), ()> {
            self.sender.send(req.to_string()).unwrap();
            Ok(())
        }
    }

    fn create_client_and_server() -> (Client, Server<FakeReader, FakeWriter>) {
        let (writer_ch, writer) = FakeWriter::new();
        let (reader_ch, reader) = FakeReader::new();
        let client = Client {
            sender: reader_ch,
            receiver: writer_ch,
        };
        let server = Server::new(reader, writer);
        return (client, server);
    }

    #[test]
    fn responds_to_initialize() {
        let (client, server) = create_client_and_server();
        let t = std::thread::spawn(move || {
            run(server);
        });

        client
            .send(json!({
                "jsonrpc": "2.0",
                "id": 1,
                "method": "initialize",
                "params": {
                    "processId": serde_json::Value::Null,
                    "rootUri": serde_json::Value::Null,
                    "capabilities": {
                    },
                },
            }))
            .unwrap();
        client.recv().unwrap();
        client
            .send(json!({
                "jsonrpc": "2.0",
                "method": "exit",
            }))
            .unwrap();

        t.join().unwrap();
    }
}
<commit_msg>Disable document highlights for now<commit_after>// Copyright 2019 Google LLC
//
// 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.

use crate::lexer::Lexer;
use crate::lexer::TokenPosition;
use crate::parser::Parser;
use crate::rename::rename;
use crate::server::LspSender;
use crate::server::Message;
use crate::server::Read;
use crate::server::Request;
use crate::server::Server;
use crate::server::Write;
use lsp_types::Diagnostic;
use lsp_types::DiagnosticSeverity;
use lsp_types::DidChangeTextDocumentParams;
use lsp_types::DidOpenTextDocumentParams;
use lsp_types::Position;
use lsp_types::PublishDiagnosticsParams;
use lsp_types::RenameParams;
use lsp_types::WorkspaceEdit;

use lsp_types::Range;
use lsp_types::Url;
use serde_json::json;
use std::collections::HashMap;

/// Runs the main loop of the LSP server.
///
/// This method finishes when `exit` notification is received.
pub fn run<R: Read, W: Write + Send + 'static>(server: Server<R, W>) {
    let mut state = State {
        files: HashMap::new(),
        sender: server.sender(),
    };
    for msg in server {
        state.handle_message(msg);
    }
}

struct State {
    files: HashMap<String, String>,
    sender: LspSender,
}

fn token_position_to_range(position: &TokenPosition) -> Range {
    Range {
        start: Position {
            line: position.start.line as u64,
            character: position.start.character as u64,
        },
        end: Position {
            line: position.end.line as u64,
            character: position.end.character as u64,
        },
    }
}

impl State {
    fn handle_message(&mut self, msg: Message) {
        match msg {
            Message::Request(req) => match req.method.as_ref() {
                "initialize" => {
                    req.response_handle.respond(Ok(json!({"capabilities": {
                        "renameProvider": true,
                        "documentHighlightProvider": true,
                    }})));
                }
                "textDocument/rename" => {
                    self.handle_rename(req);
                }
                "textDocument/documentHighlight" => {
                    self.handle_document_highlight(req);
                }
                method => {
                    eprintln!("Unrecognized request: {}", method);
                }
            },
            Message::Notification(notification) => match notification.method.as_ref() {
                "initialized" => {}
                "textDocument/didOpen" => {
                    let params: DidOpenTextDocumentParams =
                        serde_json::from_value(notification.params.clone()).unwrap();
                    self.handle_did_open(params);
                }
                "textDocument/didChange" => {
                    let params: DidChangeTextDocumentParams =
                        serde_json::from_value(notification.params.clone()).unwrap();
                    self.handle_did_change(params);
                }
                method => {
                    eprintln!("Unrecognized notification: {}", method);
                }
            },
        }
    }

    fn handle_did_open(&mut self, params: DidOpenTextDocumentParams) {
        self.files.insert(
            params.text_document.uri.as_str().to_string(),
            params.text_document.text.to_string(),
        );

        publish_diagnostics(
            &params.text_document.text,
            params.text_document.uri,
            &self.sender,
        );
    }

    fn handle_did_change(&mut self, params: DidChangeTextDocumentParams) {
        // TODO: Add support for partial content changes
        if params.content_changes.len() != 1 {
            panic!("unsupported not one content changes");
        }
        if !params.content_changes[0].range.is_none() {
            panic!("unsupported partial content change");
        }
        self.files.insert(
            params.text_document.uri.as_str().to_string(),
            params.content_changes[0].text.to_string(),
        );
        publish_diagnostics(
            &params.content_changes[0].text,
            params.text_document.uri,
            &self.sender,
        );
    }

    fn handle_rename(&self, req: Request) {
        // TODO: This doesn't work yet, it is still WIP!
        let params: RenameParams = serde_json::from_value(req.params.clone()).unwrap();
        let content = self
            .files
            .get(params.text_document_position.text_document.uri.as_str())
            .unwrap();
        let edits = rename(
            content,
            params.text_document_position.position,
            &params.new_name,
        )
        .unwrap();
        let mut changes = HashMap::new();
        changes.insert(params.text_document_position.text_document.uri, edits);
        req.response_handle
            .respond(Ok(serde_json::to_value(WorkspaceEdit {
                changes: Some(changes),
                document_changes: None,
            })
            .unwrap()))
    }

    fn handle_document_highlight(&self, req: Request) {
        req.response_handle
            .respond(Ok(serde_json::Value::Null));
    }
}

fn publish_diagnostics(text: &str, uri: Url, sender: &LspSender) {
    let mut parser = Parser::new(Lexer::new(text));
    parser.parse();
    let mut diagnostics_params = PublishDiagnosticsParams {
        uri: uri,
        diagnostics: Vec::new(),
        version: None,
    };
    for error in parser.errors {
        diagnostics_params.diagnostics.push(Diagnostic {
            range: token_position_to_range(&error.position),
            message: error.message,
            code: None,
            related_information: None,
            severity: Some(DiagnosticSeverity::Error),
            source: None,
            tags: None,
        });
    }
    sender.send_notification(
        "textDocument/publishDiagnostics",
        serde_json::to_value(diagnostics_params).unwrap(),
    );
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io;
    use std::sync::mpsc::channel;
    use std::sync::mpsc::Receiver;
    use std::sync::mpsc::Sender;

    struct FakeReader {
        receiver: Receiver<String>,
    }

    impl FakeReader {
        fn new() -> (Sender<String>, FakeReader) {
            let (sender, receiver) = channel();
            return (sender, FakeReader { receiver: receiver });
        }
    }

    impl Read for FakeReader {
        fn read_packet(&mut self) -> Result<String, io::Error> {
            self.receiver
                .recv()
                .map_err(|_| io::Error::new(io::ErrorKind::UnexpectedEof, "EOF encountered"))
        }
    }
    struct FakeWriter {
        sender: Sender<String>,
    }

    impl FakeWriter {
        fn new() -> (Receiver<String>, FakeWriter) {
            let (sender, receiver) = channel();
            return (receiver, FakeWriter { sender: sender });
        }
    }

    impl Write for FakeWriter {
        fn write_packet(&self, packet: String) -> Result<(), io::Error> {
            self.sender.send(packet).unwrap();
            Ok(())
        }
    }

    struct Client {
        sender: Sender<String>,
        receiver: Receiver<String>,
    }

    impl Client {
        fn recv(&self) -> Result<serde_json::Value, ()> {
            Ok(self.receiver.recv().unwrap().parse().unwrap())
        }
        fn send(&self, req: serde_json::Value) -> Result<(), ()> {
            self.sender.send(req.to_string()).unwrap();
            Ok(())
        }
    }

    fn create_client_and_server() -> (Client, Server<FakeReader, FakeWriter>) {
        let (writer_ch, writer) = FakeWriter::new();
        let (reader_ch, reader) = FakeReader::new();
        let client = Client {
            sender: reader_ch,
            receiver: writer_ch,
        };
        let server = Server::new(reader, writer);
        return (client, server);
    }

    #[test]
    fn responds_to_initialize() {
        let (client, server) = create_client_and_server();
        let t = std::thread::spawn(move || {
            run(server);
        });

        client
            .send(json!({
                "jsonrpc": "2.0",
                "id": 1,
                "method": "initialize",
                "params": {
                    "processId": serde_json::Value::Null,
                    "rootUri": serde_json::Value::Null,
                    "capabilities": {
                    },
                },
            }))
            .unwrap();
        client.recv().unwrap();
        client
            .send(json!({
                "jsonrpc": "2.0",
                "method": "exit",
            }))
            .unwrap();

        t.join().unwrap();
    }
}
<|endoftext|>


<commit_before>//! Exposes the `Middleware` trait which must be implemented by
//! all middleware.

use super::response::Response;
use super::request::Request;
use super::alloy::Alloy;

/// The Status returned by `Middleware's` `enter` and `exit` methods. This indicates
/// to the `Furnace` whether this is a terminal `Middleware` or whether to continue
/// passing the `Request` and `Response` down the `Furnace's` stack.
///
/// Most `Furnaces` will ignore the returned `Status` from the `exit` method of
/// `Middleware`.
pub enum Status {
    /// `Continue` indicates that this is an intermediate `Middleware` in the stack
    /// and the `Furnace` should continue passing requests down the `Furnace's`
    /// stack.
    ///
    /// Most `Middleware` will return `Continue` from both `enter` and `exit`.
    Continue,

    /// `Unwind` indicates that this is a terminal `Middleware` or that something
    /// went wrong. It can be used to immediately stop passing requests down the
    /// `Furnace's` stack and start calling `exit` of all previous `Middleware`.
    ///
    /// For instance, an authorization `Middleware` might return `Unwind` if the
    /// `Request` fails an authentication check, and `Continue` otherwise.
    Unwind
}

/// All middleware should implement `Middleware`, which allows it to be `smelted`
/// to a `Furnace` so that it will be called for each incoming request.
///
/// There are two sorts of data associated with `Middleware`, data internal
/// to the `Middleware` and data that the `Middleware` would like to expose to
/// other `Middleware` further down the stack or to terminal controllers.
///
/// Internal data should be stored on the `struct` that implements `Middleware`
/// itself. All `Middleware` are cloned for each client request, so the object
/// initially smelted on to the `Iron` instance will be provided as `&mut self` to
/// enter for every request. Data stored on a `Middleware` instance does _not_ persist
/// between requests and is _not_ shared between different, concurrent, requests.
/// The same is true for data stored on an `Alloy`.
///
/// External data should be stored in the `Alloy` passed to both `enter` and
/// `exit`. `Alloy` is a thin wrapper around `AnyMap` and is effectively a
/// a key value store from a type to an instance of that type. This means
/// that each `Middleware` can have a unique type that it stores in the `Alloy`.
/// This can either be an instance of that `Middleware` or some other type. Since
/// the same `Alloy` is passed to all further `Middleware` in the `Furnace`, this
/// scheme allows you to expose data or functionality to future `Middleware`.
pub trait Middleware: Send + Clone {
    /// `enter` is called for each `Middleware` in a `Furnace` as a client request
    /// comes down the stack. `Middleware` have their `enter` methods called in the order
    /// in which they were added to the stack, that is, FIFO. `Middleware` should expose
    /// data through `Alloy` and store any data that will persist through the request here.
    ///
    /// Returning `Unwind` from this handler will cause the `Furnace` to stop
    /// going down its stack and start bubbling back up through `Middleware`
    /// and calling `exit` on them.
    fn enter(&mut self,
             _request: &mut Request,
             _response: &mut Response,
             _alloy: &mut Alloy) -> Status {
        Continue
    }

    /// `exit` is called for each `Middleware` in a `Furnace` that has had its `enter`
    /// method called for this client request. A `Middleware's` `exit` method will be called
    /// as the stack is unwound in FILO order - i.e, `Middleware` have their `exit`
    /// methods called in opposite order from which `enter` was called.
    ///
    /// While this method must return a `Status`, most `Furnaces` will ignore
    /// this method's return value.
    fn exit(&mut self,
            _request: &mut Request,
            _response: &mut Response,
            _alloy: &mut Alloy) -> Status {
        Continue
    }

    // Helper function to clone the Middleware.
    #[allow(missing_doc)]
    fn clone_box(&self) -> Box<Middleware> { box self.clone() as Box<Middleware> }
}

impl Clone for Box<Middleware> {
    fn clone(&self) -> Box<Middleware> { self.clone_box() }
}
<commit_msg>(feat) Derived Clone + Show for Status.<commit_after>//! Exposes the `Middleware` trait which must be implemented by
//! all middleware.

use super::response::Response;
use super::request::Request;
use super::alloy::Alloy;

/// The Status returned by `Middleware's` `enter` and `exit` methods. This indicates
/// to the `Furnace` whether this is a terminal `Middleware` or whether to continue
/// passing the `Request` and `Response` down the `Furnace's` stack.
///
/// Most `Furnaces` will ignore the returned `Status` from the `exit` method of
/// `Middleware`.
#[deriving(Clone, Show)]
pub enum Status {
    /// `Continue` indicates that this is an intermediate `Middleware` in the stack
    /// and the `Furnace` should continue passing requests down the `Furnace's`
    /// stack.
    ///
    /// Most `Middleware` will return `Continue` from both `enter` and `exit`.
    Continue,

    /// `Unwind` indicates that this is a terminal `Middleware` or that something
    /// went wrong. It can be used to immediately stop passing requests down the
    /// `Furnace's` stack and start calling `exit` of all previous `Middleware`.
    ///
    /// For instance, an authorization `Middleware` might return `Unwind` if the
    /// `Request` fails an authentication check, and `Continue` otherwise.
    Unwind
}

/// All middleware should implement `Middleware`, which allows it to be `smelted`
/// to a `Furnace` so that it will be called for each incoming request.
///
/// There are two sorts of data associated with `Middleware`, data internal
/// to the `Middleware` and data that the `Middleware` would like to expose to
/// other `Middleware` further down the stack or to terminal controllers.
///
/// Internal data should be stored on the `struct` that implements `Middleware`
/// itself. All `Middleware` are cloned for each client request, so the object
/// initially smelted on to the `Iron` instance will be provided as `&mut self` to
/// enter for every request. Data stored on a `Middleware` instance does _not_ persist
/// between requests and is _not_ shared between different, concurrent, requests.
/// The same is true for data stored on an `Alloy`.
///
/// External data should be stored in the `Alloy` passed to both `enter` and
/// `exit`. `Alloy` is a thin wrapper around `AnyMap` and is effectively a
/// a key value store from a type to an instance of that type. This means
/// that each `Middleware` can have a unique type that it stores in the `Alloy`.
/// This can either be an instance of that `Middleware` or some other type. Since
/// the same `Alloy` is passed to all further `Middleware` in the `Furnace`, this
/// scheme allows you to expose data or functionality to future `Middleware`.
pub trait Middleware: Send + Clone {
    /// `enter` is called for each `Middleware` in a `Furnace` as a client request
    /// comes down the stack. `Middleware` have their `enter` methods called in the order
    /// in which they were added to the stack, that is, FIFO. `Middleware` should expose
    /// data through `Alloy` and store any data that will persist through the request here.
    ///
    /// Returning `Unwind` from this handler will cause the `Furnace` to stop
    /// going down its stack and start bubbling back up through `Middleware`
    /// and calling `exit` on them.
    fn enter(&mut self,
             _request: &mut Request,
             _response: &mut Response,
             _alloy: &mut Alloy) -> Status {
        Continue
    }

    /// `exit` is called for each `Middleware` in a `Furnace` that has had its `enter`
    /// method called for this client request. A `Middleware's` `exit` method will be called
    /// as the stack is unwound in FILO order - i.e, `Middleware` have their `exit`
    /// methods called in opposite order from which `enter` was called.
    ///
    /// While this method must return a `Status`, most `Furnaces` will ignore
    /// this method's return value.
    fn exit(&mut self,
            _request: &mut Request,
            _response: &mut Response,
            _alloy: &mut Alloy) -> Status {
        Continue
    }

    // Helper function to clone the Middleware.
    #[allow(missing_doc)]
    fn clone_box(&self) -> Box<Middleware> { box self.clone() as Box<Middleware> }
}

impl Clone for Box<Middleware> {
    fn clone(&self) -> Box<Middleware> { self.clone_box() }
}
<|endoftext|>


<commit_before>use winapi::*;
use d3d12_safe::*;
use std::marker::PhantomData;
use std::ops::Deref;

pub struct ResourceDescBuilder {
  alignment: u64,
}

pub fn resource_desc_buffer(size_in_bytes: u64) -> D3D12_RESOURCE_DESC {
  D3D12_RESOURCE_DESC {
    Dimension: D3D12_RESOURCE_DIMENSION_BUFFER,
    Alignment:  0,
    Width: size_in_bytes,
    Height: 1,
    DepthOrArraySize: 1,
    MipLevels: 1,
    Format: DXGI_FORMAT_UNKNOWN,
    SampleDesc: DXGI_SAMPLE_DESC {Count:1, Quality: 0,},
    Layout: D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
    Flags: D3D12_RESOURCE_FLAG_NONE,
  }
}

pub fn heap_properties_upload() -> D3D12_HEAP_PROPERTIES {
  D3D12_HEAP_PROPERTIES {
    Type: D3D12_HEAP_TYPE_UPLOAD,
    CPUPageProperty: D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
    MemoryPoolPreference: D3D12_MEMORY_POOL_UNKNOWN,
    CreationNodeMask: 0,
    VisibleNodeMask: 0,
  }
}

pub fn heap_properties_default() -> D3D12_HEAP_PROPERTIES {
  D3D12_HEAP_PROPERTIES {
    Type: D3D12_HEAP_TYPE_DEFAULT,
    CPUPageProperty: D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
    MemoryPoolPreference: D3D12_MEMORY_POOL_UNKNOWN,
    CreationNodeMask: 0,
    VisibleNodeMask: 0,
  }
}

pub enum ResourceBarrier<'a,'b> {
  Transition { data: D3D12_RESOURCE_BARRIER, phantom: PhantomData<&'a D3D12Resource>, },
  Aliasing { data: D3D12_RESOURCE_BARRIER, phantom1: PhantomData<&'a D3D12Resource>, phantom2: PhantomData<&'b D3D12Resource>, },
  Uav { data: D3D12_RESOURCE_BARRIER, phantom: PhantomData<&'a D3D12Resource>, },
}

impl<'e, 'f> ResourceBarrier<'e,'f> {
  pub fn transition<'a>(
    resource: &'a D3D12Resource,
    before: D3D12_RESOURCE_STATES, after: D3D12_RESOURCE_STATES
  ) -> ResourceBarrier<'a,'a> {
    let mut ret=
      D3D12_RESOURCE_BARRIER {
        Type: D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
        Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE,
        u: unsafe{ ::std::mem::uninitialized() },
      };
    unsafe {
      *ret.Transition_mut() =
        D3D12_RESOURCE_TRANSITION_BARRIER {
           pResource: resource.iptr() as *mut _,
           StateBefore: before,
           StateAfter: after,
           Subresource: D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
         };
    };
    ResourceBarrier::Transition {
      data: ret,
      phantom: PhantomData::<&'a _>,
    }
  }

  pub fn transition_subresource<'a>(
    resource: &'a D3D12Resource, subresource: UINT,
    before: D3D12_RESOURCE_STATES, after: D3D12_RESOURCE_STATES
  ) -> ResourceBarrier<'a,'a> {
    let mut ret=
      D3D12_RESOURCE_BARRIER {
        Type: D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
        Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE,
        u: unsafe{ ::std::mem::uninitialized() },
      };
    unsafe {
      *ret.Transition_mut() =
        D3D12_RESOURCE_TRANSITION_BARRIER {
           pResource: resource.iptr() as *mut _,
           StateBefore: before,
           StateAfter: after,
           Subresource: subresource,
         };
    };
    ResourceBarrier::Transition {
      data: ret,
      phantom: PhantomData::<&'a _>,
    }
  }

  pub fn aliasing<'a,'b>(
    before: Option<&'a D3D12Resource>, after: &'b D3D12Resource
  ) -> ResourceBarrier<'a,'b> {
    let mut ret=
      D3D12_RESOURCE_BARRIER {
        Type: D3D12_RESOURCE_BARRIER_TYPE_ALIASING,
        Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE,
        u: unsafe{ ::std::mem::uninitialized() },
      };
    unsafe {
      *ret.Aliasing_mut() =
        D3D12_RESOURCE_ALIASING_BARRIER {
           pResourceBefore: before.map(|r|r.iptr()).unwrap_or(::std::ptr::null_mut()) as *mut _,
           pResourceAfter: after.iptr() as *mut _,
         };
    };
    ResourceBarrier::Aliasing {
      data: ret,
      phantom1: PhantomData::<&'a _>,
      phantom2: PhantomData::<&'b _>,
    }
  }

  pub fn uav<'a>(
    resource: &'a D3D12Resource
  ) -> ResourceBarrier<'a,'a> {
    let mut ret=
      D3D12_RESOURCE_BARRIER {
        Type: D3D12_RESOURCE_BARRIER_TYPE_UAV,
        Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE,
        u: unsafe{ ::std::mem::uninitialized() },
      };
    unsafe {
      *ret.UAV_mut() =
        D3D12_RESOURCE_UAV_BARRIER {
           pResource: resource.iptr() as *mut _,
         };
    };
    ResourceBarrier::Uav {
      data: ret,
      phantom: PhantomData::<&'a _>,
    }
  }

  pub fn flags(self, flags: D3D12_RESOURCE_BARRIER_FLAGS) -> ResourceBarrier<'e,'f> {
    match self {
      ResourceBarrier::Transition {mut data, phantom: p} => {
        data.Flags = flags;
        ResourceBarrier::Transition {data: data, phantom: p}
      },
      ResourceBarrier::Aliasing {mut data, phantom1: p1, phantom2: p2} => {
          data.Flags = flags;
          ResourceBarrier::Aliasing {data: data, phantom1: p1, phantom2: p2 }
      },
      ResourceBarrier::Uav {mut data, phantom: p} => {
        data.Flags = flags;
        ResourceBarrier::Uav {data: data, phantom: p}
      },
    }
  }
}

impl<'e, 'f> Deref for ResourceBarrier<'e, 'f> {
  type Target = D3D12_RESOURCE_BARRIER;

  fn deref<'a>(&'a self) -> &'a Self::Target {
    match *self {
      ResourceBarrier::Transition {ref data, ..} => { data },
      ResourceBarrier::Aliasing {ref data, ..} => { data },
      ResourceBarrier::Uav {ref data, ..} => { data },
    }
  }
}

pub enum RootParameter<'a> {
  DescriptorTable { data: D3D12_ROOT_PARAMETER, ranges: PhantomData<&'a [D3D12_DESCRIPTOR_RANGE]>, },
  Other { data: D3D12_ROOT_PARAMETER, },
}

impl<'e> Deref for RootParameter<'e> {
  type Target = D3D12_ROOT_PARAMETER;

  fn deref<'a>(&'a self) -> &'a Self::Target {
    match *self {
      RootParameter::DescriptorTable {ref data, ..} => { data },
      RootParameter::Other {ref data, ..} => { data },
    }
  }
}

impl<'a> RootParameter<'a> {
  pub fn descriptor_table<'b>(ranges: &'b [D3D12_DESCRIPTOR_RANGE], visibility: D3D12_SHADER_VISIBILITY) -> RootParameter<'b> {
    let mut ret = D3D12_ROOT_PARAMETER {
      ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
      u: unsafe { ::std::mem::uninitialized() },
      ShaderVisibility: visibility,
    };
    unsafe{
      *ret.DescriptorTable_mut() = D3D12_ROOT_DESCRIPTOR_TABLE {
        NumDescriptorRanges: ranges.len() as UINT,
        pDescriptorRanges: ranges.as_ptr(),
      };
    };
    RootParameter::DescriptorTable {
      data: ret,
      ranges: PhantomData::<&'b _>,
    }
  }

  pub fn constants<'b>(shader_register: u32, register_space: u32, num_32bit_values: u32, visibility: D3D12_SHADER_VISIBILITY) -> RootParameter<'b> {
    let mut ret = D3D12_ROOT_PARAMETER {
      ParameterType: D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS,
      u: unsafe { ::std::mem::uninitialized() },
      ShaderVisibility: visibility,
    };
    unsafe{
      *ret.Constants_mut() = D3D12_ROOT_CONSTANTS {
        ShaderRegister: shader_register,
        RegisterSpace: register_space,
        Num32BitValues: num_32bit_values,
      };
    };
    RootParameter::Other {
      data: ret,
    }
  }
}

pub fn texture_copy_location_footprint(res: &D3D12Resource, footprint: &D3D12_PLACED_SUBRESOURCE_FOOTPRINT) -> D3D12_TEXTURE_COPY_LOCATION {
  unsafe {
    let mut ret = D3D12_TEXTURE_COPY_LOCATION {
      pResource: res.iptr() as *mut _,
      Type: D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
      u: ::std::mem::uninitialized(),
    };
    *ret.PlacedFootprint_mut() = footprint.clone();
    ret
  }
}

pub fn texture_copy_location_index(res: &D3D12Resource, subresource_index: u32) -> D3D12_TEXTURE_COPY_LOCATION {
  unsafe {
    let mut ret = D3D12_TEXTURE_COPY_LOCATION {
      pResource: res.iptr() as *mut _,
      Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
      u: ::std::mem::uninitialized(),
    };
    *ret.SubresourceIndex_mut() = subresource_index;
    ret
  }
}
<commit_msg>Some more wrappers<commit_after>use winapi::*;
use d3d12_safe::*;
use std::marker::PhantomData;
use std::ops::Deref;

pub struct ResourceDescBuilder {
  alignment: u64,
}

pub fn resource_desc_buffer(size_in_bytes: u64) -> D3D12_RESOURCE_DESC {
  D3D12_RESOURCE_DESC {
    Dimension: D3D12_RESOURCE_DIMENSION_BUFFER,
    Alignment:  0,
    Width: size_in_bytes,
    Height: 1,
    DepthOrArraySize: 1,
    MipLevels: 1,
    Format: DXGI_FORMAT_UNKNOWN,
    SampleDesc: DXGI_SAMPLE_DESC {Count:1, Quality: 0,},
    Layout: D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
    Flags: D3D12_RESOURCE_FLAG_NONE,
  }
}

pub fn heap_properties_upload() -> D3D12_HEAP_PROPERTIES {
  D3D12_HEAP_PROPERTIES {
    Type: D3D12_HEAP_TYPE_UPLOAD,
    CPUPageProperty: D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
    MemoryPoolPreference: D3D12_MEMORY_POOL_UNKNOWN,
    CreationNodeMask: 0,
    VisibleNodeMask: 0,
  }
}

pub fn heap_properties_default() -> D3D12_HEAP_PROPERTIES {
  D3D12_HEAP_PROPERTIES {
    Type: D3D12_HEAP_TYPE_DEFAULT,
    CPUPageProperty: D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
    MemoryPoolPreference: D3D12_MEMORY_POOL_UNKNOWN,
    CreationNodeMask: 0,
    VisibleNodeMask: 0,
  }
}

pub enum ResourceBarrier<'a,'b> {
  Transition { data: D3D12_RESOURCE_BARRIER, phantom: PhantomData<&'a D3D12Resource>, },
  Aliasing { data: D3D12_RESOURCE_BARRIER, phantom1: PhantomData<&'a D3D12Resource>, phantom2: PhantomData<&'b D3D12Resource>, },
  Uav { data: D3D12_RESOURCE_BARRIER, phantom: PhantomData<&'a D3D12Resource>, },
}

impl<'e, 'f> ResourceBarrier<'e,'f> {
  pub fn transition<'a>(
    resource: &'a D3D12Resource,
    before: D3D12_RESOURCE_STATES, after: D3D12_RESOURCE_STATES
  ) -> ResourceBarrier<'a,'a> {
    let mut ret=
      D3D12_RESOURCE_BARRIER {
        Type: D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
        Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE,
        u: unsafe{ ::std::mem::uninitialized() },
      };
    unsafe {
      *ret.Transition_mut() =
        D3D12_RESOURCE_TRANSITION_BARRIER {
           pResource: resource.iptr() as *mut _,
           StateBefore: before,
           StateAfter: after,
           Subresource: D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
         };
    };
    ResourceBarrier::Transition {
      data: ret,
      phantom: PhantomData::<&'a _>,
    }
  }

  pub fn transition_subresource<'a>(
    resource: &'a D3D12Resource, subresource: UINT,
    before: D3D12_RESOURCE_STATES, after: D3D12_RESOURCE_STATES
  ) -> ResourceBarrier<'a,'a> {
    let mut ret=
      D3D12_RESOURCE_BARRIER {
        Type: D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
        Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE,
        u: unsafe{ ::std::mem::uninitialized() },
      };
    unsafe {
      *ret.Transition_mut() =
        D3D12_RESOURCE_TRANSITION_BARRIER {
           pResource: resource.iptr() as *mut _,
           StateBefore: before,
           StateAfter: after,
           Subresource: subresource,
         };
    };
    ResourceBarrier::Transition {
      data: ret,
      phantom: PhantomData::<&'a _>,
    }
  }

  pub fn aliasing<'a,'b>(
    before: Option<&'a D3D12Resource>, after: &'b D3D12Resource
  ) -> ResourceBarrier<'a,'b> {
    let mut ret=
      D3D12_RESOURCE_BARRIER {
        Type: D3D12_RESOURCE_BARRIER_TYPE_ALIASING,
        Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE,
        u: unsafe{ ::std::mem::uninitialized() },
      };
    unsafe {
      *ret.Aliasing_mut() =
        D3D12_RESOURCE_ALIASING_BARRIER {
           pResourceBefore: before.map(|r|r.iptr()).unwrap_or(::std::ptr::null_mut()) as *mut _,
           pResourceAfter: after.iptr() as *mut _,
         };
    };
    ResourceBarrier::Aliasing {
      data: ret,
      phantom1: PhantomData::<&'a _>,
      phantom2: PhantomData::<&'b _>,
    }
  }

  pub fn uav<'a>(
    resource: &'a D3D12Resource
  ) -> ResourceBarrier<'a,'a> {
    let mut ret=
      D3D12_RESOURCE_BARRIER {
        Type: D3D12_RESOURCE_BARRIER_TYPE_UAV,
        Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE,
        u: unsafe{ ::std::mem::uninitialized() },
      };
    unsafe {
      *ret.UAV_mut() =
        D3D12_RESOURCE_UAV_BARRIER {
           pResource: resource.iptr() as *mut _,
         };
    };
    ResourceBarrier::Uav {
      data: ret,
      phantom: PhantomData::<&'a _>,
    }
  }

  pub fn flags(self, flags: D3D12_RESOURCE_BARRIER_FLAGS) -> ResourceBarrier<'e,'f> {
    match self {
      ResourceBarrier::Transition {mut data, phantom: p} => {
        data.Flags = flags;
        ResourceBarrier::Transition {data: data, phantom: p}
      },
      ResourceBarrier::Aliasing {mut data, phantom1: p1, phantom2: p2} => {
          data.Flags = flags;
          ResourceBarrier::Aliasing {data: data, phantom1: p1, phantom2: p2 }
      },
      ResourceBarrier::Uav {mut data, phantom: p} => {
        data.Flags = flags;
        ResourceBarrier::Uav {data: data, phantom: p}
      },
    }
  }
}

impl<'e, 'f> Deref for ResourceBarrier<'e, 'f> {
  type Target = D3D12_RESOURCE_BARRIER;

  fn deref<'a>(&'a self) -> &'a Self::Target {
    match *self {
      ResourceBarrier::Transition {ref data, ..} => { data },
      ResourceBarrier::Aliasing {ref data, ..} => { data },
      ResourceBarrier::Uav {ref data, ..} => { data },
    }
  }
}

pub enum RootParameter<'a> {
  DescriptorTable { data: D3D12_ROOT_PARAMETER, ranges: PhantomData<&'a [D3D12_DESCRIPTOR_RANGE]>, },
  Other { data: D3D12_ROOT_PARAMETER, },
}

impl<'e> Deref for RootParameter<'e> {
  type Target = D3D12_ROOT_PARAMETER;

  fn deref<'a>(&'a self) -> &'a Self::Target {
    match *self {
      RootParameter::DescriptorTable {ref data, ..} => { data },
      RootParameter::Other {ref data, ..} => { data },
    }
  }
}

impl<'a> RootParameter<'a> {
  pub fn descriptor_table<'b>(ranges: &'b [D3D12_DESCRIPTOR_RANGE], visibility: D3D12_SHADER_VISIBILITY) -> RootParameter<'b> {
    let mut ret = D3D12_ROOT_PARAMETER {
      ParameterType: D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE,
      u: unsafe { ::std::mem::uninitialized() },
      ShaderVisibility: visibility,
    };
    unsafe{
      *ret.DescriptorTable_mut() = D3D12_ROOT_DESCRIPTOR_TABLE {
        NumDescriptorRanges: ranges.len() as UINT,
        pDescriptorRanges: ranges.as_ptr(),
      };
    };
    RootParameter::DescriptorTable {
      data: ret,
      ranges: PhantomData::<&'b _>,
    }
  }

  pub fn constants<'b>(shader_register: u32, register_space: u32, num_32bit_values: u32, visibility: D3D12_SHADER_VISIBILITY) -> RootParameter<'b> {
    let mut ret = D3D12_ROOT_PARAMETER {
      ParameterType: D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS,
      u: unsafe { ::std::mem::uninitialized() },
      ShaderVisibility: visibility,
    };
    unsafe{
      *ret.Constants_mut() = D3D12_ROOT_CONSTANTS {
        ShaderRegister: shader_register,
        RegisterSpace: register_space,
        Num32BitValues: num_32bit_values,
      };
    };
    RootParameter::Other {
      data: ret,
    }
  }

  fn other<'b>(shader_register: u32, register_space: u32, visibility: D3D12_SHADER_VISIBILITY, rptype: D3D12_ROOT_PARAMETER_TYPE) -> RootParameter<'b> {
    let mut ret = D3D12_ROOT_PARAMETER {
      ParameterType: rptype,
      u: unsafe { ::std::mem::uninitialized() },
      ShaderVisibility: visibility,
    };
    unsafe{
      *ret.Descriptor_mut() = D3D12_ROOT_DESCRIPTOR {
        ShaderRegister: shader_register,
        RegisterSpace: register_space,
      };
    };
    RootParameter::Other {
      data: ret,
    }
  }

  pub fn cbv<'b>(shader_register: u32, register_space: u32, visibility: D3D12_SHADER_VISIBILITY) -> RootParameter<'b> {
    Self::other(shader_register, register_space, visibility, D3D12_ROOT_PARAMETER_TYPE_CBV)
  }

  pub fn srv<'b>(shader_register: u32, register_space: u32, visibility: D3D12_SHADER_VISIBILITY) -> RootParameter<'b> {
    Self::other(shader_register, register_space, visibility, D3D12_ROOT_PARAMETER_TYPE_SRV)
  }

  pub fn uav<'b>(shader_register: u32, register_space: u32, visibility: D3D12_SHADER_VISIBILITY) -> RootParameter<'b> {
    Self::other(shader_register, register_space, visibility, D3D12_ROOT_PARAMETER_TYPE_UAV)
  }
}

pub fn texture_copy_location_footprint(res: &D3D12Resource, footprint: &D3D12_PLACED_SUBRESOURCE_FOOTPRINT) -> D3D12_TEXTURE_COPY_LOCATION {
  unsafe {
    let mut ret = D3D12_TEXTURE_COPY_LOCATION {
      pResource: res.iptr() as *mut _,
      Type: D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
      u: ::std::mem::uninitialized(),
    };
    *ret.PlacedFootprint_mut() = footprint.clone();
    ret
  }
}

pub fn texture_copy_location_index(res: &D3D12Resource, subresource_index: u32) -> D3D12_TEXTURE_COPY_LOCATION {
  unsafe {
    let mut ret = D3D12_TEXTURE_COPY_LOCATION {
      pResource: res.iptr() as *mut _,
      Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
      u: ::std::mem::uninitialized(),
    };
    *ret.SubresourceIndex_mut() = subresource_index;
    ret
  }
}
<|endoftext|>