<commit_before>use super::ast::*;
use super::issue::{IssueTracker, Level};
use super::symbol::{Type, SymbolTable, Identifier, Symbol};
use super::lexer::Operator;

macro_rules! try_bool (
    ( $e:expr ) => {
        if !$e { return false }
    }
);

pub struct TypeChecker<'a, 'b> {
    issues: &'a IssueTracker<'a>,
    ast: &'b Root,
    symtab: &'a SymbolTable<'a>,
}

impl<'a, 'b> TypeChecker<'a, 'b> {
    pub fn new(ast: &'b Root, symtab: &'a SymbolTable<'a>, issues: &'a IssueTracker<'a>) -> TypeChecker<'a, 'b> {
        TypeChecker {
            ast: ast,
            symtab: symtab,
            issues: issues,
        }
    }

    pub fn check(&self) -> bool {
        self.check_root(self.ast)
    }

    fn check_root(&self, root: &Root) -> bool {
        let mut ok = true;
        for item in root.iter() {
            ok &= match item {
                &Item::FunctionDef(ref f) =>
                    self.check_function_def(&f),
                &Item::Assignment(ref a) =>
                    self.check_assignment(&a),
            };
        }
        ok
    }

    fn check_assignment(&self, assign: &Node<Assignment>) -> bool {
        if let Some((_, 0)) = self.symtab.get_symbol(assign.ident()) {
            self.issues.new_issue(assign.pos(), Level::Warning,
                                  "constant declaration shadows previous declaration of same name");
        }
        let mut sym = Symbol::new();
        sym.ty = self.typeof_expr(&assign.expr());
        let ok = sym.ty.is_some();
        if !ok {
            self.issues.new_issue(assign.pos(), Level::Error, "type of assignment could not be determined");
            return false;
        }
        self.symtab.set_symbol(assign.ident(), sym);
        ok
    }

    fn check_function_def(&self, def: &Node<FunctionDef>) -> bool {
        if let Some((_, 0)) = self.symtab.get_symbol(def.ident()) {
            self.issues.new_issue(def.pos(), Level::Warning,
                                  "function declaration shadows previous declaration of same name");
        }
        let mut sym = Symbol::new();
        sym.ty = Some(Type::Function(def.func.clone()));
        self.symtab.set_symbol(def.ident(), sym);
        true
    }

    fn typeof_expr(&self, expr: &Expression) -> Option<Type> {
        match expr {
            &Expression::Constant(_) => Some(Type::Number),
            &Expression::Boolean(_) => Some(Type::Boolean),
            &Expression::Variable(ref id) => self.typeof_var(id),
            &Expression::Infix(ref v) => self.typeof_infix(v),
            &Expression::Prefix(ref v) => self.typeof_prefix(v),
            &Expression::Conditional(ref c) => self.typeof_conditional(c),
            &Expression::Block(ref b) => self.typeof_block(b),
            &Expression::FunctionCall(ref c) => self.typeof_function_call(c),
        }
    }

    fn typeof_function_call(&self, call: &Node<FunctionCall>) -> Option<Type> {
        let def = match self.symtab.get_symbol(call.ident()) {
            Some((Symbol { ty: Some(Type::Function(f)) }, _)) => f.clone(),
            Some((Symbol { ty: Some(ty) }, _)) => {
                self.issues.new_issue(call.ident_pos(), Level::Error,
                                      format!("expected type `Function`, got `{}`", ty));
                return None;
            },
            None => {
                self.issues.new_issue(call.ident_pos(), Level::Error, "undefined identifier");
                return None;
            },
            _ => {
                self.issues.new_issue(call.ident_pos(), Level::Error, "identifier has no type");
                return None;
            },
        };

        let mut undef_args = def.args().clone();
        let mut def_args = Vec::new();

        // first set explicitly defined args, like a=5
        for arg in call.args() {
            match *arg.item() {
                Argument(Some(Node(id, _)), Some(_)) => {
                    undef_args.retain(|x| match *x.item() {
                        Argument(Some(Node(def_id, _)), _) if def_id == id => false,
                        _ => true,
                    });
                    def_args.push(arg.clone());
                }
                _ => { },
            }
        }

        // next set expression only args, like 2+2
        for arg in call.args() {
            match *arg.item() {
                Argument(None, Some(ref expr)) => {
                    if undef_args.len() > 0 {
                        def_args.push(Node(Argument(undef_args[0].item().0, Some(expr.clone())), arg.pos()));
                        undef_args.remove(0);
                    } else {
                        self.issues.new_issue(arg.pos(), Level::Warning, "unused argument");
                    }
                }
                _ => { },
            }
        }

        // set default args that weren't set previously
        for arg in undef_args.iter().rev() {
            match *arg.item() {
                Argument(Some(_), Some(_)) => {
                    def_args.insert(0, arg.clone());
                }
                _ => { }
            }
        }

        undef_args.retain(|x| match *x.item() {
            Argument(Some(_), Some(_)) => false,
            _ => true,
        });

        // check that all args are defined somewhere for implicit calls
        if call.ty() == CallType::Implicit {
            undef_args.retain(|x| match *x.item() {
                Argument(Some(id), None) => self.symtab.get_symbol(*id.item()).is_none(),
                _ => true,
            });
        }

        // determine the type of the arguments
        for arg in &def_args {
            match *arg.item() {
                Argument(Some(Node(id, _)), Some(ref expr)) => {
                    let mut sym = Symbol::new();
                    sym.ty = self.typeof_expr(expr);
                    self.symtab.enter_scope(def.pos());
                    if let Some((prev, 1)) = self.symtab.get_symbol(id) {
                        if sym.ty.is_some() && prev.ty.is_some() && sym.ty != prev.ty {
                            self.issues.new_issue(expr.pos(), Level::Error,
                                format!("expected type `{}`, got `{}`", prev.ty.unwrap(), sym.ty.unwrap()));
                            return None;
                        }
                    }
                    self.symtab.set_symbol(id, sym);
                    self.symtab.leave_scope();
                }
                _ => { },
            }
        }

        let recursive = self.symtab.check_cycle(def.pos());
        if recursive {
            return Some(Type::Infer);
        }

        for unassigned in undef_args.iter() {
            self.issues.new_issue(call.args_pos(), Level::Error, format!(
                    "argument `{}` is required",
                    self.symtab.get_name(unassigned.ident().unwrap())));
        }
        if undef_args.len() > 0 {
            None
        } else {
            self.symtab.enter_scope(def.pos());
            let ty = self.typeof_block(&def.block);
            self.symtab.leave_scope();
            ty
        }
    }

    fn typeof_block(&self, block: &Node<Block>) -> Option<Type> {
        self.symtab.enter_scope(block.pos());
        for stmnt in block.item() {
            match stmnt {
                &Statement::Assignment(ref a) => {
                    if !self.check_assignment(a) {
                        self.issues.new_issue(a.pos(), Level::Error,
                            "could not determine type of block assignment");
                        return None
                    }
                }
                _ => { }
            }
        }
        let count = block.iter().filter(|&x| {
            match x {
                &Statement::Expression(..) => true,
                _ => false,
            }
        }).count();
        let mut ty = None;
        for stmnt in block.item() {
            match stmnt {
                &Statement::Expression(ref e) => {
                    ty = self.typeof_expr(e);
                    match ty {
                        Some(Type::Number) => { },
                        Some(Type::Infer) => ty = Some(Type::Number),
                        Some(_) => {
                            if count > 1 {
                                self.issues.new_issue(e.pos(), Level::Error,
                                    format!("expected type `Number`, got `{}`", ty.unwrap()));
                                ty = None;
                            }
                        }
                        None => {
                            self.issues.new_issue(e.pos(), Level::Error,
                                "could not determine type of statement");
                        }

                    }
                }
                _ => { }
            }
        }
        self.symtab.leave_scope();
        ty
    }

    fn typeof_conditional(&self, cond: &Node<Conditional>) -> Option<Type> {
        match self.typeof_expr(&cond.cond()) {
            Some(x) => {
                if x != Type::Boolean {
                    self.issues.new_issue(cond.cond_pos(), Level::Error,
                                          format!("expected type `Boolean`, got `{}`", x));
                    return None;
                }
            },
            None => {
                self.issues.new_issue(cond.cond_pos(), Level::Error,
                                      "type of condition could not be determined");
                return None;
            }
        }
        let then_ty = match self.typeof_expr(cond.then()) {
            Some(x) => x,
            None => {
                self.issues.new_issue(cond.then_pos(), Level::Error,
                                      "type of conditional then expression could not be determined");
                return None;
            }
        };
        if let &Some(ref els) = cond.els() {
            let else_ty = match self.typeof_expr(&els) {
                Some(x) => x,
                None => {
                    self.issues.new_issue(els.pos(), Level::Error,
                                          "type of conditional else expression could not be determined");
                    return None;
                }
            };
            if else_ty == Type::Infer && then_ty != Type::Infer {
                return Some(then_ty);
            }
            if then_ty == Type::Infer && else_ty != Type::Infer {
                return Some(else_ty);
            }
            if else_ty != then_ty {
                    self.issues.new_issue(els.pos(), Level::Error, format!(
                                          "then branch of conditional is of type `{}` but else branch is of type `{}`",
                                          then_ty, else_ty));
                    return None;
            }
        }
        Some(then_ty)
    }

    fn typeof_var(&self, ident: &Node<Identifier>) -> Option<Type> {
        match self.symtab.get_symbol(*ident.item()) {
            Some((s, _)) => s.ty,
            None => {
                self.issues.new_issue(ident.pos(), Level::Error,
                                      "undefined variable referenced");
                None
            }
        }
    }

    fn typeof_infix(&self, infix: &Node<Infix>) -> Option<Type> {
        let lhs_ty = match self.typeof_expr(infix.left()) {
            Some(x) => x,
            None => {
                self.issues.new_issue(infix.left_pos(), Level::Error,
                                      "type of infix lhs could not be determined");
                return None;
            }
        };
        let rhs_ty = match self.typeof_expr(infix.right()) {
            Some(x) => x,
            None => {
                self.issues.new_issue(infix.right_pos(), Level::Error,
                                      "type of infix rhs could not be determined");
                return None;
            }
        };
        match infix.op() {
            Operator::Add |
            Operator::Sub |
            Operator::Mul |
            Operator::Div |
            Operator::Exp |
            Operator::Mod => {
                if lhs_ty == rhs_ty && (lhs_ty == Type::Number || lhs_ty == Type::Infer) {
                    Some(lhs_ty)
                } else if lhs_ty == Type::Number && rhs_ty == Type::Infer ||
                          rhs_ty == Type::Number && lhs_ty == Type::Infer {
                    Some(Type::Number)
                } else {
                    self.issues.new_issue(infix.op_pos(), Level::Error, format!(
                                          "cannot apply numerical operator to types `{}` and `{}`",
                                          lhs_ty, rhs_ty));
                    None
                }
            }
            Operator::Less |
            Operator::ApproxEqu |
            Operator::GreaterEqual |
            Operator::LessEqual |
            Operator::Greater => {
                if lhs_ty == rhs_ty && lhs_ty == Type::Number {
                    Some(Type::Boolean)
                } else if lhs_ty == rhs_ty && lhs_ty == Type::Infer {
                    Some(Type::Infer)
                } else if lhs_ty == Type::Number && rhs_ty == Type::Infer ||
                          rhs_ty == Type::Number && lhs_ty == Type::Infer {
                    Some(Type::Boolean)
                } else {
                    self.issues.new_issue(infix.op_pos(), Level::Error, format!(
                                          "cannot apply comparison operator to types `{}` and `{}`",
                                          lhs_ty, rhs_ty));
                    None
                }
            }
            Operator::Equ |
            Operator::NotEqu => {
                if lhs_ty == rhs_ty &&
                   (lhs_ty == Type::Number || lhs_ty == Type::Boolean) {
                    Some(Type::Boolean)
                } else if lhs_ty == rhs_ty && lhs_ty == Type::Infer {
                    Some(Type::Infer)
                } else if (lhs_ty == Type::Number || lhs_ty == Type::Boolean) && rhs_ty == Type::Infer ||
                          (rhs_ty == Type::Number || rhs_ty == Type::Boolean) && lhs_ty == Type::Infer {
                    Some(Type::Boolean)
                } else {
                    self.issues.new_issue(infix.op_pos(), Level::Error, format!(
                                          "cannot apply equality operator to types `{}` and `{}`",
                                          lhs_ty, rhs_ty));
                    None
                }
            }
            Operator::And |
            Operator::Or |
            Operator::Xor => {
                if lhs_ty == rhs_ty && (lhs_ty == Type::Boolean || lhs_ty == Type::Infer) {
                    Some(lhs_ty)
                } else if lhs_ty == Type::Boolean && rhs_ty == Type::Infer ||
                          rhs_ty == Type::Boolean && lhs_ty == Type::Infer {
                    Some(Type::Boolean)
                } else {
                    self.issues.new_issue(infix.op_pos(), Level::Error, format!(
                                          "cannot apply logical operator to types `{}` and `{}`",
                                          lhs_ty, rhs_ty));
                    None
                }
            }
            _ => {
                unreachable!();
            }
        }
    }

    fn typeof_prefix(&self, prefix: &Node<Prefix>) -> Option<Type> {
        let expr_ty = match self.typeof_expr(prefix.expr()) {
            Some(x) => x,
            None => {
                self.issues.new_issue(prefix.expr_pos(), Level::Error,
                                      "type of prefix expression could not be determined");
                return None;
            }
        };
        match prefix.op() {
            Operator::Sub => {
                if expr_ty == Type::Number {
                    Some(Type::Number)
                } else {
                    self.issues.new_issue(prefix.expr_pos(), Level::Error, format!(
                                          "expected `Number`, got `{:?}`", expr_ty));
                    None
                }
            }
            Operator::Not => {
                if expr_ty == Type::Boolean {
                    Some(Type::Boolean)
                } else {
                    self.issues.new_issue(prefix.expr_pos(), Level::Error, format!(
                                          "expected `Boolean`, got `{:?}`", expr_ty));
                    None
                }
            }
            _ => {
                unreachable!();
            }
        }
    }
}
<commit_msg>Fix required parameters not being checked on recursive calls.<commit_after>use super::ast::*;
use super::issue::{IssueTracker, Level};
use super::symbol::{Type, SymbolTable, Identifier, Symbol};
use super::lexer::Operator;

macro_rules! try_bool (
    ( $e:expr ) => {
        if !$e { return false }
    }
);

pub struct TypeChecker<'a, 'b> {
    issues: &'a IssueTracker<'a>,
    ast: &'b Root,
    symtab: &'a SymbolTable<'a>,
}

impl<'a, 'b> TypeChecker<'a, 'b> {
    pub fn new(ast: &'b Root, symtab: &'a SymbolTable<'a>, issues: &'a IssueTracker<'a>) -> TypeChecker<'a, 'b> {
        TypeChecker {
            ast: ast,
            symtab: symtab,
            issues: issues,
        }
    }

    pub fn check(&self) -> bool {
        self.check_root(self.ast)
    }

    fn check_root(&self, root: &Root) -> bool {
        let mut ok = true;
        for item in root.iter() {
            ok &= match item {
                &Item::FunctionDef(ref f) =>
                    self.check_function_def(&f),
                &Item::Assignment(ref a) =>
                    self.check_assignment(&a),
            };
        }
        ok
    }

    fn check_assignment(&self, assign: &Node<Assignment>) -> bool {
        if let Some((_, 0)) = self.symtab.get_symbol(assign.ident()) {
            self.issues.new_issue(assign.pos(), Level::Warning,
                                  "constant declaration shadows previous declaration of same name");
        }
        let mut sym = Symbol::new();
        sym.ty = self.typeof_expr(&assign.expr());
        let ok = sym.ty.is_some();
        if !ok {
            self.issues.new_issue(assign.pos(), Level::Error, "type of assignment could not be determined");
            return false;
        }
        self.symtab.set_symbol(assign.ident(), sym);
        ok
    }

    fn check_function_def(&self, def: &Node<FunctionDef>) -> bool {
        if let Some((_, 0)) = self.symtab.get_symbol(def.ident()) {
            self.issues.new_issue(def.pos(), Level::Warning,
                                  "function declaration shadows previous declaration of same name");
        }
        let mut sym = Symbol::new();
        sym.ty = Some(Type::Function(def.func.clone()));
        self.symtab.set_symbol(def.ident(), sym);
        true
    }

    fn typeof_expr(&self, expr: &Expression) -> Option<Type> {
        match expr {
            &Expression::Constant(_) => Some(Type::Number),
            &Expression::Boolean(_) => Some(Type::Boolean),
            &Expression::Variable(ref id) => self.typeof_var(id),
            &Expression::Infix(ref v) => self.typeof_infix(v),
            &Expression::Prefix(ref v) => self.typeof_prefix(v),
            &Expression::Conditional(ref c) => self.typeof_conditional(c),
            &Expression::Block(ref b) => self.typeof_block(b),
            &Expression::FunctionCall(ref c) => self.typeof_function_call(c),
        }
    }

    fn typeof_function_call(&self, call: &Node<FunctionCall>) -> Option<Type> {
        let def = match self.symtab.get_symbol(call.ident()) {
            Some((Symbol { ty: Some(Type::Function(f)) }, _)) => f.clone(),
            Some((Symbol { ty: Some(ty) }, _)) => {
                self.issues.new_issue(call.ident_pos(), Level::Error,
                                      format!("expected type `Function`, got `{}`", ty));
                return None;
            },
            None => {
                self.issues.new_issue(call.ident_pos(), Level::Error, "undefined identifier");
                return None;
            },
            _ => {
                self.issues.new_issue(call.ident_pos(), Level::Error, "identifier has no type");
                return None;
            },
        };

        let mut undef_args = def.args().clone();
        let mut def_args = Vec::new();

        // first set explicitly defined args, like a=5
        for arg in call.args() {
            match *arg.item() {
                Argument(Some(Node(id, _)), Some(_)) => {
                    undef_args.retain(|x| match *x.item() {
                        Argument(Some(Node(def_id, _)), _) if def_id == id => false,
                        _ => true,
                    });
                    def_args.push(arg.clone());
                }
                _ => { },
            }
        }

        // next set expression only args, like 2+2
        for arg in call.args() {
            match *arg.item() {
                Argument(None, Some(ref expr)) => {
                    if undef_args.len() > 0 {
                        def_args.push(Node(Argument(undef_args[0].item().0, Some(expr.clone())), arg.pos()));
                        undef_args.remove(0);
                    } else {
                        self.issues.new_issue(arg.pos(), Level::Warning, "unused argument");
                    }
                }
                _ => { },
            }
        }

        // set default args that weren't set previously
        for arg in undef_args.iter().rev() {
            match *arg.item() {
                Argument(Some(_), Some(_)) => {
                    def_args.insert(0, arg.clone());
                }
                _ => { }
            }
        }

        undef_args.retain(|x| match *x.item() {
            Argument(Some(_), Some(_)) => false,
            _ => true,
        });

        // check that all args are defined somewhere for implicit calls
        if call.ty() == CallType::Implicit {
            undef_args.retain(|x| match *x.item() {
                Argument(Some(id), None) => self.symtab.get_symbol(*id.item()).is_none(),
                _ => true,
            });
        }

        // determine the type of the arguments
        for arg in &def_args {
            match *arg.item() {
                Argument(Some(Node(id, _)), Some(ref expr)) => {
                    let mut sym = Symbol::new();
                    sym.ty = self.typeof_expr(expr);
                    self.symtab.enter_scope(def.pos());
                    if let Some((prev, 1)) = self.symtab.get_symbol(id) {
                        if sym.ty.is_some() && prev.ty.is_some() && sym.ty != prev.ty {
                            self.issues.new_issue(expr.pos(), Level::Error,
                                format!("expected type `{}`, got `{}`", prev.ty.unwrap(), sym.ty.unwrap()));
                            return None;
                        }
                    }
                    self.symtab.set_symbol(id, sym);
                    self.symtab.leave_scope();
                }
                _ => { },
            }
        }

        for unassigned in undef_args.iter() {
            self.issues.new_issue(call.args_pos(), Level::Error, format!(
                    "argument `{}` is required",
                    self.symtab.get_name(unassigned.ident().unwrap())));
        }

        let recursive = self.symtab.check_cycle(def.pos());
        if recursive {
            return Some(Type::Infer);
        }

        if undef_args.len() > 0 {
            None
        } else {
            self.symtab.enter_scope(def.pos());
            let ty = self.typeof_block(&def.block);
            self.symtab.leave_scope();
            ty
        }
    }

    fn typeof_block(&self, block: &Node<Block>) -> Option<Type> {
        self.symtab.enter_scope(block.pos());
        for stmnt in block.item() {
            match stmnt {
                &Statement::Assignment(ref a) => {
                    if !self.check_assignment(a) {
                        self.issues.new_issue(a.pos(), Level::Error,
                            "could not determine type of block assignment");
                        return None
                    }
                }
                _ => { }
            }
        }
        let count = block.iter().filter(|&x| {
            match x {
                &Statement::Expression(..) => true,
                _ => false,
            }
        }).count();
        let mut ty = None;
        for stmnt in block.item() {
            match stmnt {
                &Statement::Expression(ref e) => {
                    ty = self.typeof_expr(e);
                    match ty {
                        Some(Type::Number) => { },
                        Some(Type::Infer) => ty = Some(Type::Number),
                        Some(_) => {
                            if count > 1 {
                                self.issues.new_issue(e.pos(), Level::Error,
                                    format!("expected type `Number`, got `{}`", ty.unwrap()));
                                ty = None;
                            }
                        }
                        None => {
                            self.issues.new_issue(e.pos(), Level::Error,
                                "could not determine type of statement");
                        }

                    }
                }
                _ => { }
            }
        }
        self.symtab.leave_scope();
        ty
    }

    fn typeof_conditional(&self, cond: &Node<Conditional>) -> Option<Type> {
        match self.typeof_expr(&cond.cond()) {
            Some(x) => {
                if x != Type::Boolean {
                    self.issues.new_issue(cond.cond_pos(), Level::Error,
                                          format!("expected type `Boolean`, got `{}`", x));
                    return None;
                }
            },
            None => {
                self.issues.new_issue(cond.cond_pos(), Level::Error,
                                      "type of condition could not be determined");
                return None;
            }
        }
        let then_ty = match self.typeof_expr(cond.then()) {
            Some(x) => x,
            None => {
                self.issues.new_issue(cond.then_pos(), Level::Error,
                                      "type of conditional then expression could not be determined");
                return None;
            }
        };
        if let &Some(ref els) = cond.els() {
            let else_ty = match self.typeof_expr(&els) {
                Some(x) => x,
                None => {
                    self.issues.new_issue(els.pos(), Level::Error,
                                          "type of conditional else expression could not be determined");
                    return None;
                }
            };
            if else_ty == Type::Infer && then_ty != Type::Infer {
                return Some(then_ty);
            }
            if then_ty == Type::Infer && else_ty != Type::Infer {
                return Some(else_ty);
            }
            if else_ty != then_ty {
                    self.issues.new_issue(els.pos(), Level::Error, format!(
                                          "then branch of conditional is of type `{}` but else branch is of type `{}`",
                                          then_ty, else_ty));
                    return None;
            }
        }
        Some(then_ty)
    }

    fn typeof_var(&self, ident: &Node<Identifier>) -> Option<Type> {
        match self.symtab.get_symbol(*ident.item()) {
            Some((s, _)) => s.ty,
            None => {
                self.issues.new_issue(ident.pos(), Level::Error,
                                      "undefined variable referenced");
                None
            }
        }
    }

    fn typeof_infix(&self, infix: &Node<Infix>) -> Option<Type> {
        let lhs_ty = match self.typeof_expr(infix.left()) {
            Some(x) => x,
            None => {
                self.issues.new_issue(infix.left_pos(), Level::Error,
                                      "type of infix lhs could not be determined");
                return None;
            }
        };
        let rhs_ty = match self.typeof_expr(infix.right()) {
            Some(x) => x,
            None => {
                self.issues.new_issue(infix.right_pos(), Level::Error,
                                      "type of infix rhs could not be determined");
                return None;
            }
        };
        match infix.op() {
            Operator::Add |
            Operator::Sub |
            Operator::Mul |
            Operator::Div |
            Operator::Exp |
            Operator::Mod => {
                if lhs_ty == rhs_ty && (lhs_ty == Type::Number || lhs_ty == Type::Infer) {
                    Some(lhs_ty)
                } else if lhs_ty == Type::Number && rhs_ty == Type::Infer ||
                          rhs_ty == Type::Number && lhs_ty == Type::Infer {
                    Some(Type::Number)
                } else {
                    self.issues.new_issue(infix.op_pos(), Level::Error, format!(
                                          "cannot apply numerical operator to types `{}` and `{}`",
                                          lhs_ty, rhs_ty));
                    None
                }
            }
            Operator::Less |
            Operator::ApproxEqu |
            Operator::GreaterEqual |
            Operator::LessEqual |
            Operator::Greater => {
                if lhs_ty == rhs_ty && lhs_ty == Type::Number {
                    Some(Type::Boolean)
                } else if lhs_ty == rhs_ty && lhs_ty == Type::Infer {
                    Some(Type::Infer)
                } else if lhs_ty == Type::Number && rhs_ty == Type::Infer ||
                          rhs_ty == Type::Number && lhs_ty == Type::Infer {
                    Some(Type::Boolean)
                } else {
                    self.issues.new_issue(infix.op_pos(), Level::Error, format!(
                                          "cannot apply comparison operator to types `{}` and `{}`",
                                          lhs_ty, rhs_ty));
                    None
                }
            }
            Operator::Equ |
            Operator::NotEqu => {
                if lhs_ty == rhs_ty &&
                   (lhs_ty == Type::Number || lhs_ty == Type::Boolean) {
                    Some(Type::Boolean)
                } else if lhs_ty == rhs_ty && lhs_ty == Type::Infer {
                    Some(Type::Infer)
                } else if (lhs_ty == Type::Number || lhs_ty == Type::Boolean) && rhs_ty == Type::Infer ||
                          (rhs_ty == Type::Number || rhs_ty == Type::Boolean) && lhs_ty == Type::Infer {
                    Some(Type::Boolean)
                } else {
                    self.issues.new_issue(infix.op_pos(), Level::Error, format!(
                                          "cannot apply equality operator to types `{}` and `{}`",
                                          lhs_ty, rhs_ty));
                    None
                }
            }
            Operator::And |
            Operator::Or |
            Operator::Xor => {
                if lhs_ty == rhs_ty && (lhs_ty == Type::Boolean || lhs_ty == Type::Infer) {
                    Some(lhs_ty)
                } else if lhs_ty == Type::Boolean && rhs_ty == Type::Infer ||
                          rhs_ty == Type::Boolean && lhs_ty == Type::Infer {
                    Some(Type::Boolean)
                } else {
                    self.issues.new_issue(infix.op_pos(), Level::Error, format!(
                                          "cannot apply logical operator to types `{}` and `{}`",
                                          lhs_ty, rhs_ty));
                    None
                }
            }
            _ => {
                unreachable!();
            }
        }
    }

    fn typeof_prefix(&self, prefix: &Node<Prefix>) -> Option<Type> {
        let expr_ty = match self.typeof_expr(prefix.expr()) {
            Some(x) => x,
            None => {
                self.issues.new_issue(prefix.expr_pos(), Level::Error,
                                      "type of prefix expression could not be determined");
                return None;
            }
        };
        match prefix.op() {
            Operator::Sub => {
                if expr_ty == Type::Number {
                    Some(Type::Number)
                } else {
                    self.issues.new_issue(prefix.expr_pos(), Level::Error, format!(
                                          "expected `Number`, got `{:?}`", expr_ty));
                    None
                }
            }
            Operator::Not => {
                if expr_ty == Type::Boolean {
                    Some(Type::Boolean)
                } else {
                    self.issues.new_issue(prefix.expr_pos(), Level::Error, format!(
                                          "expected `Boolean`, got `{:?}`", expr_ty));
                    None
                }
            }
            _ => {
                unreachable!();
            }
        }
    }
}
<|endoftext|>


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

//! A helper class for dealing with static archives

use back::link::{get_ar_prog};
use driver::session::Session;
use metadata::filesearch;
use lib::llvm::{ArchiveRef, llvm};

use std::cast;
use std::io;
use std::io::{fs, TempDir};
use std::libc;
use std::os;
use std::io::process::{ProcessConfig, Process, ProcessOutput};
use std::str;
use std::raw;
use syntax::abi;

pub static METADATA_FILENAME: &'static str = "rust.metadata.bin";

pub struct Archive<'a> {
    priv sess: &'a Session,
    priv dst: Path,
}

pub struct ArchiveRO {
    priv ptr: ArchiveRef,
}

fn run_ar(sess: &Session, args: &str, cwd: Option<&Path>,
          paths: &[&Path]) -> ProcessOutput {
    let ar = get_ar_prog(sess);

    let mut args = vec!(args.to_owned());
    let mut paths = paths.iter().map(|p| p.as_str().unwrap().to_owned());
    args.extend(&mut paths);
    debug!("{} {}", ar, args.connect(" "));
    match cwd {
        Some(p) => { debug!("inside {}", p.display()); }
        None => {}
    }
    match Process::configure(ProcessConfig {
        program: ar.as_slice(),
        args: args.as_slice(),
        cwd: cwd.map(|a| &*a),
        .. ProcessConfig::new()
    }) {
        Ok(mut prog) => {
            let o = prog.wait_with_output();
            if !o.status.success() {
                sess.err(format!("{} {} failed with: {}", ar, args.connect(" "),
                                 o.status));
                sess.note(format!("stdout ---\n{}", str::from_utf8(o.output).unwrap()));
                sess.note(format!("stderr ---\n{}", str::from_utf8(o.error).unwrap()));
                sess.abort_if_errors();
            }
            o
        },
        Err(e) => {
            sess.err(format!("could not exec `{}`: {}", ar, e));
            sess.abort_if_errors();
            fail!("rustc::back::archive::run_ar() should not reach this point");
        }
    }
}

impl<'a> Archive<'a> {
    /// Initializes a new static archive with the given object file
    pub fn create<'b>(sess: &'a Session, dst: &'b Path,
                      initial_object: &'b Path) -> Archive<'a> {
        run_ar(sess, "crus", None, [dst, initial_object]);
        Archive { sess: sess, dst: dst.clone() }
    }

    /// Opens an existing static archive
    pub fn open(sess: &'a Session, dst: Path) -> Archive<'a> {
        assert!(dst.exists());
        Archive { sess: sess, dst: dst }
    }

    /// Read a file in the archive
    pub fn read(&self, file: &str) -> Vec<u8> {
        // Apparently if "ar p" is used on windows, it generates a corrupt file
        // which has bad headers and LLVM will immediately choke on it
        if cfg!(windows) && cfg!(windows) { // FIXME(#10734) double-and
            let loc = TempDir::new("rsar").unwrap();
            let archive = os::make_absolute(&self.dst);
            run_ar(self.sess, "x", Some(loc.path()), [&archive,
                                                      &Path::new(file)]);
            let result: Vec<u8> =
                fs::File::open(&loc.path().join(file)).read_to_end()
                                                      .unwrap()
                                                      .move_iter()
                                                      .collect();
            result
        } else {
            run_ar(self.sess,
                   "p",
                   None,
                   [&self.dst, &Path::new(file)]).output.move_iter().collect()
        }
    }

    /// Adds all of the contents of a native library to this archive. This will
    /// search in the relevant locations for a library named `name`.
    pub fn add_native_library(&mut self, name: &str) -> io::IoResult<()> {
        let location = self.find_library(name);
        self.add_archive(&location, name, [])
    }

    /// Adds all of the contents of the rlib at the specified path to this
    /// archive.
    ///
    /// This ignores adding the bytecode from the rlib, and if LTO is enabled
    /// then the object file also isn't added.
    pub fn add_rlib(&mut self, rlib: &Path, name: &str,
                    lto: bool) -> io::IoResult<()> {
        let object = format!("{}.o", name);
        let bytecode = format!("{}.bc", name);
        let mut ignore = vec!(METADATA_FILENAME, bytecode.as_slice());
        if lto {
            ignore.push(object.as_slice());
        }
        self.add_archive(rlib, name, ignore.as_slice())
    }

    /// Adds an arbitrary file to this archive
    pub fn add_file(&mut self, file: &Path, has_symbols: bool) {
        let cmd = if has_symbols {"r"} else {"rS"};
        run_ar(self.sess, cmd, None, [&self.dst, file]);
    }

    /// Removes a file from this archive
    pub fn remove_file(&mut self, file: &str) {
        run_ar(self.sess, "d", None, [&self.dst, &Path::new(file)]);
    }

    /// Updates all symbols in the archive (runs 'ar s' over it)
    pub fn update_symbols(&mut self) {
        run_ar(self.sess, "s", None, [&self.dst]);
    }

    /// Lists all files in an archive
    pub fn files(&self) -> Vec<~str> {
        let output = run_ar(self.sess, "t", None, [&self.dst]);
        let output = str::from_utf8(output.output).unwrap();
        // use lines_any because windows delimits output with `\r\n` instead of
        // just `\n`
        output.lines_any().map(|s| s.to_owned()).collect()
    }

    fn add_archive(&mut self, archive: &Path, name: &str,
                   skip: &[&str]) -> io::IoResult<()> {
        let loc = TempDir::new("rsar").unwrap();

        // First, extract the contents of the archive to a temporary directory
        let archive = os::make_absolute(archive);
        run_ar(self.sess, "x", Some(loc.path()), [&archive]);

        // Next, we must rename all of the inputs to "guaranteed unique names".
        // The reason for this is that archives are keyed off the name of the
        // files, so if two files have the same name they will override one
        // another in the archive (bad).
        //
        // We skip any files explicitly desired for skipping, and we also skip
        // all SYMDEF files as these are just magical placeholders which get
        // re-created when we make a new archive anyway.
        let files = try!(fs::readdir(loc.path()));
        let mut inputs = Vec::new();
        for file in files.iter() {
            let filename = file.filename_str().unwrap();
            if skip.iter().any(|s| *s == filename) { continue }
            if filename.contains(".SYMDEF") { continue }

            let filename = format!("r-{}-{}", name, filename);
            let new_filename = file.with_filename(filename);
            try!(fs::rename(file, &new_filename));
            inputs.push(new_filename);
        }
        if inputs.len() == 0 { return Ok(()) }

        // Finally, add all the renamed files to this archive
        let mut args = vec!(&self.dst);
        args.extend(&mut inputs.iter());
        run_ar(self.sess, "r", None, args.as_slice());
        Ok(())
    }

    fn find_library(&self, name: &str) -> Path {
        let (osprefix, osext) = match self.sess.targ_cfg.os {
            abi::OsWin32 => ("", "lib"), _ => ("lib", "a"),
        };
        // On Windows, static libraries sometimes show up as libfoo.a and other
        // times show up as foo.lib
        let oslibname = format!("{}{}.{}", osprefix, name, osext);
        let unixlibname = format!("lib{}.a", name);

        let mut rustpath = filesearch::rust_path();
        rustpath.push(self.sess.filesearch().get_target_lib_path());
        let search = self.sess.opts.addl_lib_search_paths.borrow();
        for path in search.iter().chain(rustpath.iter()) {
            debug!("looking for {} inside {}", name, path.display());
            let test = path.join(oslibname.as_slice());
            if test.exists() { return test }
            if oslibname != unixlibname {
                let test = path.join(unixlibname.as_slice());
                if test.exists() { return test }
            }
        }
        self.sess.fatal(format!("could not find native static library `{}`, \
                                 perhaps an -L flag is missing?", name));
    }
}

impl ArchiveRO {
    /// Opens a static archive for read-only purposes. This is more optimized
    /// than the `open` method because it uses LLVM's internal `Archive` class
    /// rather than shelling out to `ar` for everything.
    ///
    /// If this archive is used with a mutable method, then an error will be
    /// raised.
    pub fn open(dst: &Path) -> Option<ArchiveRO> {
        unsafe {
            let ar = dst.with_c_str(|dst| {
                llvm::LLVMRustOpenArchive(dst)
            });
            if ar.is_null() {
                None
            } else {
                Some(ArchiveRO { ptr: ar })
            }
        }
    }

    /// Reads a file in the archive
    pub fn read<'a>(&'a self, file: &str) -> Option<&'a [u8]> {
        unsafe {
            let mut size = 0 as libc::size_t;
            let ptr = file.with_c_str(|file| {
                llvm::LLVMRustArchiveReadSection(self.ptr, file, &mut size)
            });
            if ptr.is_null() {
                None
            } else {
                Some(cast::transmute(raw::Slice {
                    data: ptr,
                    len: size as uint,
                }))
            }
        }
    }
}

impl Drop for ArchiveRO {
    fn drop(&mut self) {
        unsafe {
            llvm::LLVMRustDestroyArchive(self.ptr);
        }
    }
}
<commit_msg>Cleanup fixed issue #10734 workaround<commit_after>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A helper class for dealing with static archives

use back::link::{get_ar_prog};
use driver::session::Session;
use metadata::filesearch;
use lib::llvm::{ArchiveRef, llvm};

use std::cast;
use std::io;
use std::io::{fs, TempDir};
use std::libc;
use std::os;
use std::io::process::{ProcessConfig, Process, ProcessOutput};
use std::str;
use std::raw;
use syntax::abi;

pub static METADATA_FILENAME: &'static str = "rust.metadata.bin";

pub struct Archive<'a> {
    priv sess: &'a Session,
    priv dst: Path,
}

pub struct ArchiveRO {
    priv ptr: ArchiveRef,
}

fn run_ar(sess: &Session, args: &str, cwd: Option<&Path>,
          paths: &[&Path]) -> ProcessOutput {
    let ar = get_ar_prog(sess);

    let mut args = vec!(args.to_owned());
    let mut paths = paths.iter().map(|p| p.as_str().unwrap().to_owned());
    args.extend(&mut paths);
    debug!("{} {}", ar, args.connect(" "));
    match cwd {
        Some(p) => { debug!("inside {}", p.display()); }
        None => {}
    }
    match Process::configure(ProcessConfig {
        program: ar.as_slice(),
        args: args.as_slice(),
        cwd: cwd.map(|a| &*a),
        .. ProcessConfig::new()
    }) {
        Ok(mut prog) => {
            let o = prog.wait_with_output();
            if !o.status.success() {
                sess.err(format!("{} {} failed with: {}", ar, args.connect(" "),
                                 o.status));
                sess.note(format!("stdout ---\n{}", str::from_utf8(o.output).unwrap()));
                sess.note(format!("stderr ---\n{}", str::from_utf8(o.error).unwrap()));
                sess.abort_if_errors();
            }
            o
        },
        Err(e) => {
            sess.err(format!("could not exec `{}`: {}", ar, e));
            sess.abort_if_errors();
            fail!("rustc::back::archive::run_ar() should not reach this point");
        }
    }
}

impl<'a> Archive<'a> {
    /// Initializes a new static archive with the given object file
    pub fn create<'b>(sess: &'a Session, dst: &'b Path,
                      initial_object: &'b Path) -> Archive<'a> {
        run_ar(sess, "crus", None, [dst, initial_object]);
        Archive { sess: sess, dst: dst.clone() }
    }

    /// Opens an existing static archive
    pub fn open(sess: &'a Session, dst: Path) -> Archive<'a> {
        assert!(dst.exists());
        Archive { sess: sess, dst: dst }
    }

    /// Read a file in the archive
    pub fn read(&self, file: &str) -> Vec<u8> {
        // Apparently if "ar p" is used on windows, it generates a corrupt file
        // which has bad headers and LLVM will immediately choke on it
        if cfg!(windows) {
            let loc = TempDir::new("rsar").unwrap();
            let archive = os::make_absolute(&self.dst);
            run_ar(self.sess, "x", Some(loc.path()), [&archive,
                                                      &Path::new(file)]);
            let result: Vec<u8> =
                fs::File::open(&loc.path().join(file)).read_to_end()
                                                      .unwrap()
                                                      .move_iter()
                                                      .collect();
            result
        } else {
            run_ar(self.sess,
                   "p",
                   None,
                   [&self.dst, &Path::new(file)]).output.move_iter().collect()
        }
    }

    /// Adds all of the contents of a native library to this archive. This will
    /// search in the relevant locations for a library named `name`.
    pub fn add_native_library(&mut self, name: &str) -> io::IoResult<()> {
        let location = self.find_library(name);
        self.add_archive(&location, name, [])
    }

    /// Adds all of the contents of the rlib at the specified path to this
    /// archive.
    ///
    /// This ignores adding the bytecode from the rlib, and if LTO is enabled
    /// then the object file also isn't added.
    pub fn add_rlib(&mut self, rlib: &Path, name: &str,
                    lto: bool) -> io::IoResult<()> {
        let object = format!("{}.o", name);
        let bytecode = format!("{}.bc", name);
        let mut ignore = vec!(METADATA_FILENAME, bytecode.as_slice());
        if lto {
            ignore.push(object.as_slice());
        }
        self.add_archive(rlib, name, ignore.as_slice())
    }

    /// Adds an arbitrary file to this archive
    pub fn add_file(&mut self, file: &Path, has_symbols: bool) {
        let cmd = if has_symbols {"r"} else {"rS"};
        run_ar(self.sess, cmd, None, [&self.dst, file]);
    }

    /// Removes a file from this archive
    pub fn remove_file(&mut self, file: &str) {
        run_ar(self.sess, "d", None, [&self.dst, &Path::new(file)]);
    }

    /// Updates all symbols in the archive (runs 'ar s' over it)
    pub fn update_symbols(&mut self) {
        run_ar(self.sess, "s", None, [&self.dst]);
    }

    /// Lists all files in an archive
    pub fn files(&self) -> Vec<~str> {
        let output = run_ar(self.sess, "t", None, [&self.dst]);
        let output = str::from_utf8(output.output).unwrap();
        // use lines_any because windows delimits output with `\r\n` instead of
        // just `\n`
        output.lines_any().map(|s| s.to_owned()).collect()
    }

    fn add_archive(&mut self, archive: &Path, name: &str,
                   skip: &[&str]) -> io::IoResult<()> {
        let loc = TempDir::new("rsar").unwrap();

        // First, extract the contents of the archive to a temporary directory
        let archive = os::make_absolute(archive);
        run_ar(self.sess, "x", Some(loc.path()), [&archive]);

        // Next, we must rename all of the inputs to "guaranteed unique names".
        // The reason for this is that archives are keyed off the name of the
        // files, so if two files have the same name they will override one
        // another in the archive (bad).
        //
        // We skip any files explicitly desired for skipping, and we also skip
        // all SYMDEF files as these are just magical placeholders which get
        // re-created when we make a new archive anyway.
        let files = try!(fs::readdir(loc.path()));
        let mut inputs = Vec::new();
        for file in files.iter() {
            let filename = file.filename_str().unwrap();
            if skip.iter().any(|s| *s == filename) { continue }
            if filename.contains(".SYMDEF") { continue }

            let filename = format!("r-{}-{}", name, filename);
            let new_filename = file.with_filename(filename);
            try!(fs::rename(file, &new_filename));
            inputs.push(new_filename);
        }
        if inputs.len() == 0 { return Ok(()) }

        // Finally, add all the renamed files to this archive
        let mut args = vec!(&self.dst);
        args.extend(&mut inputs.iter());
        run_ar(self.sess, "r", None, args.as_slice());
        Ok(())
    }

    fn find_library(&self, name: &str) -> Path {
        let (osprefix, osext) = match self.sess.targ_cfg.os {
            abi::OsWin32 => ("", "lib"), _ => ("lib", "a"),
        };
        // On Windows, static libraries sometimes show up as libfoo.a and other
        // times show up as foo.lib
        let oslibname = format!("{}{}.{}", osprefix, name, osext);
        let unixlibname = format!("lib{}.a", name);

        let mut rustpath = filesearch::rust_path();
        rustpath.push(self.sess.filesearch().get_target_lib_path());
        let search = self.sess.opts.addl_lib_search_paths.borrow();
        for path in search.iter().chain(rustpath.iter()) {
            debug!("looking for {} inside {}", name, path.display());
            let test = path.join(oslibname.as_slice());
            if test.exists() { return test }
            if oslibname != unixlibname {
                let test = path.join(unixlibname.as_slice());
                if test.exists() { return test }
            }
        }
        self.sess.fatal(format!("could not find native static library `{}`, \
                                 perhaps an -L flag is missing?", name));
    }
}

impl ArchiveRO {
    /// Opens a static archive for read-only purposes. This is more optimized
    /// than the `open` method because it uses LLVM's internal `Archive` class
    /// rather than shelling out to `ar` for everything.
    ///
    /// If this archive is used with a mutable method, then an error will be
    /// raised.
    pub fn open(dst: &Path) -> Option<ArchiveRO> {
        unsafe {
            let ar = dst.with_c_str(|dst| {
                llvm::LLVMRustOpenArchive(dst)
            });
            if ar.is_null() {
                None
            } else {
                Some(ArchiveRO { ptr: ar })
            }
        }
    }

    /// Reads a file in the archive
    pub fn read<'a>(&'a self, file: &str) -> Option<&'a [u8]> {
        unsafe {
            let mut size = 0 as libc::size_t;
            let ptr = file.with_c_str(|file| {
                llvm::LLVMRustArchiveReadSection(self.ptr, file, &mut size)
            });
            if ptr.is_null() {
                None
            } else {
                Some(cast::transmute(raw::Slice {
                    data: ptr,
                    len: size as uint,
                }))
            }
        }
    }
}

impl Drop for ArchiveRO {
    fn drop(&mut self) {
        unsafe {
            llvm::LLVMRustDestroyArchive(self.ptr);
        }
    }
}
<|endoftext|>


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

//! A helper class for dealing with static archives

use back::link::{get_ar_prog};
use driver::session::Session;
use metadata::filesearch;
use lib::llvm::{ArchiveRef, llvm};

use std::cast;
use std::io::fs;
use std::io;
use std::libc;
use std::os;
use std::io::process::{ProcessConfig, Process, ProcessOutput};
use std::str;
use std::raw;
use extra::tempfile::TempDir;
use syntax::abi;

pub static METADATA_FILENAME: &'static str = "rust.metadata.bin";

pub struct Archive {
    priv sess: Session,
    priv dst: Path,
}

pub struct ArchiveRO {
    priv ptr: ArchiveRef,
}

fn run_ar(sess: Session, args: &str, cwd: Option<&Path>,
        paths: &[&Path]) -> ProcessOutput {
    let ar = get_ar_prog(sess);

    let mut args = ~[args.to_owned()];
    let mut paths = paths.iter().map(|p| p.as_str().unwrap().to_owned());
    args.extend(&mut paths);
    debug!("{} {}", ar, args.connect(" "));
    match cwd {
        Some(p) => { debug!("inside {}", p.display()); }
        None => {}
    }
    match Process::configure(ProcessConfig {
        program: ar.as_slice(),
        args: args.as_slice(),
        cwd: cwd.map(|a| &*a),
        .. ProcessConfig::new()
    }) {
        Ok(mut prog) => {
            let o = prog.wait_with_output();
            if !o.status.success() {
                sess.err(format!("{} {} failed with: {}", ar, args.connect(" "),
                                 o.status));
                sess.note(format!("stdout ---\n{}", str::from_utf8(o.output).unwrap()));
                sess.note(format!("stderr ---\n{}", str::from_utf8(o.error).unwrap()));
                sess.abort_if_errors();
            }
            o
        },
        Err(e) => {
            sess.err(format!("could not exec `{}`: {}", ar, e));
            sess.abort_if_errors();
            fail!("rustc::back::archive::run_ar() should not reach this point");
        }
    }
}

impl Archive {
    /// Initializes a new static archive with the given object file
    pub fn create<'a>(sess: Session, dst: &'a Path,
                      initial_object: &'a Path) -> Archive {
        run_ar(sess, "crus", None, [dst, initial_object]);
        Archive { sess: sess, dst: dst.clone() }
    }

    /// Opens an existing static archive
    pub fn open(sess: Session, dst: Path) -> Archive {
        assert!(dst.exists());
        Archive { sess: sess, dst: dst }
    }

    /// Read a file in the archive
    pub fn read(&self, file: &str) -> ~[u8] {
        // Apparently if "ar p" is used on windows, it generates a corrupt file
        // which has bad headers and LLVM will immediately choke on it
        if cfg!(windows) && cfg!(windows) { // FIXME(#10734) double-and
            let loc = TempDir::new("rsar").unwrap();
            let archive = os::make_absolute(&self.dst);
            run_ar(self.sess, "x", Some(loc.path()), [&archive,
                                                      &Path::new(file)]);
            fs::File::open(&loc.path().join(file)).read_to_end().unwrap()
        } else {
            run_ar(self.sess, "p", None, [&self.dst, &Path::new(file)]).output
        }
    }

    /// Adds all of the contents of a native library to this archive. This will
    /// search in the relevant locations for a library named `name`.
    pub fn add_native_library(&mut self, name: &str) -> io::IoResult<()> {
        let location = self.find_library(name);
        self.add_archive(&location, name, [])
    }

    /// Adds all of the contents of the rlib at the specified path to this
    /// archive.
    ///
    /// This ignores adding the bytecode from the rlib, and if LTO is enabled
    /// then the object file also isn't added.
    pub fn add_rlib(&mut self, rlib: &Path, name: &str,
                    lto: bool) -> io::IoResult<()> {
        let object = format!("{}.o", name);
        let bytecode = format!("{}.bc", name);
        let mut ignore = ~[METADATA_FILENAME, bytecode.as_slice()];
        if lto {
            ignore.push(object.as_slice());
        }
        self.add_archive(rlib, name, ignore)
    }

    /// Adds an arbitrary file to this archive
    pub fn add_file(&mut self, file: &Path, has_symbols: bool) {
        let cmd = if has_symbols {"r"} else {"rS"};
        run_ar(self.sess, cmd, None, [&self.dst, file]);
    }

    /// Removes a file from this archive
    pub fn remove_file(&mut self, file: &str) {
        run_ar(self.sess, "d", None, [&self.dst, &Path::new(file)]);
    }

    /// Updates all symbols in the archive (runs 'ar s' over it)
    pub fn update_symbols(&mut self) {
        run_ar(self.sess, "s", None, [&self.dst]);
    }

    /// Lists all files in an archive
    pub fn files(&self) -> ~[~str] {
        let output = run_ar(self.sess, "t", None, [&self.dst]);
        str::from_utf8(output.output).unwrap().lines().map(|s| s.to_owned()).collect()
    }

    fn add_archive(&mut self, archive: &Path, name: &str,
                   skip: &[&str]) -> io::IoResult<()> {
        let loc = TempDir::new("rsar").unwrap();

        // First, extract the contents of the archive to a temporary directory
        let archive = os::make_absolute(archive);
        run_ar(self.sess, "x", Some(loc.path()), [&archive]);

        // Next, we must rename all of the inputs to "guaranteed unique names".
        // The reason for this is that archives are keyed off the name of the
        // files, so if two files have the same name they will override one
        // another in the archive (bad).
        //
        // We skip any files explicitly desired for skipping, and we also skip
        // all SYMDEF files as these are just magical placeholders which get
        // re-created when we make a new archive anyway.
        let files = try!(fs::readdir(loc.path()));
        let mut inputs = ~[];
        for file in files.iter() {
            let filename = file.filename_str().unwrap();
            if skip.iter().any(|s| *s == filename) { continue }
            if filename.contains(".SYMDEF") { continue }

            let filename = format!("r-{}-{}", name, filename);
            let new_filename = file.with_filename(filename);
            try!(fs::rename(file, &new_filename));
            inputs.push(new_filename);
        }
        if inputs.len() == 0 { return Ok(()) }

        // Finally, add all the renamed files to this archive
        let mut args = ~[&self.dst];
        args.extend(&mut inputs.iter());
        run_ar(self.sess, "r", None, args.as_slice());
        Ok(())
    }

    fn find_library(&self, name: &str) -> Path {
        let (osprefix, osext) = match self.sess.targ_cfg.os {
            abi::OsWin32 => ("", "lib"), _ => ("lib", "a"),
        };
        // On Windows, static libraries sometimes show up as libfoo.a and other
        // times show up as foo.lib
        let oslibname = format!("{}{}.{}", osprefix, name, osext);
        let unixlibname = format!("lib{}.a", name);

        let mut rustpath = filesearch::rust_path();
        rustpath.push(self.sess.filesearch.get_target_lib_path());
        let addl_lib_search_paths = self.sess
                                        .opts
                                        .addl_lib_search_paths
                                        .borrow();
        let path = addl_lib_search_paths.get().iter();
        for path in path.chain(rustpath.iter()) {
            debug!("looking for {} inside {}", name, path.display());
            let test = path.join(oslibname.as_slice());
            if test.exists() { return test }
            if oslibname != unixlibname {
                let test = path.join(unixlibname.as_slice());
                if test.exists() { return test }
            }
        }
        self.sess.fatal(format!("could not find native static library `{}`, \
                                 perhaps an -L flag is missing?", name));
    }
}

impl ArchiveRO {
    /// Opens a static archive for read-only purposes. This is more optimized
    /// than the `open` method because it uses LLVM's internal `Archive` class
    /// rather than shelling out to `ar` for everything.
    ///
    /// If this archive is used with a mutable method, then an error will be
    /// raised.
    pub fn open(dst: &Path) -> Option<ArchiveRO> {
        unsafe {
            let ar = dst.with_c_str(|dst| {
                llvm::LLVMRustOpenArchive(dst)
            });
            if ar.is_null() {
                None
            } else {
                Some(ArchiveRO { ptr: ar })
            }
        }
    }

    /// Reads a file in the archive
    pub fn read<'a>(&'a self, file: &str) -> Option<&'a [u8]> {
        unsafe {
            let mut size = 0 as libc::size_t;
            let ptr = file.with_c_str(|file| {
                llvm::LLVMRustArchiveReadSection(self.ptr, file, &mut size)
            });
            if ptr.is_null() {
                None
            } else {
                Some(cast::transmute(raw::Slice {
                    data: ptr,
                    len: size as uint,
                }))
            }
        }
    }
}

impl Drop for ArchiveRO {
    fn drop(&mut self) {
        unsafe {
            llvm::LLVMRustDestroyArchive(self.ptr);
        }
    }
}
<commit_msg>Use lines_any() when parsing output form "ar"<commit_after>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A helper class for dealing with static archives

use back::link::{get_ar_prog};
use driver::session::Session;
use metadata::filesearch;
use lib::llvm::{ArchiveRef, llvm};

use std::cast;
use std::io::fs;
use std::io;
use std::libc;
use std::os;
use std::io::process::{ProcessConfig, Process, ProcessOutput};
use std::str;
use std::raw;
use extra::tempfile::TempDir;
use syntax::abi;

pub static METADATA_FILENAME: &'static str = "rust.metadata.bin";

pub struct Archive {
    priv sess: Session,
    priv dst: Path,
}

pub struct ArchiveRO {
    priv ptr: ArchiveRef,
}

fn run_ar(sess: Session, args: &str, cwd: Option<&Path>,
        paths: &[&Path]) -> ProcessOutput {
    let ar = get_ar_prog(sess);

    let mut args = ~[args.to_owned()];
    let mut paths = paths.iter().map(|p| p.as_str().unwrap().to_owned());
    args.extend(&mut paths);
    debug!("{} {}", ar, args.connect(" "));
    match cwd {
        Some(p) => { debug!("inside {}", p.display()); }
        None => {}
    }
    match Process::configure(ProcessConfig {
        program: ar.as_slice(),
        args: args.as_slice(),
        cwd: cwd.map(|a| &*a),
        .. ProcessConfig::new()
    }) {
        Ok(mut prog) => {
            let o = prog.wait_with_output();
            if !o.status.success() {
                sess.err(format!("{} {} failed with: {}", ar, args.connect(" "),
                                 o.status));
                sess.note(format!("stdout ---\n{}", str::from_utf8(o.output).unwrap()));
                sess.note(format!("stderr ---\n{}", str::from_utf8(o.error).unwrap()));
                sess.abort_if_errors();
            }
            o
        },
        Err(e) => {
            sess.err(format!("could not exec `{}`: {}", ar, e));
            sess.abort_if_errors();
            fail!("rustc::back::archive::run_ar() should not reach this point");
        }
    }
}

impl Archive {
    /// Initializes a new static archive with the given object file
    pub fn create<'a>(sess: Session, dst: &'a Path,
                      initial_object: &'a Path) -> Archive {
        run_ar(sess, "crus", None, [dst, initial_object]);
        Archive { sess: sess, dst: dst.clone() }
    }

    /// Opens an existing static archive
    pub fn open(sess: Session, dst: Path) -> Archive {
        assert!(dst.exists());
        Archive { sess: sess, dst: dst }
    }

    /// Read a file in the archive
    pub fn read(&self, file: &str) -> ~[u8] {
        // Apparently if "ar p" is used on windows, it generates a corrupt file
        // which has bad headers and LLVM will immediately choke on it
        if cfg!(windows) && cfg!(windows) { // FIXME(#10734) double-and
            let loc = TempDir::new("rsar").unwrap();
            let archive = os::make_absolute(&self.dst);
            run_ar(self.sess, "x", Some(loc.path()), [&archive,
                                                      &Path::new(file)]);
            fs::File::open(&loc.path().join(file)).read_to_end().unwrap()
        } else {
            run_ar(self.sess, "p", None, [&self.dst, &Path::new(file)]).output
        }
    }

    /// Adds all of the contents of a native library to this archive. This will
    /// search in the relevant locations for a library named `name`.
    pub fn add_native_library(&mut self, name: &str) -> io::IoResult<()> {
        let location = self.find_library(name);
        self.add_archive(&location, name, [])
    }

    /// Adds all of the contents of the rlib at the specified path to this
    /// archive.
    ///
    /// This ignores adding the bytecode from the rlib, and if LTO is enabled
    /// then the object file also isn't added.
    pub fn add_rlib(&mut self, rlib: &Path, name: &str,
                    lto: bool) -> io::IoResult<()> {
        let object = format!("{}.o", name);
        let bytecode = format!("{}.bc", name);
        let mut ignore = ~[METADATA_FILENAME, bytecode.as_slice()];
        if lto {
            ignore.push(object.as_slice());
        }
        self.add_archive(rlib, name, ignore)
    }

    /// Adds an arbitrary file to this archive
    pub fn add_file(&mut self, file: &Path, has_symbols: bool) {
        let cmd = if has_symbols {"r"} else {"rS"};
        run_ar(self.sess, cmd, None, [&self.dst, file]);
    }

    /// Removes a file from this archive
    pub fn remove_file(&mut self, file: &str) {
        run_ar(self.sess, "d", None, [&self.dst, &Path::new(file)]);
    }

    /// Updates all symbols in the archive (runs 'ar s' over it)
    pub fn update_symbols(&mut self) {
        run_ar(self.sess, "s", None, [&self.dst]);
    }

    /// Lists all files in an archive
    pub fn files(&self) -> ~[~str] {
        let output = run_ar(self.sess, "t", None, [&self.dst]);
        let output = str::from_utf8(output.output).unwrap();
        // use lines_any because windows delimits output with `\r\n` instead of
        // just `\n`
        output.lines_any().map(|s| s.to_owned()).collect()
    }

    fn add_archive(&mut self, archive: &Path, name: &str,
                   skip: &[&str]) -> io::IoResult<()> {
        let loc = TempDir::new("rsar").unwrap();

        // First, extract the contents of the archive to a temporary directory
        let archive = os::make_absolute(archive);
        run_ar(self.sess, "x", Some(loc.path()), [&archive]);

        // Next, we must rename all of the inputs to "guaranteed unique names".
        // The reason for this is that archives are keyed off the name of the
        // files, so if two files have the same name they will override one
        // another in the archive (bad).
        //
        // We skip any files explicitly desired for skipping, and we also skip
        // all SYMDEF files as these are just magical placeholders which get
        // re-created when we make a new archive anyway.
        let files = try!(fs::readdir(loc.path()));
        let mut inputs = ~[];
        for file in files.iter() {
            let filename = file.filename_str().unwrap();
            if skip.iter().any(|s| *s == filename) { continue }
            if filename.contains(".SYMDEF") { continue }

            let filename = format!("r-{}-{}", name, filename);
            let new_filename = file.with_filename(filename);
            try!(fs::rename(file, &new_filename));
            inputs.push(new_filename);
        }
        if inputs.len() == 0 { return Ok(()) }

        // Finally, add all the renamed files to this archive
        let mut args = ~[&self.dst];
        args.extend(&mut inputs.iter());
        run_ar(self.sess, "r", None, args.as_slice());
        Ok(())
    }

    fn find_library(&self, name: &str) -> Path {
        let (osprefix, osext) = match self.sess.targ_cfg.os {
            abi::OsWin32 => ("", "lib"), _ => ("lib", "a"),
        };
        // On Windows, static libraries sometimes show up as libfoo.a and other
        // times show up as foo.lib
        let oslibname = format!("{}{}.{}", osprefix, name, osext);
        let unixlibname = format!("lib{}.a", name);

        let mut rustpath = filesearch::rust_path();
        rustpath.push(self.sess.filesearch.get_target_lib_path());
        let addl_lib_search_paths = self.sess
                                        .opts
                                        .addl_lib_search_paths
                                        .borrow();
        let path = addl_lib_search_paths.get().iter();
        for path in path.chain(rustpath.iter()) {
            debug!("looking for {} inside {}", name, path.display());
            let test = path.join(oslibname.as_slice());
            if test.exists() { return test }
            if oslibname != unixlibname {
                let test = path.join(unixlibname.as_slice());
                if test.exists() { return test }
            }
        }
        self.sess.fatal(format!("could not find native static library `{}`, \
                                 perhaps an -L flag is missing?", name));
    }
}

impl ArchiveRO {
    /// Opens a static archive for read-only purposes. This is more optimized
    /// than the `open` method because it uses LLVM's internal `Archive` class
    /// rather than shelling out to `ar` for everything.
    ///
    /// If this archive is used with a mutable method, then an error will be
    /// raised.
    pub fn open(dst: &Path) -> Option<ArchiveRO> {
        unsafe {
            let ar = dst.with_c_str(|dst| {
                llvm::LLVMRustOpenArchive(dst)
            });
            if ar.is_null() {
                None
            } else {
                Some(ArchiveRO { ptr: ar })
            }
        }
    }

    /// Reads a file in the archive
    pub fn read<'a>(&'a self, file: &str) -> Option<&'a [u8]> {
        unsafe {
            let mut size = 0 as libc::size_t;
            let ptr = file.with_c_str(|file| {
                llvm::LLVMRustArchiveReadSection(self.ptr, file, &mut size)
            });
            if ptr.is_null() {
                None
            } else {
                Some(cast::transmute(raw::Slice {
                    data: ptr,
                    len: size as uint,
                }))
            }
        }
    }
}

impl Drop for ArchiveRO {
    fn drop(&mut self) {
        unsafe {
            llvm::LLVMRustDestroyArchive(self.ptr);
        }
    }
}
<|endoftext|>


<commit_before>use crate::ty::query::Providers;
use crate::hir::def_id::DefId;
use crate::hir;
use crate::ty::TyCtxt;
use syntax_pos::symbol::{sym, Symbol};
use rustc_target::spec::abi::Abi;
use crate::hir::map::blocks::FnLikeNode;
use syntax::attr;

impl<'tcx> TyCtxt<'tcx> {
    /// Whether the `def_id` counts as const fn in your current crate, considering all active
    /// feature gates
    pub fn is_const_fn(self, def_id: DefId) -> bool {
        self.is_const_fn_raw(def_id) && match self.is_unstable_const_fn(def_id) {
            Some(feature_name) => {
                // has a `rustc_const_unstable` attribute, check whether the user enabled the
                // corresponding feature gate.
                self.features()
                    .declared_lib_features
                    .iter()
                    .any(|&(sym, _)| sym == feature_name)
            },
            // functions without const stability are either stable user written
            // const fn or the user is using feature gates and we thus don't
            // care what they do
            None => true,
        }
    }

    /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
    pub fn is_unstable_const_fn(self, def_id: DefId) -> Option<Symbol> {
        if self.is_const_fn_raw(def_id) {
            self.lookup_stability(def_id)?.const_stability
        } else {
            None
        }
    }

    /// Returns `true` if the `def_id` refers to an intrisic which we've whitelisted
    /// for being called from stable `const fn`s (`min_const_fn`).
    ///
    /// Adding more intrinsics requires sign-off from @rust-lang/lang.
    ///
    /// This list differs from the list in `is_const_intrinsic` in the sense that any item on this
    /// list must be on the `is_const_intrinsic` list, too, because if an intrinsic is callable from
    /// stable, it must be callable at all.
    fn is_intrinsic_min_const_fn(self, def_id: DefId) -> bool {
        match self.item_name(def_id) {
            | sym::size_of
            | sym::min_align_of
            | sym::needs_drop
            // Arithmetic:
            | sym::add_with_overflow // ~> .overflowing_add
            | sym::sub_with_overflow // ~> .overflowing_sub
            | sym::mul_with_overflow // ~> .overflowing_mul
            | sym::wrapping_add // ~> .wrapping_add
            | sym::wrapping_sub // ~> .wrapping_sub
            | sym::wrapping_mul // ~> .wrapping_mul
            | sym::saturating_add // ~> .saturating_add
            | sym::saturating_sub // ~> .saturating_sub
            | sym::unchecked_shl // ~> .wrapping_shl
            | sym::unchecked_shr // ~> .wrapping_shr
            | sym::rotate_left // ~> .rotate_left
            | sym::rotate_right // ~> .rotate_right
            | sym::ctpop // ~> .count_ones
            | sym::ctlz // ~> .leading_zeros
            | sym::cttz // ~> .trailing_zeros
            | sym::bswap // ~> .swap_bytes
            | sym::bitreverse // ~> .reverse_bits
            => true,
            _ => false,
        }
    }

    /// Returns `true` if this function must conform to `min_const_fn`
    pub fn is_min_const_fn(self, def_id: DefId) -> bool {
        // Bail out if the signature doesn't contain `const`
        if !self.is_const_fn_raw(def_id) {
            return false;
        }
        if let Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
            return self.is_intrinsic_min_const_fn(def_id);
        }

        if self.features().staged_api {
            // in order for a libstd function to be considered min_const_fn
            // it needs to be stable and have no `rustc_const_unstable` attribute
            match self.lookup_stability(def_id) {
                // stable functions with unstable const fn aren't `min_const_fn`
                Some(&attr::Stability { const_stability: Some(_), .. }) => false,
                // unstable functions don't need to conform
                Some(&attr::Stability { ref level, .. }) if level.is_unstable() => false,
                // everything else needs to conform, because it would be callable from
                // other `min_const_fn` functions
                _ => true,
            }
        } else {
            // users enabling the `const_fn` feature gate can do what they want
            !self.features().const_fn
        }
    }
}

pub fn provide(providers: &mut Providers<'_>) {
    /// Const evaluability whitelist is here to check evaluability at the
    /// top level beforehand.
    fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
        match tcx.fn_sig(def_id).abi() {
            Abi::RustIntrinsic |
            Abi::PlatformIntrinsic => {
                match tcx.item_name(def_id) {
                    // Keep this list in the same order as the match patterns in
                    // `librustc_mir/interpret/intrinsics.rs`

                    // This whitelist is a list of intrinsics that have a miri-engine implementation
                    // and can thus be called when enabling enough feature gates. The similar
                    // whitelist in `is_intrinsic_min_const_fn` (in this file), exists for allowing
                    // the intrinsics to be transitively called by stable const fns.
                    | sym::caller_location

                    | sym::min_align_of
                    | sym::pref_align_of
                    | sym::needs_drop
                    | sym::size_of
                    | sym::type_id
                    | sym::type_name

                    | sym::ctpop
                    | sym::cttz
                    | sym::cttz_nonzero
                    | sym::ctlz
                    | sym::ctlz_nonzero
                    | sym::bswap
                    | sym::bitreverse

                    | sym::wrapping_add
                    | sym::wrapping_sub
                    | sym::wrapping_mul
                    | sym::add_with_overflow
                    | sym::sub_with_overflow
                    | sym::mul_with_overflow

                    | sym::saturating_add
                    | sym::saturating_sub

                    | sym::unchecked_shl
                    | sym::unchecked_shr

                    | sym::rotate_left
                    | sym::rotate_right

                    | sym::ptr_offset_from

                    | sym::transmute

                    | sym::simd_insert

                    | sym::simd_extract

                    => Some(true),

                    _ => Some(false)
                }
            }
            _ => None
        }
    }

    /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
    /// said intrinsic is on the whitelist for being const callable.
    fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
        let hir_id = tcx.hir().as_local_hir_id(def_id)
                              .expect("Non-local call to local provider is_const_fn");

        let node = tcx.hir().get(hir_id);

        if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
            whitelisted
        } else if let Some(fn_like) = FnLikeNode::from_node(node) {
            fn_like.constness() == hir::Constness::Const
        } else if let hir::Node::Ctor(_) = node {
            true
        } else {
            false
        }
    }

    fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
        tcx.is_const_fn(def_id) && match tcx.lookup_stability(def_id) {
            Some(stab) => {
                if cfg!(debug_assertions) && stab.promotable {
                    let sig = tcx.fn_sig(def_id);
                    assert_eq!(
                        sig.unsafety(),
                        hir::Unsafety::Normal,
                        "don't mark const unsafe fns as promotable",
                        // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
                    );
                }
                stab.promotable
            },
            None => false,
        }
    }

    fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
        tcx.is_const_fn(def_id) &&
            tcx.lookup_stability(def_id)
                .map(|stab| stab.allow_const_fn_ptr).unwrap_or(false)
    }

    *providers = Providers {
        is_const_fn_raw,
        is_promotable_const_fn,
        const_fn_is_allowed_fn_ptr,
        ..*providers
    };
}
<commit_msg>const fns can read constants that invoke unstable const fns<commit_after>use crate::ty::query::Providers;
use crate::hir::def_id::DefId;
use crate::hir;
use crate::ty::TyCtxt;
use syntax_pos::symbol::{sym, Symbol};
use rustc_target::spec::abi::Abi;
use crate::hir::map::blocks::FnLikeNode;
use syntax::attr;

impl<'tcx> TyCtxt<'tcx> {
    /// Whether the `def_id` counts as const fn in your current crate, considering all active
    /// feature gates
    pub fn is_const_fn(self, def_id: DefId) -> bool {
        self.is_const_fn_raw(def_id) && match self.is_unstable_const_fn(def_id) {
            Some(feature_name) => {
                // has a `rustc_const_unstable` attribute, check whether the user enabled the
                // corresponding feature gate.
                self.features()
                    .declared_lib_features
                    .iter()
                    .any(|&(sym, _)| sym == feature_name)
            },
            // functions without const stability are either stable user written
            // const fn or the user is using feature gates and we thus don't
            // care what they do
            None => true,
        }
    }

    /// Whether the `def_id` is an unstable const fn and what feature gate is necessary to enable it
    pub fn is_unstable_const_fn(self, def_id: DefId) -> Option<Symbol> {
        if self.is_const_fn_raw(def_id) {
            self.lookup_stability(def_id)?.const_stability
        } else {
            None
        }
    }

    /// Returns `true` if the `def_id` refers to an intrisic which we've whitelisted
    /// for being called from stable `const fn`s (`min_const_fn`).
    ///
    /// Adding more intrinsics requires sign-off from @rust-lang/lang.
    ///
    /// This list differs from the list in `is_const_intrinsic` in the sense that any item on this
    /// list must be on the `is_const_intrinsic` list, too, because if an intrinsic is callable from
    /// stable, it must be callable at all.
    fn is_intrinsic_min_const_fn(self, def_id: DefId) -> bool {
        match self.item_name(def_id) {
            | sym::size_of
            | sym::min_align_of
            | sym::needs_drop
            // Arithmetic:
            | sym::add_with_overflow // ~> .overflowing_add
            | sym::sub_with_overflow // ~> .overflowing_sub
            | sym::mul_with_overflow // ~> .overflowing_mul
            | sym::wrapping_add // ~> .wrapping_add
            | sym::wrapping_sub // ~> .wrapping_sub
            | sym::wrapping_mul // ~> .wrapping_mul
            | sym::saturating_add // ~> .saturating_add
            | sym::saturating_sub // ~> .saturating_sub
            | sym::unchecked_shl // ~> .wrapping_shl
            | sym::unchecked_shr // ~> .wrapping_shr
            | sym::rotate_left // ~> .rotate_left
            | sym::rotate_right // ~> .rotate_right
            | sym::ctpop // ~> .count_ones
            | sym::ctlz // ~> .leading_zeros
            | sym::cttz // ~> .trailing_zeros
            | sym::bswap // ~> .swap_bytes
            | sym::bitreverse // ~> .reverse_bits
            => true,
            _ => false,
        }
    }

    /// Returns `true` if this function must conform to `min_const_fn`
    pub fn is_min_const_fn(self, def_id: DefId) -> bool {
        // Bail out if the signature doesn't contain `const`
        if !self.is_const_fn_raw(def_id) {
            return false;
        }
        if let Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
            return self.is_intrinsic_min_const_fn(def_id);
        }

        if self.features().staged_api {
            // in order for a libstd function to be considered min_const_fn
            // it needs to be stable and have no `rustc_const_unstable` attribute
            match self.lookup_stability(def_id) {
                // stable functions with unstable const fn aren't `min_const_fn`
                Some(&attr::Stability { const_stability: Some(_), .. }) => false,
                // unstable functions don't need to conform
                Some(&attr::Stability { ref level, .. }) if level.is_unstable() => false,
                // everything else needs to conform, because it would be callable from
                // other `min_const_fn` functions
                _ => true,
            }
        } else {
            // users enabling the `const_fn` feature gate can do what they want
            !self.features().const_fn
        }
    }
}

pub fn provide(providers: &mut Providers<'_>) {
    /// Const evaluability whitelist is here to check evaluability at the
    /// top level beforehand.
    fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
        match tcx.fn_sig(def_id).abi() {
            Abi::RustIntrinsic |
            Abi::PlatformIntrinsic => {
                match tcx.item_name(def_id) {
                    // Keep this list in the same order as the match patterns in
                    // `librustc_mir/interpret/intrinsics.rs`

                    // This whitelist is a list of intrinsics that have a miri-engine implementation
                    // and can thus be called when enabling enough feature gates. The similar
                    // whitelist in `is_intrinsic_min_const_fn` (in this file), exists for allowing
                    // the intrinsics to be called by stable const fns.
                    | sym::caller_location

                    | sym::min_align_of
                    | sym::pref_align_of
                    | sym::needs_drop
                    | sym::size_of
                    | sym::type_id
                    | sym::type_name

                    | sym::ctpop
                    | sym::cttz
                    | sym::cttz_nonzero
                    | sym::ctlz
                    | sym::ctlz_nonzero
                    | sym::bswap
                    | sym::bitreverse

                    | sym::wrapping_add
                    | sym::wrapping_sub
                    | sym::wrapping_mul
                    | sym::add_with_overflow
                    | sym::sub_with_overflow
                    | sym::mul_with_overflow

                    | sym::saturating_add
                    | sym::saturating_sub

                    | sym::unchecked_shl
                    | sym::unchecked_shr

                    | sym::rotate_left
                    | sym::rotate_right

                    | sym::ptr_offset_from

                    | sym::transmute

                    | sym::simd_insert

                    | sym::simd_extract

                    => Some(true),

                    _ => Some(false)
                }
            }
            _ => None
        }
    }

    /// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
    /// said intrinsic is on the whitelist for being const callable.
    fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
        let hir_id = tcx.hir().as_local_hir_id(def_id)
                              .expect("Non-local call to local provider is_const_fn");

        let node = tcx.hir().get(hir_id);

        if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
            whitelisted
        } else if let Some(fn_like) = FnLikeNode::from_node(node) {
            fn_like.constness() == hir::Constness::Const
        } else if let hir::Node::Ctor(_) = node {
            true
        } else {
            false
        }
    }

    fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
        tcx.is_const_fn(def_id) && match tcx.lookup_stability(def_id) {
            Some(stab) => {
                if cfg!(debug_assertions) && stab.promotable {
                    let sig = tcx.fn_sig(def_id);
                    assert_eq!(
                        sig.unsafety(),
                        hir::Unsafety::Normal,
                        "don't mark const unsafe fns as promotable",
                        // https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
                    );
                }
                stab.promotable
            },
            None => false,
        }
    }

    fn const_fn_is_allowed_fn_ptr(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
        tcx.is_const_fn(def_id) &&
            tcx.lookup_stability(def_id)
                .map(|stab| stab.allow_const_fn_ptr).unwrap_or(false)
    }

    *providers = Providers {
        is_const_fn_raw,
        is_promotable_const_fn,
        const_fn_is_allowed_fn_ptr,
        ..*providers
    };
}
<|endoftext|>