summary refs log tree commit diff
path: root/compiler/rustc_middle/src/middle/limits.rs
blob: 601198fd0de0443090df30821db510102dee1d82 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//! Registering limits:
//! * recursion_limit,
//! * move_size_limit,
//! * type_length_limit, and
//! * const_eval_limit
//!
//! There are various parts of the compiler that must impose arbitrary limits
//! on how deeply they recurse to prevent stack overflow. Users can override
//! this via an attribute on the crate like `#![recursion_limit="22"]`. This pass
//! just peeks and looks for that attribute.

use crate::bug;
use rustc_ast as ast;
use rustc_data_structures::sync::OnceCell;
use rustc_session::Session;
use rustc_span::symbol::{sym, Symbol};

use std::num::IntErrorKind;

pub fn update_limits(sess: &Session, krate: &ast::Crate) {
    update_limit(sess, krate, &sess.recursion_limit, sym::recursion_limit, 128);
    update_limit(sess, krate, &sess.move_size_limit, sym::move_size_limit, 0);
    update_limit(sess, krate, &sess.type_length_limit, sym::type_length_limit, 1048576);
    update_limit(sess, krate, &sess.const_eval_limit, sym::const_eval_limit, 1_000_000);
}

fn update_limit(
    sess: &Session,
    krate: &ast::Crate,
    limit: &OnceCell<impl From<usize> + std::fmt::Debug>,
    name: Symbol,
    default: usize,
) {
    for attr in &krate.attrs {
        if !sess.check_name(attr, name) {
            continue;
        }

        if let Some(s) = attr.value_str() {
            match s.as_str().parse() {
                Ok(n) => {
                    limit.set(From::from(n)).unwrap();
                    return;
                }
                Err(e) => {
                    let mut err =
                        sess.struct_span_err(attr.span, "`limit` must be a non-negative integer");

                    let value_span = attr
                        .meta()
                        .and_then(|meta| meta.name_value_literal_span())
                        .unwrap_or(attr.span);

                    let error_str = match e.kind() {
                        IntErrorKind::PosOverflow => "`limit` is too large",
                        IntErrorKind::Empty => "`limit` must be a non-negative integer",
                        IntErrorKind::InvalidDigit => "not a valid integer",
                        IntErrorKind::NegOverflow => {
                            bug!("`limit` should never negatively overflow")
                        }
                        IntErrorKind::Zero => bug!("zero is a valid `limit`"),
                        kind => bug!("unimplemented IntErrorKind variant: {:?}", kind),
                    };

                    err.span_label(value_span, error_str);
                    err.emit();
                }
            }
        }
    }
    limit.set(From::from(default)).unwrap();
}