<commit_before>//! Validates the MIR to ensure that invariants are upheld.

use super::{MirPass, MirSource};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::{
    mir::{
        BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator,
        TerminatorKind,
    },
    ty::{self, ParamEnv, TyCtxt},
};
use rustc_span::def_id::DefId;

enum EdgeKind {
    Unwind,
    Other,
}

pub struct Validator {
    /// Describes at which point in the pipeline this validation is happening.
    pub when: String,
}

impl<'tcx> MirPass<'tcx> for Validator {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let def_id = source.def_id();
        let param_env = tcx.param_env(def_id);
        TypeChecker { when: &self.when, def_id, body, tcx, param_env }.visit_body(body);
    }
}

struct TypeChecker<'a, 'tcx> {
    when: &'a str,
    def_id: DefId,
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
    fn fail(&self, location: Location, msg: impl AsRef<str>) {
        let span = self.body.source_info(location).span;
        // We use `delay_span_bug` as we might see broken MIR when other errors have already
        // occurred.
        self.tcx.sess.diagnostic().delay_span_bug(
            span,
            &format!(
                "broken MIR in {:?} ({}) at {:?}:\n{}",
                self.def_id,
                self.when,
                location,
                msg.as_ref()
            ),
        );
    }

    fn check_bb(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
        if let Some(bb) = self.body.basic_blocks().get(bb) {
            let src = self.body.basic_blocks().get(location.block).unwrap();
            match (src.is_cleanup, bb.is_cleanup, edge_kind) {
                // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
                (false, false, EdgeKind::Other)
                // Non-cleanup blocks can jump to cleanup blocks along unwind edges
                | (false, true, EdgeKind::Unwind)
                // Cleanup blocks can jump to cleanup blocks along unwind edges
                | (true, true, EdgeKind::Unwind) => {}
                // All other jumps are invalid
                _ => {
                    self.fail(
                        location,
                        format!("encountered jump that does not respect unwind invariants {:?}", bb)
                    )
                }
            }
        } else {
            self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
        // `Operand::Copy` is only supposed to be used with `Copy` types.
        if let Operand::Copy(place) = operand {
            let ty = place.ty(&self.body.local_decls, self.tcx).ty;
            let span = self.body.source_info(location).span;

            if !ty.is_copy_modulo_regions(self.tcx, self.param_env, span) {
                self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
            }
        }

        self.super_operand(operand, location);
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
        // The sides of an assignment must not alias. Currently this just checks whether the places
        // are identical.
        if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind {
            match rvalue {
                Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
                    if dest == src {
                        self.fail(
                            location,
                            "encountered `Assign` statement with overlapping memory",
                        );
                    }
                }
                _ => {}
            }
        }
    }

    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
        match &terminator.kind {
            TerminatorKind::Goto { target } => {
                self.check_bb(location, *target, EdgeKind::Other);
            }
            TerminatorKind::SwitchInt { targets, values, .. } => {
                if targets.len() != values.len() + 1 {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)",
                            values.len(),
                            targets.len(),
                        ),
                    );
                }
                for target in targets {
                    self.check_bb(location, *target, EdgeKind::Other);
                }
            }
            TerminatorKind::Drop { target, unwind, .. } => {
                self.check_bb(location, *target, EdgeKind::Other);
                if let Some(unwind) = unwind {
                    self.check_bb(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::DropAndReplace { target, unwind, .. } => {
                self.check_bb(location, *target, EdgeKind::Other);
                if let Some(unwind) = unwind {
                    self.check_bb(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Call { func, destination, cleanup, .. } => {
                let func_ty = func.ty(&self.body.local_decls, self.tcx);
                match func_ty.kind {
                    ty::FnPtr(..) | ty::FnDef(..) => {}
                    _ => self.fail(
                        location,
                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
                    ),
                }
                if let Some((_, target)) = destination {
                    self.check_bb(location, *target, EdgeKind::Other);
                }
                if let Some(cleanup) = cleanup {
                    self.check_bb(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Assert { cond, target, cleanup, .. } => {
                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                if cond_ty != self.tcx.types.bool {
                    self.fail(
                        location,
                        format!(
                            "encountered non-boolean condition of type {} in `Assert` terminator",
                            cond_ty
                        ),
                    );
                }
                self.check_bb(location, *target, EdgeKind::Other);
                if let Some(cleanup) = cleanup {
                    self.check_bb(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Yield { resume, drop, .. } => {
                self.check_bb(location, *resume, EdgeKind::Other);
                if let Some(drop) = drop {
                    self.check_bb(location, *drop, EdgeKind::Other);
                }
            }
            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
                self.check_bb(location, *real_target, EdgeKind::Other);
                self.check_bb(location, *imaginary_target, EdgeKind::Other);
            }
            TerminatorKind::FalseUnwind { real_target, unwind } => {
                self.check_bb(location, *real_target, EdgeKind::Other);
                if let Some(unwind) = unwind {
                    self.check_bb(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::InlineAsm { destination, .. } => {
                if let Some(destination) = destination {
                    self.check_bb(location, *destination, EdgeKind::Other);
                }
            }
            // Nothing to validate for these.
            TerminatorKind::Resume
            | TerminatorKind::Abort
            | TerminatorKind::Return
            | TerminatorKind::Unreachable
            | TerminatorKind::GeneratorDrop => {}
        }
    }
}
<commit_msg>Relax cleanup to cleanup check<commit_after>//! Validates the MIR to ensure that invariants are upheld.

use super::{MirPass, MirSource};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::{
    mir::{
        BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator,
        TerminatorKind,
    },
    ty::{self, ParamEnv, TyCtxt},
};
use rustc_span::def_id::DefId;

enum EdgeKind {
    Unwind,
    Other,
}

pub struct Validator {
    /// Describes at which point in the pipeline this validation is happening.
    pub when: String,
}

impl<'tcx> MirPass<'tcx> for Validator {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let def_id = source.def_id();
        let param_env = tcx.param_env(def_id);
        TypeChecker { when: &self.when, def_id, body, tcx, param_env }.visit_body(body);
    }
}

struct TypeChecker<'a, 'tcx> {
    when: &'a str,
    def_id: DefId,
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
    fn fail(&self, location: Location, msg: impl AsRef<str>) {
        let span = self.body.source_info(location).span;
        // We use `delay_span_bug` as we might see broken MIR when other errors have already
        // occurred.
        self.tcx.sess.diagnostic().delay_span_bug(
            span,
            &format!(
                "broken MIR in {:?} ({}) at {:?}:\n{}",
                self.def_id,
                self.when,
                location,
                msg.as_ref()
            ),
        );
    }

    fn check_bb(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
        if let Some(bb) = self.body.basic_blocks().get(bb) {
            let src = self.body.basic_blocks().get(location.block).unwrap();
            match (src.is_cleanup, bb.is_cleanup, edge_kind) {
                // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
                (false, false, EdgeKind::Other)
                // Non-cleanup blocks can jump to cleanup blocks along unwind edges
                | (false, true, EdgeKind::Unwind)
                // Cleanup blocks can jump to cleanup blocks along any edges
                | (true, true, _) => {}
                // All other jumps are invalid
                _ => {
                    self.fail(
                        location,
                        format!("encountered jump that does not respect unwind invariants {:?}", bb)
                    )
                }
            }
        } else {
            self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
        // `Operand::Copy` is only supposed to be used with `Copy` types.
        if let Operand::Copy(place) = operand {
            let ty = place.ty(&self.body.local_decls, self.tcx).ty;
            let span = self.body.source_info(location).span;

            if !ty.is_copy_modulo_regions(self.tcx, self.param_env, span) {
                self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
            }
        }

        self.super_operand(operand, location);
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
        // The sides of an assignment must not alias. Currently this just checks whether the places
        // are identical.
        if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind {
            match rvalue {
                Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
                    if dest == src {
                        self.fail(
                            location,
                            "encountered `Assign` statement with overlapping memory",
                        );
                    }
                }
                _ => {}
            }
        }
    }

    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
        match &terminator.kind {
            TerminatorKind::Goto { target } => {
                self.check_bb(location, *target, EdgeKind::Other);
            }
            TerminatorKind::SwitchInt { targets, values, .. } => {
                if targets.len() != values.len() + 1 {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)",
                            values.len(),
                            targets.len(),
                        ),
                    );
                }
                for target in targets {
                    self.check_bb(location, *target, EdgeKind::Other);
                }
            }
            TerminatorKind::Drop { target, unwind, .. } => {
                self.check_bb(location, *target, EdgeKind::Other);
                if let Some(unwind) = unwind {
                    self.check_bb(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::DropAndReplace { target, unwind, .. } => {
                self.check_bb(location, *target, EdgeKind::Other);
                if let Some(unwind) = unwind {
                    self.check_bb(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Call { func, destination, cleanup, .. } => {
                let func_ty = func.ty(&self.body.local_decls, self.tcx);
                match func_ty.kind {
                    ty::FnPtr(..) | ty::FnDef(..) => {}
                    _ => self.fail(
                        location,
                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
                    ),
                }
                if let Some((_, target)) = destination {
                    self.check_bb(location, *target, EdgeKind::Other);
                }
                if let Some(cleanup) = cleanup {
                    self.check_bb(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Assert { cond, target, cleanup, .. } => {
                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                if cond_ty != self.tcx.types.bool {
                    self.fail(
                        location,
                        format!(
                            "encountered non-boolean condition of type {} in `Assert` terminator",
                            cond_ty
                        ),
                    );
                }
                self.check_bb(location, *target, EdgeKind::Other);
                if let Some(cleanup) = cleanup {
                    self.check_bb(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Yield { resume, drop, .. } => {
                self.check_bb(location, *resume, EdgeKind::Other);
                if let Some(drop) = drop {
                    self.check_bb(location, *drop, EdgeKind::Other);
                }
            }
            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
                self.check_bb(location, *real_target, EdgeKind::Other);
                self.check_bb(location, *imaginary_target, EdgeKind::Other);
            }
            TerminatorKind::FalseUnwind { real_target, unwind } => {
                self.check_bb(location, *real_target, EdgeKind::Other);
                if let Some(unwind) = unwind {
                    self.check_bb(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::InlineAsm { destination, .. } => {
                if let Some(destination) = destination {
                    self.check_bb(location, *destination, EdgeKind::Other);
                }
            }
            // Nothing to validate for these.
            TerminatorKind::Resume
            | TerminatorKind::Abort
            | TerminatorKind::Return
            | TerminatorKind::Unreachable
            | TerminatorKind::GeneratorDrop => {}
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::cmp;

use enums;
use parse;

pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
    // TODO: not implemented correctly

    // Finding all the descriptors.
    let mut descriptors = Vec::new();
    struct Descriptor {
        set: u32,
        binding: u32,
        desc_ty: String,
        array_count: u64,
        readonly: bool,
    }

    // Looping to find all the elements that have the `DescriptorSet` decoration.
    for instruction in doc.instructions.iter() {
        let (variable_id, descriptor_set) = match instruction {
            &parse::Instruction::Decorate {
                target_id,
                decoration: enums::Decoration::DecorationDescriptorSet,
                ref params,
            } => {
                (target_id, params[0])
            },
            _ => continue,
        };

        // Find which type is pointed to by this variable.
        let pointed_ty = pointer_variable_ty(doc, variable_id);
        // Name of the variable.
        let name = ::name_from_id(doc, variable_id);

        // Find the binding point of this descriptor.
        let binding = doc.instructions
            .iter()
            .filter_map(|i| {
                match i {
                    &parse::Instruction::Decorate {
                        target_id,
                        decoration: enums::Decoration::DecorationBinding,
                        ref params,
                    } if target_id == variable_id => {
                        Some(params[0])
                    },
                    _ => None,      // TODO: other types
                }
            })
            .next()
            .expect(&format!("Uniform `{}` is missing a binding", name));

        // Find informations about the kind of binding for this descriptor.
        let (desc_ty, readonly, array_count) = descriptor_infos(doc, pointed_ty, false)
            .expect(&format!("Couldn't find relevant type for uniform `{}` (type {}, maybe \
                              unimplemented)",
                             name,
                             pointed_ty));

        descriptors.push(Descriptor {
                             desc_ty: desc_ty,
                             set: descriptor_set,
                             binding: binding,
                             array_count: array_count,
                             readonly: readonly,
                         });
    }

    // Looping to find all the push constant structs.
    let mut push_constants_size = 0;
    for instruction in doc.instructions.iter() {
        let type_id = match instruction {
            &parse::Instruction::TypePointer {
                type_id,
                storage_class: enums::StorageClass::StorageClassPushConstant,
                ..
            } => {
                type_id
            },
            _ => continue,
        };

        let (_, size, _) = ::structs::type_from_id(doc, type_id);
        let size = size.expect("Found runtime-sized push constants");
        push_constants_size = cmp::max(push_constants_size, size);
    }

    // Writing the body of the `descriptor` method.
    let descriptor_body = descriptors
        .iter()
        .map(|d| {
            format!(
                "({set}, {binding}) => Some(DescriptorDesc {{
            ty: {desc_ty},
            array_count: {array_count},
            stages: self.0.clone(),
            readonly: {readonly},
        }}),",
                set = d.set,
                binding = d.binding,
                desc_ty = d.desc_ty,
                array_count = d.array_count,
                readonly = if d.readonly { "true" } else { "false" }
            )

        })
        .collect::<Vec<_>>()
        .concat();

    let num_sets = 1 + descriptors.iter().fold(0, |s, d| cmp::max(s, d.set));

    // Writing the body of the `num_bindings_in_set` method.
    let num_bindings_in_set_body = {
        (0 .. num_sets)
            .map(|set| {
                     let num = 1 +
                         descriptors
                             .iter()
                             .filter(|d| d.set == set)
                             .fold(0, |s, d| cmp::max(s, d.binding));
                     format!("{set} => Some({num}),", set = set, num = num)
                 })
            .collect::<Vec<_>>()
            .concat()
    };

    // Writing the body of the `num_push_constants_ranges` method.
    let num_push_constants_ranges_body = {
        if push_constants_size == 0 { "0" } else { "1" }
    };

    // Writing the body of the `push_constants_range` method.
    let push_constants_range_body = format!(
        r#"
        if num != 0 || {pc_size} == 0 {{ return None; }}
        Some(PipelineLayoutDescPcRange {{
            offset: 0,                      // FIXME: not necessarily true
            size: {pc_size},
            stages: ShaderStages::all(),     // FIXME: wrong
        }})
    "#,
        pc_size = push_constants_size
    );

    format!(
        r#"
        #[derive(Debug, Clone)]
        pub struct Layout(ShaderStages);

        #[allow(unsafe_code)]
        unsafe impl PipelineLayoutDesc for Layout {{
            fn num_sets(&self) -> usize {{
                {num_sets}
            }}

            fn num_bindings_in_set(&self, set: usize) -> Option<usize> {{
                match set {{
                    {num_bindings_in_set_body}
                    _ => None
                }}
            }}

            fn descriptor(&self, set: usize, binding: usize) -> Option<DescriptorDesc> {{
                match (set, binding) {{
                    {descriptor_body}
                    _ => None
                }}
            }}

            fn num_push_constants_ranges(&self) -> usize {{
                {num_push_constants_ranges_body}
            }}

            fn push_constants_range(&self, num: usize) -> Option<PipelineLayoutDescPcRange> {{
                {push_constants_range_body}
            }}
        }}
        "#,
        num_sets = num_sets,
        num_bindings_in_set_body = num_bindings_in_set_body,
        descriptor_body = descriptor_body,
        num_push_constants_ranges_body = num_push_constants_ranges_body,
        push_constants_range_body = push_constants_range_body
    )
}

/// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed
/// type.
fn pointer_variable_ty(doc: &parse::Spirv, variable: u32) -> u32 {
    let var_ty = doc.instructions
        .iter()
        .filter_map(|i| match i {
                        &parse::Instruction::Variable {
                            result_type_id,
                            result_id,
                            ..
                        } if result_id == variable => {
                            Some(result_type_id)
                        },
                        _ => None,
                    })
        .next()
        .unwrap();

    doc.instructions
        .iter()
        .filter_map(|i| match i {
                        &parse::Instruction::TypePointer { result_id, type_id, .. }
                            if result_id == var_ty => {
                            Some(type_id)
                        },
                        _ => None,
                    })
        .next()
        .unwrap()
}

/// Returns a `DescriptorDescTy` constructor, a bool indicating whether the descriptor is
/// read-only, and the number of array elements.
///
/// See also section 14.5.2 of the Vulkan specs: Descriptor Set Interface
fn descriptor_infos(doc: &parse::Spirv, pointed_ty: u32, force_combined_image_sampled: bool)
                    -> Option<(String, bool, u64)> {
    doc.instructions.iter().filter_map(|i| {
        match i {
            &parse::Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => {
                // Determine whether there's a Block or BufferBlock decoration.
                let is_ssbo = doc.instructions.iter().filter_map(|i| {
                    match i {
                        &parse::Instruction::Decorate
                            { target_id, decoration: enums::Decoration::DecorationBufferBlock, .. }
                            if target_id == pointed_ty =>
                        {
                            Some(true)
                        },
                        &parse::Instruction::Decorate
                            { target_id, decoration: enums::Decoration::DecorationBlock, .. }
                            if target_id == pointed_ty =>
                        {
                            Some(false)
                        },
                        _ => None,
                    }
                }).next().expect("Found a buffer uniform with neither the Block nor BufferBlock \
                                  decorations");

                // Determine whether there's a NonWritable decoration.
                //let non_writable = false;       // TODO: tricky because the decoration is on struct members

                let desc = format!("DescriptorDescTy::Buffer(DescriptorBufferDesc {{
                    dynamic: Some(false),
                    storage: {},
                }})", if is_ssbo { "true" } else { "false "});

                Some((desc, true, 1))
            },

            &parse::Instruction::TypeImage { result_id, ref dim, arrayed, ms, sampled,
                                             ref format, .. } if result_id == pointed_ty =>
            {
                let sampled = sampled.expect("Vulkan requires that variables of type OpTypeImage \
                                              have a Sampled operand of 1 or 2");

                let ms = if ms { "true" } else { "false" };
                let arrayed = if arrayed {
                    "DescriptorImageDescArray::Arrayed { max_layers: None }"
                } else {
                    "DescriptorImageDescArray::NonArrayed"
                };

                if let &enums::Dim::DimSubpassData = dim {
                    // We are an input attachment.
                    assert!(!force_combined_image_sampled, "An OpTypeSampledImage can't point to \
                                                            an OpTypeImage whose dimension is \
                                                            SubpassData");
                    assert!(if let &enums::ImageFormat::ImageFormatUnknown = format { true }
                            else { false }, "If Dim is SubpassData, Image Format must be Unknown");
                    assert!(!sampled, "If Dim is SubpassData, Sampled must be 2");

                    let desc = format!("DescriptorDescTy::InputAttachment {{
                                            multisampled: {},
                                            array_layers: {}
                                        }}", ms, arrayed);

                    Some((desc, true, 1))

                } else if let &enums::Dim::DimBuffer = dim {
                    // We are a texel buffer.
                    let desc = format!("DescriptorDescTy::TexelBuffer {{
                        storage: {},
                        format: None,       // TODO: specify format if known
                    }}", !sampled);

                    Some((desc, true, 1))

                } else {
                    // We are a sampled or storage image.
                    let sampled = if sampled { "true" } else { "false" };
                    let ty = if force_combined_image_sampled { "CombinedImageSampler" }
                             else { "Image" };
                    let dim = match *dim {
                        enums::Dim::Dim1D => "DescriptorImageDescDimensions::OneDimensional",
                        enums::Dim::Dim2D => "DescriptorImageDescDimensions::TwoDimensional",
                        enums::Dim::Dim3D => "DescriptorImageDescDimensions::ThreeDimensional",
                        enums::Dim::DimCube => "DescriptorImageDescDimensions::Cube",
                        enums::Dim::DimRect => panic!("Vulkan doesn't support rectangle textures"),
                        _ => unreachable!()
                    };

                    let desc = format!("DescriptorDescTy::{}(DescriptorImageDesc {{
                        sampled: {},
                        dimensions: {},
                        format: None,       // TODO: specify format if known
                        multisampled: {},
                        array_layers: {},
                    }})", ty, sampled, dim, ms, arrayed);

                    Some((desc, true, 1))
                }
            },

            &parse::Instruction::TypeSampledImage { result_id, image_type_id }
                                                                if result_id == pointed_ty =>
            {
                descriptor_infos(doc, image_type_id, true)
            },

            &parse::Instruction::TypeSampler { result_id } if result_id == pointed_ty => {
                let desc = format!("DescriptorDescTy::Sampler");
                Some((desc, true, 1))
            },

            &parse::Instruction::TypeArray { result_id, type_id, length_id } if result_id == pointed_ty => {
                let (desc, readonly, arr) = match descriptor_infos(doc, type_id, false) {
                    None => return None,
                    Some(v) => v,
                };
                assert_eq!(arr, 1);     // TODO: implement?
                let len = doc.instructions.iter().filter_map(|e| {
                    match e { &parse::Instruction::Constant { result_id, ref data, .. } if result_id == length_id => Some(data.clone()), _ => None }
                }).next().expect("failed to find array length");
                let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
                Some((desc, readonly, len))
            },

            _ => None,      // TODO: other types
        }
    }).next()
}
<commit_msg>Fix number of sets in vulkano-shaders (#764)<commit_after>// Copyright (c) 2016 The vulkano developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::cmp;

use enums;
use parse;

pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
    // TODO: not implemented correctly

    // Finding all the descriptors.
    let mut descriptors = Vec::new();
    struct Descriptor {
        set: u32,
        binding: u32,
        desc_ty: String,
        array_count: u64,
        readonly: bool,
    }

    // Looping to find all the elements that have the `DescriptorSet` decoration.
    for instruction in doc.instructions.iter() {
        let (variable_id, descriptor_set) = match instruction {
            &parse::Instruction::Decorate {
                target_id,
                decoration: enums::Decoration::DecorationDescriptorSet,
                ref params,
            } => {
                (target_id, params[0])
            },
            _ => continue,
        };

        // Find which type is pointed to by this variable.
        let pointed_ty = pointer_variable_ty(doc, variable_id);
        // Name of the variable.
        let name = ::name_from_id(doc, variable_id);

        // Find the binding point of this descriptor.
        let binding = doc.instructions
            .iter()
            .filter_map(|i| {
                match i {
                    &parse::Instruction::Decorate {
                        target_id,
                        decoration: enums::Decoration::DecorationBinding,
                        ref params,
                    } if target_id == variable_id => {
                        Some(params[0])
                    },
                    _ => None,      // TODO: other types
                }
            })
            .next()
            .expect(&format!("Uniform `{}` is missing a binding", name));

        // Find informations about the kind of binding for this descriptor.
        let (desc_ty, readonly, array_count) = descriptor_infos(doc, pointed_ty, false)
            .expect(&format!("Couldn't find relevant type for uniform `{}` (type {}, maybe \
                              unimplemented)",
                             name,
                             pointed_ty));

        descriptors.push(Descriptor {
                             desc_ty: desc_ty,
                             set: descriptor_set,
                             binding: binding,
                             array_count: array_count,
                             readonly: readonly,
                         });
    }

    // Looping to find all the push constant structs.
    let mut push_constants_size = 0;
    for instruction in doc.instructions.iter() {
        let type_id = match instruction {
            &parse::Instruction::TypePointer {
                type_id,
                storage_class: enums::StorageClass::StorageClassPushConstant,
                ..
            } => {
                type_id
            },
            _ => continue,
        };

        let (_, size, _) = ::structs::type_from_id(doc, type_id);
        let size = size.expect("Found runtime-sized push constants");
        push_constants_size = cmp::max(push_constants_size, size);
    }

    // Writing the body of the `descriptor` method.
    let descriptor_body = descriptors
        .iter()
        .map(|d| {
            format!(
                "({set}, {binding}) => Some(DescriptorDesc {{
            ty: {desc_ty},
            array_count: {array_count},
            stages: self.0.clone(),
            readonly: {readonly},
        }}),",
                set = d.set,
                binding = d.binding,
                desc_ty = d.desc_ty,
                array_count = d.array_count,
                readonly = if d.readonly { "true" } else { "false" }
            )

        })
        .collect::<Vec<_>>()
        .concat();

    let num_sets = descriptors.iter().fold(0, |s, d| cmp::max(s, d.set + 1));

    // Writing the body of the `num_bindings_in_set` method.
    let num_bindings_in_set_body = {
        (0 .. num_sets)
            .map(|set| {
                     let num = 1 +
                         descriptors
                             .iter()
                             .filter(|d| d.set == set)
                             .fold(0, |s, d| cmp::max(s, d.binding));
                     format!("{set} => Some({num}),", set = set, num = num)
                 })
            .collect::<Vec<_>>()
            .concat()
    };

    // Writing the body of the `num_push_constants_ranges` method.
    let num_push_constants_ranges_body = {
        if push_constants_size == 0 { "0" } else { "1" }
    };

    // Writing the body of the `push_constants_range` method.
    let push_constants_range_body = format!(
        r#"
        if num != 0 || {pc_size} == 0 {{ return None; }}
        Some(PipelineLayoutDescPcRange {{
            offset: 0,                      // FIXME: not necessarily true
            size: {pc_size},
            stages: ShaderStages::all(),     // FIXME: wrong
        }})
    "#,
        pc_size = push_constants_size
    );

    format!(
        r#"
        #[derive(Debug, Clone)]
        pub struct Layout(ShaderStages);

        #[allow(unsafe_code)]
        unsafe impl PipelineLayoutDesc for Layout {{
            fn num_sets(&self) -> usize {{
                {num_sets}
            }}

            fn num_bindings_in_set(&self, set: usize) -> Option<usize> {{
                match set {{
                    {num_bindings_in_set_body}
                    _ => None
                }}
            }}

            fn descriptor(&self, set: usize, binding: usize) -> Option<DescriptorDesc> {{
                match (set, binding) {{
                    {descriptor_body}
                    _ => None
                }}
            }}

            fn num_push_constants_ranges(&self) -> usize {{
                {num_push_constants_ranges_body}
            }}

            fn push_constants_range(&self, num: usize) -> Option<PipelineLayoutDescPcRange> {{
                {push_constants_range_body}
            }}
        }}
        "#,
        num_sets = num_sets,
        num_bindings_in_set_body = num_bindings_in_set_body,
        descriptor_body = descriptor_body,
        num_push_constants_ranges_body = num_push_constants_ranges_body,
        push_constants_range_body = push_constants_range_body
    )
}

/// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed
/// type.
fn pointer_variable_ty(doc: &parse::Spirv, variable: u32) -> u32 {
    let var_ty = doc.instructions
        .iter()
        .filter_map(|i| match i {
                        &parse::Instruction::Variable {
                            result_type_id,
                            result_id,
                            ..
                        } if result_id == variable => {
                            Some(result_type_id)
                        },
                        _ => None,
                    })
        .next()
        .unwrap();

    doc.instructions
        .iter()
        .filter_map(|i| match i {
                        &parse::Instruction::TypePointer { result_id, type_id, .. }
                            if result_id == var_ty => {
                            Some(type_id)
                        },
                        _ => None,
                    })
        .next()
        .unwrap()
}

/// Returns a `DescriptorDescTy` constructor, a bool indicating whether the descriptor is
/// read-only, and the number of array elements.
///
/// See also section 14.5.2 of the Vulkan specs: Descriptor Set Interface
fn descriptor_infos(doc: &parse::Spirv, pointed_ty: u32, force_combined_image_sampled: bool)
                    -> Option<(String, bool, u64)> {
    doc.instructions.iter().filter_map(|i| {
        match i {
            &parse::Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => {
                // Determine whether there's a Block or BufferBlock decoration.
                let is_ssbo = doc.instructions.iter().filter_map(|i| {
                    match i {
                        &parse::Instruction::Decorate
                            { target_id, decoration: enums::Decoration::DecorationBufferBlock, .. }
                            if target_id == pointed_ty =>
                        {
                            Some(true)
                        },
                        &parse::Instruction::Decorate
                            { target_id, decoration: enums::Decoration::DecorationBlock, .. }
                            if target_id == pointed_ty =>
                        {
                            Some(false)
                        },
                        _ => None,
                    }
                }).next().expect("Found a buffer uniform with neither the Block nor BufferBlock \
                                  decorations");

                // Determine whether there's a NonWritable decoration.
                //let non_writable = false;       // TODO: tricky because the decoration is on struct members

                let desc = format!("DescriptorDescTy::Buffer(DescriptorBufferDesc {{
                    dynamic: Some(false),
                    storage: {},
                }})", if is_ssbo { "true" } else { "false "});

                Some((desc, true, 1))
            },

            &parse::Instruction::TypeImage { result_id, ref dim, arrayed, ms, sampled,
                                             ref format, .. } if result_id == pointed_ty =>
            {
                let sampled = sampled.expect("Vulkan requires that variables of type OpTypeImage \
                                              have a Sampled operand of 1 or 2");

                let ms = if ms { "true" } else { "false" };
                let arrayed = if arrayed {
                    "DescriptorImageDescArray::Arrayed { max_layers: None }"
                } else {
                    "DescriptorImageDescArray::NonArrayed"
                };

                if let &enums::Dim::DimSubpassData = dim {
                    // We are an input attachment.
                    assert!(!force_combined_image_sampled, "An OpTypeSampledImage can't point to \
                                                            an OpTypeImage whose dimension is \
                                                            SubpassData");
                    assert!(if let &enums::ImageFormat::ImageFormatUnknown = format { true }
                            else { false }, "If Dim is SubpassData, Image Format must be Unknown");
                    assert!(!sampled, "If Dim is SubpassData, Sampled must be 2");

                    let desc = format!("DescriptorDescTy::InputAttachment {{
                                            multisampled: {},
                                            array_layers: {}
                                        }}", ms, arrayed);

                    Some((desc, true, 1))

                } else if let &enums::Dim::DimBuffer = dim {
                    // We are a texel buffer.
                    let desc = format!("DescriptorDescTy::TexelBuffer {{
                        storage: {},
                        format: None,       // TODO: specify format if known
                    }}", !sampled);

                    Some((desc, true, 1))

                } else {
                    // We are a sampled or storage image.
                    let sampled = if sampled { "true" } else { "false" };
                    let ty = if force_combined_image_sampled { "CombinedImageSampler" }
                             else { "Image" };
                    let dim = match *dim {
                        enums::Dim::Dim1D => "DescriptorImageDescDimensions::OneDimensional",
                        enums::Dim::Dim2D => "DescriptorImageDescDimensions::TwoDimensional",
                        enums::Dim::Dim3D => "DescriptorImageDescDimensions::ThreeDimensional",
                        enums::Dim::DimCube => "DescriptorImageDescDimensions::Cube",
                        enums::Dim::DimRect => panic!("Vulkan doesn't support rectangle textures"),
                        _ => unreachable!()
                    };

                    let desc = format!("DescriptorDescTy::{}(DescriptorImageDesc {{
                        sampled: {},
                        dimensions: {},
                        format: None,       // TODO: specify format if known
                        multisampled: {},
                        array_layers: {},
                    }})", ty, sampled, dim, ms, arrayed);

                    Some((desc, true, 1))
                }
            },

            &parse::Instruction::TypeSampledImage { result_id, image_type_id }
                                                                if result_id == pointed_ty =>
            {
                descriptor_infos(doc, image_type_id, true)
            },

            &parse::Instruction::TypeSampler { result_id } if result_id == pointed_ty => {
                let desc = format!("DescriptorDescTy::Sampler");
                Some((desc, true, 1))
            },

            &parse::Instruction::TypeArray { result_id, type_id, length_id } if result_id == pointed_ty => {
                let (desc, readonly, arr) = match descriptor_infos(doc, type_id, false) {
                    None => return None,
                    Some(v) => v,
                };
                assert_eq!(arr, 1);     // TODO: implement?
                let len = doc.instructions.iter().filter_map(|e| {
                    match e { &parse::Instruction::Constant { result_id, ref data, .. } if result_id == length_id => Some(data.clone()), _ => None }
                }).next().expect("failed to find array length");
                let len = len.iter().rev().fold(0u64, |a, &b| (a << 32) | b as u64);
                Some((desc, readonly, len))
            },

            _ => None,      // TODO: other types
        }
    }).next()
}
<|endoftext|>


<commit_before>use glium::backend::Facade;
use glium::{self, BackfaceCullingMode, DepthTest, DrawParameters, IndexBuffer, VertexBuffer};
use glium::index::PrimitiveType;
use Camera;
use util::ToArr;
use base::math::*;
use base::gen::seeded_rng;
use base::prop::Plant;
use std::rc::Rc;
use super::PlantRenderer;
use base::prop::plant::ControlPoint;

/// Graphical representation of a 'base::Plant'
pub struct PlantView {
    branches: Vec<VertexBuffer<Vertex>>,
    indices: IndexBuffer<u32>,
    renderer: Rc<PlantRenderer>,
    pos: Point3f,
}

impl PlantView {
    pub fn from_plant<F: Facade>(pos: Point3f,
                                 plant: &Plant,
                                 renderer: Rc<PlantRenderer>,
                                 facade: &F)
                                 -> Self {
        // FIXME handle other plant types
        // let verts = 0;
        let mut indices = Vec::new();
        let mut vertices = Vec::new();
        let branches = match *plant {
            Plant::Tree { ref branches } => {
                branches.iter()
                    .map(|branch| {

                        for i in 1..branch.points.len() {
                            get_vertices_for_branch(&branch.points[i - 1],
                                                    &branch.points[i],
                                                    &mut vertices,
                                                    &mut indices,
                                                    branch.color)
                        }
                        VertexBuffer::new(facade, &vertices).unwrap()
                    })
                    .collect()
            }
        };

        PlantView {
            branches: branches,
            indices: IndexBuffer::new(facade, PrimitiveType::TrianglesList, &indices).unwrap(),
            renderer: renderer,
            pos: pos,
        }
    }

    pub fn draw<S: glium::Surface>(&self, surface: &mut S, camera: &Camera) {
        let uniforms = uniform! {
            offset: self.pos.to_arr(),
            proj_matrix: camera.proj_matrix().to_arr(),
            view_matrix: camera.view_matrix().to_arr(),
        };

        let params = DrawParameters {
            depth: glium::Depth {
                write: true,
                test: DepthTest::IfLess,
                ..Default::default()
            },
            backface_culling: BackfaceCullingMode::CullCounterClockwise,
            ..Default::default()
        };

        for vbuf in &self.branches {
            surface.draw(vbuf,
                      &self.indices,
                      &self.renderer.program(),
                      &uniforms,
                      &params)
                .unwrap();
        }
    }
}

/// Vertex type used to render plants/trees.
#[derive(Debug, Copy, Clone)]
pub struct Vertex {
    pub position: [f32; 3],
    pub color: [f32; 3],
    pub normal: [f32; 3],
}

implement_vertex!(Vertex, position, color, normal);

/// generates Vertexbuffer and indexbuffer for a branch
fn get_vertices_for_branch(start: &ControlPoint,
                           end: &ControlPoint,
                           vertices: &mut Vec<Vertex>,
                           indices: &mut Vec<u32>,
                           color: Vector3f) {

    let to_end: Vector3f = Vector3f::new(end.point.x - start.point.x,
                                         end.point.y - start.point.y,
                                         end.point.z - start.point.z);
    let ortho = get_points_from_vector(to_end);

    let mut cur_len = vertices.len() as u32;

    // Bottom
    for vec in ortho.iter() {
        vertices.push(Vertex {
            position: [start.point.x + vec.x * start.diameter,
                       start.point.y + vec.y * start.diameter,
                       start.point.z + vec.z * start.diameter],
            normal: (-to_end).to_arr(),
            color: color.to_arr(),
        });
    }
    indices.append(&mut vec![cur_len + 2, cur_len + 0, cur_len + 1]);

    // Top
    cur_len = vertices.len() as u32;

    for vec in ortho.iter() {
        vertices.push(Vertex {
            position: [end.point.x + vec.x * end.diameter,
                       end.point.y + vec.y * end.diameter,
                       end.point.z + vec.z * end.diameter],
            normal: to_end.to_arr(),
            color: color.to_arr(),
        });
    }
    indices.append(&mut vec![cur_len + 1, cur_len + 0, cur_len + 2]);

    side(vertices, indices, color, start, end, ortho[0], ortho[1]);
    side(vertices, indices, color, start, end, ortho[1], ortho[2]);
    side(vertices, indices, color, start, end, ortho[2], ortho[0]);
}

/// Creates Vertexbuffer and IndexBuffer for a Side of the plants
fn side(vertices: &mut Vec<Vertex>,
        indices: &mut Vec<u32>,
        color: Vector3f,
        start: &ControlPoint,
        end: &ControlPoint,
        first_normal: Vector3f,
        second_normal: Vector3f) {
    let cur_len = vertices.len() as u32;

    vertices.push(Vertex {
        position: [end.point.x + first_normal.x * end.diameter,
                   end.point.y + first_normal.y * end.diameter,
                   end.point.z + first_normal.z * end.diameter],
        normal: (first_normal + second_normal).to_arr(),
        color: color.to_arr(),
    });

    vertices.push(Vertex {
        position: [end.point.x + second_normal.x * end.diameter,
                   end.point.y + second_normal.y * end.diameter,
                   end.point.z + second_normal.z * end.diameter],
        normal: (first_normal + second_normal).to_arr(),
        color: color.to_arr(),
    });

    vertices.push(Vertex {
        position: [start.point.x + first_normal.x * start.diameter,
                   start.point.y + first_normal.y * start.diameter,
                   start.point.z + first_normal.z * start.diameter],
        normal: (first_normal + second_normal).to_arr(),
        color: color.to_arr(),
    });

    vertices.push(Vertex {
        position: [start.point.x + second_normal.x * start.diameter,
                   start.point.y + second_normal.y * start.diameter,
                   start.point.z + second_normal.z * start.diameter],
        normal: (first_normal + second_normal).to_arr(),
        color: color.to_arr(),
    });

    indices.append(&mut vec![cur_len + 2,
                             cur_len + 0,
                             cur_len + 1,
                             cur_len + 2,
                             cur_len + 1,
                             cur_len + 3]);
}

/// generates 3 normalized vectors  perpendicular to the given vector
fn get_points_from_vector(vector: Vector3f) -> [Vector3f; 3] {
    let ortho = random_vec_with_angle(&mut seeded_rng(0x2651aa465abded, (), ()), vector, 90.0);
    let rot = Basis3::from_axis_angle(vector, Deg::new(120.0).into());
    let v0 = rot.rotate_vector(ortho);
    let v1 = rot.rotate_vector(v0);

    [ortho.normalize(), v0.normalize(), v1.normalize()]
}
<commit_msg>comment removed<commit_after>use glium::backend::Facade;
use glium::{self, BackfaceCullingMode, DepthTest, DrawParameters, IndexBuffer, VertexBuffer};
use glium::index::PrimitiveType;
use Camera;
use util::ToArr;
use base::math::*;
use base::gen::seeded_rng;
use base::prop::Plant;
use std::rc::Rc;
use super::PlantRenderer;
use base::prop::plant::ControlPoint;

/// Graphical representation of a 'base::Plant'
pub struct PlantView {
    branches: Vec<VertexBuffer<Vertex>>,
    indices: IndexBuffer<u32>,
    renderer: Rc<PlantRenderer>,
    pos: Point3f,
}

impl PlantView {
    pub fn from_plant<F: Facade>(pos: Point3f,
                                 plant: &Plant,
                                 renderer: Rc<PlantRenderer>,
                                 facade: &F)
                                 -> Self {
        // FIXME handle other plant types
        let mut indices = Vec::new();
        let mut vertices = Vec::new();
        let branches = match *plant {
            Plant::Tree { ref branches } => {
                branches.iter()
                    .map(|branch| {

                        for i in 1..branch.points.len() {
                            get_vertices_for_branch(&branch.points[i - 1],
                                                    &branch.points[i],
                                                    &mut vertices,
                                                    &mut indices,
                                                    branch.color)
                        }
                        VertexBuffer::new(facade, &vertices).unwrap()
                    })
                    .collect()
            }
        };

        PlantView {
            branches: branches,
            indices: IndexBuffer::new(facade, PrimitiveType::TrianglesList, &indices).unwrap(),
            renderer: renderer,
            pos: pos,
        }
    }

    pub fn draw<S: glium::Surface>(&self, surface: &mut S, camera: &Camera) {
        let uniforms = uniform! {
            offset: self.pos.to_arr(),
            proj_matrix: camera.proj_matrix().to_arr(),
            view_matrix: camera.view_matrix().to_arr(),
        };

        let params = DrawParameters {
            depth: glium::Depth {
                write: true,
                test: DepthTest::IfLess,
                ..Default::default()
            },
            backface_culling: BackfaceCullingMode::CullCounterClockwise,
            ..Default::default()
        };

        for vbuf in &self.branches {
            surface.draw(vbuf,
                      &self.indices,
                      &self.renderer.program(),
                      &uniforms,
                      &params)
                .unwrap();
        }
    }
}

/// Vertex type used to render plants/trees.
#[derive(Debug, Copy, Clone)]
pub struct Vertex {
    pub position: [f32; 3],
    pub color: [f32; 3],
    pub normal: [f32; 3],
}

implement_vertex!(Vertex, position, color, normal);

/// generates Vertexbuffer and indexbuffer for a branch
fn get_vertices_for_branch(start: &ControlPoint,
                           end: &ControlPoint,
                           vertices: &mut Vec<Vertex>,
                           indices: &mut Vec<u32>,
                           color: Vector3f) {

    let to_end: Vector3f = Vector3f::new(end.point.x - start.point.x,
                                         end.point.y - start.point.y,
                                         end.point.z - start.point.z);
    let ortho = get_points_from_vector(to_end);

    let mut cur_len = vertices.len() as u32;

    // Bottom
    for vec in ortho.iter() {
        vertices.push(Vertex {
            position: [start.point.x + vec.x * start.diameter,
                       start.point.y + vec.y * start.diameter,
                       start.point.z + vec.z * start.diameter],
            normal: (-to_end).to_arr(),
            color: color.to_arr(),
        });
    }
    indices.append(&mut vec![cur_len + 2, cur_len + 0, cur_len + 1]);

    // Top
    cur_len = vertices.len() as u32;

    for vec in ortho.iter() {
        vertices.push(Vertex {
            position: [end.point.x + vec.x * end.diameter,
                       end.point.y + vec.y * end.diameter,
                       end.point.z + vec.z * end.diameter],
            normal: to_end.to_arr(),
            color: color.to_arr(),
        });
    }
    indices.append(&mut vec![cur_len + 1, cur_len + 0, cur_len + 2]);

    side(vertices, indices, color, start, end, ortho[0], ortho[1]);
    side(vertices, indices, color, start, end, ortho[1], ortho[2]);
    side(vertices, indices, color, start, end, ortho[2], ortho[0]);
}

/// Creates Vertexbuffer and IndexBuffer for a Side of the plants
fn side(vertices: &mut Vec<Vertex>,
        indices: &mut Vec<u32>,
        color: Vector3f,
        start: &ControlPoint,
        end: &ControlPoint,
        first_normal: Vector3f,
        second_normal: Vector3f) {
    let cur_len = vertices.len() as u32;

    vertices.push(Vertex {
        position: [end.point.x + first_normal.x * end.diameter,
                   end.point.y + first_normal.y * end.diameter,
                   end.point.z + first_normal.z * end.diameter],
        normal: (first_normal + second_normal).to_arr(),
        color: color.to_arr(),
    });

    vertices.push(Vertex {
        position: [end.point.x + second_normal.x * end.diameter,
                   end.point.y + second_normal.y * end.diameter,
                   end.point.z + second_normal.z * end.diameter],
        normal: (first_normal + second_normal).to_arr(),
        color: color.to_arr(),
    });

    vertices.push(Vertex {
        position: [start.point.x + first_normal.x * start.diameter,
                   start.point.y + first_normal.y * start.diameter,
                   start.point.z + first_normal.z * start.diameter],
        normal: (first_normal + second_normal).to_arr(),
        color: color.to_arr(),
    });

    vertices.push(Vertex {
        position: [start.point.x + second_normal.x * start.diameter,
                   start.point.y + second_normal.y * start.diameter,
                   start.point.z + second_normal.z * start.diameter],
        normal: (first_normal + second_normal).to_arr(),
        color: color.to_arr(),
    });

    indices.append(&mut vec![cur_len + 2,
                             cur_len + 0,
                             cur_len + 1,
                             cur_len + 2,
                             cur_len + 1,
                             cur_len + 3]);
}

/// generates 3 normalized vectors  perpendicular to the given vector
fn get_points_from_vector(vector: Vector3f) -> [Vector3f; 3] {
    let ortho = random_vec_with_angle(&mut seeded_rng(0x2651aa465abded, (), ()), vector, 90.0);
    let rot = Basis3::from_axis_angle(vector, Deg::new(120.0).into());
    let v0 = rot.rotate_vector(ortho);
    let v1 = rot.rotate_vector(v0);

    [ortho.normalize(), v0.normalize(), v1.normalize()]
}
<|endoftext|>


<commit_before>use reexport::*;
use rustc::lint::*;
use rustc::hir::def::Def;
use rustc::hir::*;
use rustc::hir::intravisit::{Visitor, walk_ty, walk_ty_param_bound, walk_fn_decl, walk_generics, NestedVisitorMap};
use std::collections::{HashSet, HashMap};
use syntax::codemap::Span;
use utils::{in_external_macro, span_lint, last_path_segment};

/// **What it does:** Checks for lifetime annotations which can be removed by
/// relying on lifetime elision.
///
/// **Why is this bad?** The additional lifetimes make the code look more
/// complicated, while there is nothing out of the ordinary going on. Removing
/// them leads to more readable code.
///
/// **Known problems:** Potential false negatives: we bail out if the function
/// has a `where` clause where lifetimes are mentioned.
///
/// **Example:**
/// ```rust
/// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 { x }
/// ```
declare_lint! {
    pub NEEDLESS_LIFETIMES,
    Warn,
    "using explicit lifetimes for references in function arguments when elision rules \
     would allow omitting them"
}

/// **What it does:** Checks for lifetimes in generics that are never used
/// anywhere else.
///
/// **Why is this bad?** The additional lifetimes make the code look more
/// complicated, while there is nothing out of the ordinary going on. Removing
/// them leads to more readable code.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn unused_lifetime<'a>(x: u8) { .. }
/// ```
declare_lint! {
    pub UNUSED_LIFETIMES,
    Warn,
    "unused lifetimes in function definitions"
}

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

impl LintPass for LifetimePass {
    fn get_lints(&self) -> LintArray {
        lint_array!(NEEDLESS_LIFETIMES, UNUSED_LIFETIMES)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LifetimePass {
    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
        if let ItemFn(ref decl, _, _, _, ref generics, _) = item.node {
            check_fn_inner(cx, decl, generics, item.span);
        }
    }

    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem) {
        if let ImplItemKind::Method(ref sig, _) = item.node {
            check_fn_inner(cx, &sig.decl, &sig.generics, item.span);
        }
    }

    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem) {
        if let MethodTraitItem(ref sig, _) = item.node {
            check_fn_inner(cx, &sig.decl, &sig.generics, item.span);
        }
    }
}

/// The lifetime of a &-reference.
#[derive(PartialEq, Eq, Hash, Debug)]
enum RefLt {
    Unnamed,
    Static,
    Named(Name),
}

fn bound_lifetimes(bound: &TyParamBound) -> HirVec<&Lifetime> {
    if let TraitTyParamBound(ref trait_ref, _) = *bound {
        trait_ref.trait_ref
            .path
            .segments
            .last()
            .expect("a path must have at least one segment")
            .parameters
            .lifetimes()
    } else {
        HirVec::new()
    }
}

fn check_fn_inner<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl, generics: &'tcx Generics, span: Span) {
    if in_external_macro(cx, span) || has_where_lifetimes(cx, &generics.where_clause) {
        return;
    }

    let bounds_lts = generics.ty_params
        .iter()
        .flat_map(|typ| typ.bounds.iter().flat_map(bound_lifetimes));

    if could_use_elision(cx, decl, &generics.lifetimes, bounds_lts) {
        span_lint(cx,
                  NEEDLESS_LIFETIMES,
                  span,
                  "explicit lifetimes given in parameter types where they could be elided");
    }
    report_extra_lifetimes(cx, decl, generics);
}

fn could_use_elision<'a, 'tcx: 'a, T: Iterator<Item = &'tcx Lifetime>>(
    cx: &LateContext<'a, 'tcx>,
    func: &'tcx FnDecl,
    named_lts: &'tcx [LifetimeDef],
    bounds_lts: T
) -> bool {
    // There are two scenarios where elision works:
    // * no output references, all input references have different LT
    // * output references, exactly one input reference with same LT
    // All lifetimes must be unnamed, 'static or defined without bounds on the
    // level of the current item.

    // check named LTs
    let allowed_lts = allowed_lts_from(named_lts);

    // these will collect all the lifetimes for references in arg/return types
    let mut input_visitor = RefVisitor::new(cx);
    let mut output_visitor = RefVisitor::new(cx);

    // extract lifetimes in input argument types
    for arg in &func.inputs {
        input_visitor.visit_ty(&arg.ty);
    }
    // extract lifetimes in output type
    if let Return(ref ty) = func.output {
        output_visitor.visit_ty(ty);
    }

    let input_lts = lts_from_bounds(input_visitor.into_vec(), bounds_lts);
    let output_lts = output_visitor.into_vec();

    // check for lifetimes from higher scopes
    for lt in input_lts.iter().chain(output_lts.iter()) {
        if !allowed_lts.contains(lt) {
            return false;
        }
    }

    // no input lifetimes? easy case!
    if input_lts.is_empty() {
        false
    } else if output_lts.is_empty() {
        // no output lifetimes, check distinctness of input lifetimes

        // only unnamed and static, ok
        if input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static) {
            return false;
        }
        // we have no output reference, so we only need all distinct lifetimes
        input_lts.len() == unique_lifetimes(&input_lts)
    } else {
        // we have output references, so we need one input reference,
        // and all output lifetimes must be the same
        if unique_lifetimes(&output_lts) > 1 {
            return false;
        }
        if input_lts.len() == 1 {
            match (&input_lts[0], &output_lts[0]) {
                (&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true,
                (&RefLt::Named(_), &RefLt::Unnamed) => true,
                _ => false, // already elided, different named lifetimes
                // or something static going on
            }
        } else {
            false
        }
    }
}

fn allowed_lts_from(named_lts: &[LifetimeDef]) -> HashSet<RefLt> {
    let mut allowed_lts = HashSet::new();
    for lt in named_lts {
        if lt.bounds.is_empty() {
            allowed_lts.insert(RefLt::Named(lt.lifetime.name));
        }
    }
    allowed_lts.insert(RefLt::Unnamed);
    allowed_lts.insert(RefLt::Static);
    allowed_lts
}

fn lts_from_bounds<'a, T: Iterator<Item = &'a Lifetime>>(mut vec: Vec<RefLt>, bounds_lts: T) -> Vec<RefLt> {
    for lt in bounds_lts {
        if &*lt.name.as_str() != "'static" {
            vec.push(RefLt::Named(lt.name));
        }
    }

    vec
}

/// Number of unique lifetimes in the given vector.
fn unique_lifetimes(lts: &[RefLt]) -> usize {
    lts.iter().collect::<HashSet<_>>().len()
}

/// A visitor usable for `rustc_front::visit::walk_ty()`.
struct RefVisitor<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
    lts: Vec<RefLt>,
}

impl<'v, 't> RefVisitor<'v, 't> {
    fn new(cx: &'v LateContext<'v, 't>) -> RefVisitor<'v, 't> {
        RefVisitor {
            cx: cx,
            lts: Vec::new(),
        }
    }

    fn record(&mut self, lifetime: &Option<Lifetime>) {
        if let Some(ref lt) = *lifetime {
            if &*lt.name.as_str() == "'static" {
                self.lts.push(RefLt::Static);
            } else {
                self.lts.push(RefLt::Named(lt.name));
            }
        } else {
            self.lts.push(RefLt::Unnamed);
        }
    }

    fn into_vec(self) -> Vec<RefLt> {
        self.lts
    }

    fn collect_anonymous_lifetimes(&mut self, qpath: &QPath, ty: &Ty) {
        let last_path_segment = &last_path_segment(qpath).parameters;
        if let AngleBracketedParameters(ref params) = *last_path_segment {
            if params.lifetimes.is_empty() {
                match self.cx.tcx.tables().qpath_def(qpath, ty.id) {
                    Def::TyAlias(def_id) |
                    Def::Struct(def_id) => {
                        let generics = self.cx.tcx.item_generics(def_id);
                        for _ in generics.regions.as_slice() {
                            self.record(&None);
                        }
                    },
                    Def::Trait(def_id) => {
                        let trait_def = self.cx.tcx.trait_defs.borrow()[&def_id];
                        for _ in &self.cx.tcx.item_generics(trait_def.def_id).regions {
                            self.record(&None);
                        }
                    },
                    _ => (),
                }
            }
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
    // for lifetimes as parameters of generics
    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
        self.record(&Some(*lifetime));
    }

    fn visit_ty(&mut self, ty: &'tcx Ty) {
        match ty.node {
            TyRptr(None, _) => {
                self.record(&None);
            },
            TyPath(ref path) => {
                self.collect_anonymous_lifetimes(path, ty);
            },
            TyImplTrait(ref param_bounds) => {
                for bound in param_bounds {
                    if let RegionTyParamBound(_) = *bound {
                        self.record(&None);
                    }
                }
            }
            _ => (),
        }
        walk_ty(self, ty);
    }
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::None
    }
}

/// Are any lifetimes mentioned in the `where` clause? If yes, we don't try to
/// reason about elision.
fn has_where_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, where_clause: &'tcx WhereClause) -> bool {
    for predicate in &where_clause.predicates {
        match *predicate {
            WherePredicate::RegionPredicate(..) => return true,
            WherePredicate::BoundPredicate(ref pred) => {
                // a predicate like F: Trait or F: for<'a> Trait<'a>
                let mut visitor = RefVisitor::new(cx);
                // walk the type F, it may not contain LT refs
                walk_ty(&mut visitor, &pred.bounded_ty);
                if !visitor.lts.is_empty() {
                    return true;
                }
                // if the bounds define new lifetimes, they are fine to occur
                let allowed_lts = allowed_lts_from(&pred.bound_lifetimes);
                // now walk the bounds
                for bound in pred.bounds.iter() {
                    walk_ty_param_bound(&mut visitor, bound);
                }
                // and check that all lifetimes are allowed
                for lt in visitor.into_vec() {
                    if !allowed_lts.contains(&lt) {
                        return true;
                    }
                }
            },
            WherePredicate::EqPredicate(ref pred) => {
                let mut visitor = RefVisitor::new(cx);
                walk_ty(&mut visitor, &pred.ty);
                if !visitor.lts.is_empty() {
                    return true;
                }
            },
        }
    }
    false
}

struct LifetimeChecker {
    map: HashMap<Name, Span>,
}

impl<'tcx> Visitor<'tcx> for LifetimeChecker {
    // for lifetimes as parameters of generics
    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
        self.map.remove(&lifetime.name);
    }

    fn visit_lifetime_def(&mut self, _: &'tcx LifetimeDef) {
        // don't actually visit `<'a>` or `<'a: 'b>`
        // we've already visited the `'a` declarations and
        // don't want to spuriously remove them
        // `'b` in `'a: 'b` is useless unless used elsewhere in
        // a non-lifetime bound
    }
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::None
    }
}

fn report_extra_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, func: &'tcx FnDecl, generics: &'tcx Generics) {
    let hs = generics.lifetimes
        .iter()
        .map(|lt| (lt.lifetime.name, lt.lifetime.span))
        .collect();
    let mut checker = LifetimeChecker { map: hs };

    walk_generics(&mut checker, generics);
    walk_fn_decl(&mut checker, func);

    for &v in checker.map.values() {
        span_lint(cx, UNUSED_LIFETIMES, v, "this lifetime isn't used in the function definition");
    }
}
<commit_msg>fix rustfmt<commit_after>use reexport::*;
use rustc::lint::*;
use rustc::hir::def::Def;
use rustc::hir::*;
use rustc::hir::intravisit::{Visitor, walk_ty, walk_ty_param_bound, walk_fn_decl, walk_generics, NestedVisitorMap};
use std::collections::{HashSet, HashMap};
use syntax::codemap::Span;
use utils::{in_external_macro, span_lint, last_path_segment};

/// **What it does:** Checks for lifetime annotations which can be removed by
/// relying on lifetime elision.
///
/// **Why is this bad?** The additional lifetimes make the code look more
/// complicated, while there is nothing out of the ordinary going on. Removing
/// them leads to more readable code.
///
/// **Known problems:** Potential false negatives: we bail out if the function
/// has a `where` clause where lifetimes are mentioned.
///
/// **Example:**
/// ```rust
/// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 { x }
/// ```
declare_lint! {
    pub NEEDLESS_LIFETIMES,
    Warn,
    "using explicit lifetimes for references in function arguments when elision rules \
     would allow omitting them"
}

/// **What it does:** Checks for lifetimes in generics that are never used
/// anywhere else.
///
/// **Why is this bad?** The additional lifetimes make the code look more
/// complicated, while there is nothing out of the ordinary going on. Removing
/// them leads to more readable code.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn unused_lifetime<'a>(x: u8) { .. }
/// ```
declare_lint! {
    pub UNUSED_LIFETIMES,
    Warn,
    "unused lifetimes in function definitions"
}

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

impl LintPass for LifetimePass {
    fn get_lints(&self) -> LintArray {
        lint_array!(NEEDLESS_LIFETIMES, UNUSED_LIFETIMES)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LifetimePass {
    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
        if let ItemFn(ref decl, _, _, _, ref generics, _) = item.node {
            check_fn_inner(cx, decl, generics, item.span);
        }
    }

    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem) {
        if let ImplItemKind::Method(ref sig, _) = item.node {
            check_fn_inner(cx, &sig.decl, &sig.generics, item.span);
        }
    }

    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem) {
        if let MethodTraitItem(ref sig, _) = item.node {
            check_fn_inner(cx, &sig.decl, &sig.generics, item.span);
        }
    }
}

/// The lifetime of a &-reference.
#[derive(PartialEq, Eq, Hash, Debug)]
enum RefLt {
    Unnamed,
    Static,
    Named(Name),
}

fn bound_lifetimes(bound: &TyParamBound) -> HirVec<&Lifetime> {
    if let TraitTyParamBound(ref trait_ref, _) = *bound {
        trait_ref.trait_ref
            .path
            .segments
            .last()
            .expect("a path must have at least one segment")
            .parameters
            .lifetimes()
    } else {
        HirVec::new()
    }
}

fn check_fn_inner<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl, generics: &'tcx Generics, span: Span) {
    if in_external_macro(cx, span) || has_where_lifetimes(cx, &generics.where_clause) {
        return;
    }

    let bounds_lts = generics.ty_params
        .iter()
        .flat_map(|typ| typ.bounds.iter().flat_map(bound_lifetimes));

    if could_use_elision(cx, decl, &generics.lifetimes, bounds_lts) {
        span_lint(cx,
                  NEEDLESS_LIFETIMES,
                  span,
                  "explicit lifetimes given in parameter types where they could be elided");
    }
    report_extra_lifetimes(cx, decl, generics);
}

fn could_use_elision<'a, 'tcx: 'a, T: Iterator<Item = &'tcx Lifetime>>(
    cx: &LateContext<'a, 'tcx>,
    func: &'tcx FnDecl,
    named_lts: &'tcx [LifetimeDef],
    bounds_lts: T
) -> bool {
    // There are two scenarios where elision works:
    // * no output references, all input references have different LT
    // * output references, exactly one input reference with same LT
    // All lifetimes must be unnamed, 'static or defined without bounds on the
    // level of the current item.

    // check named LTs
    let allowed_lts = allowed_lts_from(named_lts);

    // these will collect all the lifetimes for references in arg/return types
    let mut input_visitor = RefVisitor::new(cx);
    let mut output_visitor = RefVisitor::new(cx);

    // extract lifetimes in input argument types
    for arg in &func.inputs {
        input_visitor.visit_ty(&arg.ty);
    }
    // extract lifetimes in output type
    if let Return(ref ty) = func.output {
        output_visitor.visit_ty(ty);
    }

    let input_lts = lts_from_bounds(input_visitor.into_vec(), bounds_lts);
    let output_lts = output_visitor.into_vec();

    // check for lifetimes from higher scopes
    for lt in input_lts.iter().chain(output_lts.iter()) {
        if !allowed_lts.contains(lt) {
            return false;
        }
    }

    // no input lifetimes? easy case!
    if input_lts.is_empty() {
        false
    } else if output_lts.is_empty() {
        // no output lifetimes, check distinctness of input lifetimes

        // only unnamed and static, ok
        if input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static) {
            return false;
        }
        // we have no output reference, so we only need all distinct lifetimes
        input_lts.len() == unique_lifetimes(&input_lts)
    } else {
        // we have output references, so we need one input reference,
        // and all output lifetimes must be the same
        if unique_lifetimes(&output_lts) > 1 {
            return false;
        }
        if input_lts.len() == 1 {
            match (&input_lts[0], &output_lts[0]) {
                (&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true,
                (&RefLt::Named(_), &RefLt::Unnamed) => true,
                _ => false, // already elided, different named lifetimes
                // or something static going on
            }
        } else {
            false
        }
    }
}

fn allowed_lts_from(named_lts: &[LifetimeDef]) -> HashSet<RefLt> {
    let mut allowed_lts = HashSet::new();
    for lt in named_lts {
        if lt.bounds.is_empty() {
            allowed_lts.insert(RefLt::Named(lt.lifetime.name));
        }
    }
    allowed_lts.insert(RefLt::Unnamed);
    allowed_lts.insert(RefLt::Static);
    allowed_lts
}

fn lts_from_bounds<'a, T: Iterator<Item = &'a Lifetime>>(mut vec: Vec<RefLt>, bounds_lts: T) -> Vec<RefLt> {
    for lt in bounds_lts {
        if &*lt.name.as_str() != "'static" {
            vec.push(RefLt::Named(lt.name));
        }
    }

    vec
}

/// Number of unique lifetimes in the given vector.
fn unique_lifetimes(lts: &[RefLt]) -> usize {
    lts.iter().collect::<HashSet<_>>().len()
}

/// A visitor usable for `rustc_front::visit::walk_ty()`.
struct RefVisitor<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
    lts: Vec<RefLt>,
}

impl<'v, 't> RefVisitor<'v, 't> {
    fn new(cx: &'v LateContext<'v, 't>) -> RefVisitor<'v, 't> {
        RefVisitor {
            cx: cx,
            lts: Vec::new(),
        }
    }

    fn record(&mut self, lifetime: &Option<Lifetime>) {
        if let Some(ref lt) = *lifetime {
            if &*lt.name.as_str() == "'static" {
                self.lts.push(RefLt::Static);
            } else {
                self.lts.push(RefLt::Named(lt.name));
            }
        } else {
            self.lts.push(RefLt::Unnamed);
        }
    }

    fn into_vec(self) -> Vec<RefLt> {
        self.lts
    }

    fn collect_anonymous_lifetimes(&mut self, qpath: &QPath, ty: &Ty) {
        let last_path_segment = &last_path_segment(qpath).parameters;
        if let AngleBracketedParameters(ref params) = *last_path_segment {
            if params.lifetimes.is_empty() {
                match self.cx.tcx.tables().qpath_def(qpath, ty.id) {
                    Def::TyAlias(def_id) |
                    Def::Struct(def_id) => {
                        let generics = self.cx.tcx.item_generics(def_id);
                        for _ in generics.regions.as_slice() {
                            self.record(&None);
                        }
                    },
                    Def::Trait(def_id) => {
                        let trait_def = self.cx.tcx.trait_defs.borrow()[&def_id];
                        for _ in &self.cx.tcx.item_generics(trait_def.def_id).regions {
                            self.record(&None);
                        }
                    },
                    _ => (),
                }
            }
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
    // for lifetimes as parameters of generics
    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
        self.record(&Some(*lifetime));
    }

    fn visit_ty(&mut self, ty: &'tcx Ty) {
        match ty.node {
            TyRptr(None, _) => {
                self.record(&None);
            },
            TyPath(ref path) => {
                self.collect_anonymous_lifetimes(path, ty);
            },
            TyImplTrait(ref param_bounds) => {
                for bound in param_bounds {
                    if let RegionTyParamBound(_) = *bound {
                        self.record(&None);
                    }
                }
            },
            _ => (),
        }
        walk_ty(self, ty);
    }
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::None
    }
}

/// Are any lifetimes mentioned in the `where` clause? If yes, we don't try to
/// reason about elision.
fn has_where_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, where_clause: &'tcx WhereClause) -> bool {
    for predicate in &where_clause.predicates {
        match *predicate {
            WherePredicate::RegionPredicate(..) => return true,
            WherePredicate::BoundPredicate(ref pred) => {
                // a predicate like F: Trait or F: for<'a> Trait<'a>
                let mut visitor = RefVisitor::new(cx);
                // walk the type F, it may not contain LT refs
                walk_ty(&mut visitor, &pred.bounded_ty);
                if !visitor.lts.is_empty() {
                    return true;
                }
                // if the bounds define new lifetimes, they are fine to occur
                let allowed_lts = allowed_lts_from(&pred.bound_lifetimes);
                // now walk the bounds
                for bound in pred.bounds.iter() {
                    walk_ty_param_bound(&mut visitor, bound);
                }
                // and check that all lifetimes are allowed
                for lt in visitor.into_vec() {
                    if !allowed_lts.contains(&lt) {
                        return true;
                    }
                }
            },
            WherePredicate::EqPredicate(ref pred) => {
                let mut visitor = RefVisitor::new(cx);
                walk_ty(&mut visitor, &pred.ty);
                if !visitor.lts.is_empty() {
                    return true;
                }
            },
        }
    }
    false
}

struct LifetimeChecker {
    map: HashMap<Name, Span>,
}

impl<'tcx> Visitor<'tcx> for LifetimeChecker {
    // for lifetimes as parameters of generics
    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
        self.map.remove(&lifetime.name);
    }

    fn visit_lifetime_def(&mut self, _: &'tcx LifetimeDef) {
        // don't actually visit `<'a>` or `<'a: 'b>`
        // we've already visited the `'a` declarations and
        // don't want to spuriously remove them
        // `'b` in `'a: 'b` is useless unless used elsewhere in
        // a non-lifetime bound
    }
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::None
    }
}

fn report_extra_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, func: &'tcx FnDecl, generics: &'tcx Generics) {
    let hs = generics.lifetimes
        .iter()
        .map(|lt| (lt.lifetime.name, lt.lifetime.span))
        .collect();
    let mut checker = LifetimeChecker { map: hs };

    walk_generics(&mut checker, generics);
    walk_fn_decl(&mut checker, func);

    for &v in checker.map.values() {
        span_lint(cx, UNUSED_LIFETIMES, v, "this lifetime isn't used in the function definition");
    }
}
<|endoftext|>


<commit_before>use futures::sync::mpsc;
use futures::{try_ready, Async, AsyncSink, Future, Poll, Sink, Stream};
use std::io::{self, Read};
use tokio_postgres::types::{ToSql, Type};
use tokio_postgres::{Error, Row};
#[cfg(feature = "runtime")]
use tokio_postgres::{MakeTlsMode, Socket, TlsMode};

#[cfg(feature = "runtime")]
use crate::Builder;
use crate::{Query, Statement, Transaction};

pub struct Client(tokio_postgres::Client);

impl Client {
    #[cfg(feature = "runtime")]
    pub fn connect<T>(params: &str, tls_mode: T) -> Result<Client, Error>
    where
        T: MakeTlsMode<Socket> + 'static + Send,
        T::TlsMode: Send,
        T::Stream: Send,
        T::Future: Send,
        <T::TlsMode as TlsMode<Socket>>::Future: Send,
    {
        params.parse::<Builder>()?.connect(tls_mode)
    }

    #[cfg(feature = "runtime")]
    pub fn builder() -> Builder {
        Builder::new()
    }

    pub fn prepare(&mut self, query: &str) -> Result<Statement, Error> {
        self.0.prepare(query).wait().map(Statement)
    }

    pub fn prepare_typed(&mut self, query: &str, types: &[Type]) -> Result<Statement, Error> {
        self.0.prepare_typed(query, types).wait().map(Statement)
    }

    pub fn execute<T>(&mut self, query: &T, params: &[&dyn ToSql]) -> Result<u64, Error>
    where
        T: ?Sized + Query,
    {
        let statement = query.__statement(self)?;
        self.0.execute(&statement.0, params).wait()
    }

    pub fn query<T>(&mut self, query: &T, params: &[&dyn ToSql]) -> Result<Vec<Row>, Error>
    where
        T: ?Sized + Query,
    {
        let statement = query.__statement(self)?;
        self.0.query(&statement.0, params).collect().wait()
    }

    pub fn copy_in<T, R>(
        &mut self,
        query: &T,
        params: &[&dyn ToSql],
        reader: R,
    ) -> Result<u64, Error>
    where
        T: ?Sized + Query,
        R: Read,
    {
        let statement = query.__statement(self)?;
        let (sender, receiver) = mpsc::channel(1);
        let future = self.0.copy_in(&statement.0, params, CopyInStream(receiver));

        CopyInFuture {
            future,
            sender,
            reader,
            pending: None,
        }
        .wait()
    }

    pub fn batch_execute(&mut self, query: &str) -> Result<(), Error> {
        self.0.batch_execute(query).wait()
    }

    pub fn transaction(&mut self) -> Result<Transaction<'_>, Error> {
        self.batch_execute("BEGIN")?;
        Ok(Transaction::new(self))
    }

    pub fn is_closed(&self) -> bool {
        self.0.is_closed()
    }
}

impl From<tokio_postgres::Client> for Client {
    fn from(c: tokio_postgres::Client) -> Client {
        Client(c)
    }
}

enum CopyData {
    Data(Vec<u8>),
    Error(io::Error),
    Done,
}

struct CopyInStream(mpsc::Receiver<CopyData>);

impl Stream for CopyInStream {
    type Item = Vec<u8>;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<Vec<u8>>, io::Error> {
        match self.0.poll().expect("mpsc::Receiver can't error") {
            Async::Ready(Some(CopyData::Data(buf))) => Ok(Async::Ready(Some(buf))),
            Async::Ready(Some(CopyData::Error(e))) => Err(e),
            Async::Ready(Some(CopyData::Done)) => Ok(Async::Ready(None)),
            Async::Ready(None) => Err(io::Error::new(io::ErrorKind::Other, "writer disconnected")),
            Async::NotReady => Ok(Async::NotReady),
        }
    }
}

struct CopyInFuture<R> {
    future: tokio_postgres::CopyIn<CopyInStream>,
    sender: mpsc::Sender<CopyData>,
    reader: R,
    pending: Option<CopyData>,
}

impl<R> CopyInFuture<R> {
    fn poll_send_data(&mut self, data: CopyData) -> Poll<(), Error> {
        match self.sender.start_send(data) {
            Ok(AsyncSink::Ready) => Ok(Async::Ready(())),
            Ok(AsyncSink::NotReady(pending)) => {
                self.pending = Some(pending);
                Ok(Async::NotReady)
            }
            // the future's hung up on its end of the channel, so we'll wait for it to report an error
            Err(_) => {
                self.pending = Some(CopyData::Done);
                Ok(Async::NotReady)
            }
        }
    }
}

impl<R> Future for CopyInFuture<R>
where
    R: Read,
{
    type Item = u64;
    type Error = Error;

    fn poll(&mut self) -> Poll<u64, Error> {
        if let Async::Ready(n) = self.future.poll()? {
            return Ok(Async::Ready(n));
        }

        loop {
            let data = match self.pending.take() {
                Some(pending) => pending,
                None => {
                    let mut buf = vec![];
                    match self.reader.by_ref().take(4096).read_to_end(&mut buf) {
                        Ok(0) => CopyData::Done,
                        Ok(_) => CopyData::Data(buf),
                        Err(e) => CopyData::Error(e),
                    }
                }
            };

            try_ready!(self.poll_send_data(data));
        }
    }
}
<commit_msg>Clean up CopyInFuture<commit_after>use futures::sync::mpsc;
use futures::{Async, AsyncSink, Future, Poll, Sink, Stream};
use std::io::{self, Read};
use tokio_postgres::types::{ToSql, Type};
use tokio_postgres::{Error, Row};
#[cfg(feature = "runtime")]
use tokio_postgres::{MakeTlsMode, Socket, TlsMode};

#[cfg(feature = "runtime")]
use crate::Builder;
use crate::{Query, Statement, Transaction};

pub struct Client(tokio_postgres::Client);

impl Client {
    #[cfg(feature = "runtime")]
    pub fn connect<T>(params: &str, tls_mode: T) -> Result<Client, Error>
    where
        T: MakeTlsMode<Socket> + 'static + Send,
        T::TlsMode: Send,
        T::Stream: Send,
        T::Future: Send,
        <T::TlsMode as TlsMode<Socket>>::Future: Send,
    {
        params.parse::<Builder>()?.connect(tls_mode)
    }

    #[cfg(feature = "runtime")]
    pub fn builder() -> Builder {
        Builder::new()
    }

    pub fn prepare(&mut self, query: &str) -> Result<Statement, Error> {
        self.0.prepare(query).wait().map(Statement)
    }

    pub fn prepare_typed(&mut self, query: &str, types: &[Type]) -> Result<Statement, Error> {
        self.0.prepare_typed(query, types).wait().map(Statement)
    }

    pub fn execute<T>(&mut self, query: &T, params: &[&dyn ToSql]) -> Result<u64, Error>
    where
        T: ?Sized + Query,
    {
        let statement = query.__statement(self)?;
        self.0.execute(&statement.0, params).wait()
    }

    pub fn query<T>(&mut self, query: &T, params: &[&dyn ToSql]) -> Result<Vec<Row>, Error>
    where
        T: ?Sized + Query,
    {
        let statement = query.__statement(self)?;
        self.0.query(&statement.0, params).collect().wait()
    }

    pub fn copy_in<T, R>(
        &mut self,
        query: &T,
        params: &[&dyn ToSql],
        reader: R,
    ) -> Result<u64, Error>
    where
        T: ?Sized + Query,
        R: Read,
    {
        let statement = query.__statement(self)?;
        let (sender, receiver) = mpsc::channel(1);
        let future = self.0.copy_in(&statement.0, params, CopyInStream(receiver));

        CopyInFuture {
            future,
            sender,
            reader,
            pending: None,
            done: false,
        }
        .wait()
    }

    pub fn batch_execute(&mut self, query: &str) -> Result<(), Error> {
        self.0.batch_execute(query).wait()
    }

    pub fn transaction(&mut self) -> Result<Transaction<'_>, Error> {
        self.batch_execute("BEGIN")?;
        Ok(Transaction::new(self))
    }

    pub fn is_closed(&self) -> bool {
        self.0.is_closed()
    }
}

impl From<tokio_postgres::Client> for Client {
    fn from(c: tokio_postgres::Client) -> Client {
        Client(c)
    }
}

enum CopyData {
    Data(Vec<u8>),
    Error(io::Error),
    Done,
}

struct CopyInStream(mpsc::Receiver<CopyData>);

impl Stream for CopyInStream {
    type Item = Vec<u8>;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<Vec<u8>>, io::Error> {
        match self.0.poll().expect("mpsc::Receiver can't error") {
            Async::Ready(Some(CopyData::Data(buf))) => Ok(Async::Ready(Some(buf))),
            Async::Ready(Some(CopyData::Error(e))) => Err(e),
            Async::Ready(Some(CopyData::Done)) => Ok(Async::Ready(None)),
            Async::Ready(None) => Err(io::Error::new(io::ErrorKind::Other, "writer disconnected")),
            Async::NotReady => Ok(Async::NotReady),
        }
    }
}

struct CopyInFuture<R> {
    future: tokio_postgres::CopyIn<CopyInStream>,
    sender: mpsc::Sender<CopyData>,
    reader: R,
    pending: Option<CopyData>,
    done: bool,
}

impl<R> Future for CopyInFuture<R>
where
    R: Read,
{
    type Item = u64;
    type Error = Error;

    fn poll(&mut self) -> Poll<u64, Error> {
        loop {
            if let Async::Ready(n) = self.future.poll()? {
                return Ok(Async::Ready(n));
            }

            let data = match self.pending.take() {
                Some(pending) => pending,
                None => {
                    if self.done {
                        continue;
                    }

                    let mut buf = vec![];
                    match self.reader.by_ref().take(4096).read_to_end(&mut buf) {
                        Ok(0) => {
                            self.done = true;
                            CopyData::Done
                        }
                        Ok(_) => CopyData::Data(buf),
                        Err(e) => {
                            self.done = true;
                            CopyData::Error(e)
                        }
                    }
                }
            };

            match self.sender.start_send(data) {
                Ok(AsyncSink::Ready) => {}
                Ok(AsyncSink::NotReady(pending)) => {
                    self.pending = Some(pending);
                    return Ok(Async::NotReady);
                }
                // the future's hung up on its end of the channel, so we'll wait for it to error
                Err(_) => {
                    self.done = true;
                    return Ok(Async::NotReady);
                }
            }
        }
    }
}
<|endoftext|>