Skip to content

Commit 600a004

Browse files
committed
---
yaml --- r: 152037 b: refs/heads/try2 c: c327080 h: refs/heads/master i: 152035: 44bbc88 v: v3
1 parent d543def commit 600a004

File tree

5 files changed

+182
-42
lines changed

5 files changed

+182
-42
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ refs/heads/snap-stage3: 78a7676898d9f80ab540c6df5d4c9ce35bb50463
55
refs/heads/try: 519addf6277dbafccbb4159db4b710c37eaa2ec5
66
refs/tags/release-0.1: 1f5c5126e96c79d22cb7862f75304136e204f105
77
refs/heads/ndm: f3868061cd7988080c30d6d5bf352a5a5fe2460b
8-
refs/heads/try2: f122ad08a57d3e250f0c8c5036327eaa320dd4eb
8+
refs/heads/try2: c327080ee04e641a34f30ae71b713a91106680b1
99
refs/heads/dist-snap: ba4081a5a8573875fed17545846f6f6902c8ba8d
1010
refs/tags/release-0.2: c870d2dffb391e14efb05aa27898f1f6333a9596
1111
refs/tags/release-0.3: b5f0d0f648d9a6153664837026ba1be43d3e2503

branches/try2/src/librustc/middle/lint.rs

Lines changed: 80 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ use syntax::parse::token;
7272
use syntax::visit::Visitor;
7373
use syntax::{ast, ast_util, visit};
7474

75-
#[deriving(Clone, Eq, Ord, TotalEq, TotalOrd)]
75+
#[deriving(Clone, Show, Eq, Ord, TotalEq, TotalOrd)]
7676
pub enum Lint {
7777
CTypes,
7878
UnusedImports,
@@ -93,6 +93,7 @@ pub enum Lint {
9393
UnknownFeatures,
9494
UnknownCrateType,
9595
UnsignedNegate,
96+
VariantSizeDifference,
9697

9798
ManagedHeapMemory,
9899
OwnedHeapMemory,
@@ -146,8 +147,9 @@ pub struct LintSpec {
146147

147148
pub type LintDict = HashMap<&'static str, LintSpec>;
148149

150+
// this is public for the lints that run in trans
149151
#[deriving(Eq)]
150-
enum LintSource {
152+
pub enum LintSource {
151153
Node(Span),
152154
Default,
153155
CommandLine
@@ -399,6 +401,13 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[
399401
default: Warn
400402
}),
401403

404+
("variant_size_difference",
405+
LintSpec {
406+
lint: VariantSizeDifference,
407+
desc: "detects enums with widely varying variant sizes",
408+
default: Allow,
409+
}),
410+
402411
("unused_must_use",
403412
LintSpec {
404413
lint: UnusedMustUse,
@@ -461,6 +470,54 @@ struct Context<'a> {
461470

462471
/// Ids of structs/enums which have been checked for raw_pointer_deriving
463472
checked_raw_pointers: NodeSet,
473+
474+
/// Level of EnumSizeVariance lint for each enum, stored here because the
475+
/// body of the lint needs to run in trans.
476+
enum_levels: HashMap<ast::NodeId, (Level, LintSource)>,
477+
}
478+
479+
pub fn emit_lint(level: Level, src: LintSource, msg: &str, span: Span,
480+
lint_str: &str, tcx: &ty::ctxt) {
481+
if level == Allow { return }
482+
483+
let mut note = None;
484+
let msg = match src {
485+
Default => {
486+
format!("{}, \\#[{}({})] on by default", msg,
487+
level_to_str(level), lint_str)
488+
},
489+
CommandLine => {
490+
format!("{} [-{} {}]", msg,
491+
match level {
492+
Warn => 'W', Deny => 'D', Forbid => 'F',
493+
Allow => fail!()
494+
}, lint_str.replace("_", "-"))
495+
},
496+
Node(src) => {
497+
note = Some(src);
498+
msg.to_str()
499+
}
500+
};
501+
502+
match level {
503+
Warn => { tcx.sess.span_warn(span, msg.as_slice()); }
504+
Deny | Forbid => { tcx.sess.span_err(span, msg.as_slice()); }
505+
Allow => fail!(),
506+
}
507+
508+
for &span in note.iter() {
509+
tcx.sess.span_note(span, "lint level defined here");
510+
}
511+
}
512+
513+
pub fn lint_to_str(lint: Lint) -> &'static str {
514+
for &(name, lspec) in lint_table.iter() {
515+
if lspec.lint == lint {
516+
return name;
517+
}
518+
}
519+
520+
fail!("unrecognized lint: {}", lint);
464521
}
465522

466523
impl<'a> Context<'a> {
@@ -492,7 +549,7 @@ impl<'a> Context<'a> {
492549
return *k;
493550
}
494551
}
495-
fail!("unregistered lint {:?}", lint);
552+
fail!("unregistered lint {}", lint);
496553
}
497554

498555
fn span_lint(&self, lint: Lint, span: Span, msg: &str) {
@@ -501,37 +558,8 @@ impl<'a> Context<'a> {
501558
Some(&(Warn, src)) => (self.get_level(Warnings), src),
502559
Some(&pair) => pair,
503560
};
504-
if level == Allow { return }
505-
506-
let mut note = None;
507-
let msg = match src {
508-
Default => {
509-
format_strbuf!("{}, \\#[{}({})] on by default",
510-
msg,
511-
level_to_str(level),
512-
self.lint_to_str(lint))
513-
},
514-
CommandLine => {
515-
format!("{} [-{} {}]", msg,
516-
match level {
517-
Warn => 'W', Deny => 'D', Forbid => 'F',
518-
Allow => fail!()
519-
}, self.lint_to_str(lint).replace("_", "-"))
520-
},
521-
Node(src) => {
522-
note = Some(src);
523-
msg.to_str()
524-
}
525-
};
526-
match level {
527-
Warn => self.tcx.sess.span_warn(span, msg.as_slice()),
528-
Deny | Forbid => self.tcx.sess.span_err(span, msg.as_slice()),
529-
Allow => fail!(),
530-
}
531561

532-
for &span in note.iter() {
533-
self.tcx.sess.span_note(span, "lint level defined here");
534-
}
562+
emit_lint(level, src, msg, span, self.lint_to_str(lint), self.tcx);
535563
}
536564

537565
/**
@@ -1685,9 +1713,24 @@ fn check_stability(cx: &Context, e: &ast::Expr) {
16851713
cx.span_lint(lint, e.span, msg.as_slice());
16861714
}
16871715

1716+
fn check_enum_variant_sizes(cx: &mut Context, it: &ast::Item) {
1717+
match it.node {
1718+
ast::ItemEnum(..) => {
1719+
match cx.cur.find(&(VariantSizeDifference as uint)) {
1720+
Some(&(lvl, src)) if lvl != Allow => {
1721+
cx.node_levels.insert((it.id, VariantSizeDifference), (lvl, src));
1722+
},
1723+
_ => { }
1724+
}
1725+
},
1726+
_ => { }
1727+
}
1728+
}
1729+
16881730
impl<'a> Visitor<()> for Context<'a> {
16891731
fn visit_item(&mut self, it: &ast::Item, _: ()) {
16901732
self.with_lint_attrs(it.attrs.as_slice(), |cx| {
1733+
check_enum_variant_sizes(cx, it);
16911734
check_item_ctypes(cx, it);
16921735
check_item_non_camel_case_types(cx, it);
16931736
check_item_non_uppercase_statics(cx, it);
@@ -1878,6 +1921,7 @@ pub fn check_crate(tcx: &ty::ctxt,
18781921
lint_stack: Vec::new(),
18791922
negated_expr_id: -1,
18801923
checked_raw_pointers: NodeSet::new(),
1924+
enum_levels: HashMap::new(),
18811925
};
18821926

18831927
// Install default lint levels, followed by the command line levels, and
@@ -1913,13 +1957,11 @@ pub fn check_crate(tcx: &ty::ctxt,
19131957
// in the iteration code.
19141958
for (id, v) in tcx.sess.lints.borrow().iter() {
19151959
for &(lint, span, ref msg) in v.iter() {
1916-
tcx.sess.span_bug(span,
1917-
format!("unprocessed lint {:?} at {}: {}",
1918-
lint,
1919-
tcx.map.node_to_str(*id),
1920-
*msg).as_slice())
1960+
tcx.sess.span_bug(span, format!("unprocessed lint {} at {}: {}",
1961+
lint, tcx.map.node_to_str(*id), *msg).as_slice())
19211962
}
19221963
}
19231964

19241965
tcx.sess.abort_if_errors();
1966+
*tcx.enum_lint_levels.borrow_mut() = cx.enum_levels;
19251967
}

branches/try2/src/librustc/middle/trans/base.rs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use lib::llvm::{ModuleRef, ValueRef, BasicBlockRef};
3636
use lib::llvm::{llvm, Vector};
3737
use lib;
3838
use metadata::{csearch, encoder};
39+
use middle::lint;
3940
use middle::astencode;
4041
use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
4142
use middle::weak_lang_items;
@@ -57,7 +58,7 @@ use middle::trans::foreign;
5758
use middle::trans::glue;
5859
use middle::trans::inline;
5960
use middle::trans::machine;
60-
use middle::trans::machine::{llalign_of_min, llsize_of};
61+
use middle::trans::machine::{llalign_of_min, llsize_of, llsize_of_real};
6162
use middle::trans::meth;
6263
use middle::trans::monomorphize;
6364
use middle::trans::tvec;
@@ -1539,7 +1540,7 @@ fn trans_enum_variant_or_tuple_like_struct(ccx: &CrateContext,
15391540
}
15401541

15411542
fn trans_enum_def(ccx: &CrateContext, enum_definition: &ast::EnumDef,
1542-
id: ast::NodeId, vi: &[Rc<ty::VariantInfo>],
1543+
sp: Span, id: ast::NodeId, vi: &[Rc<ty::VariantInfo>],
15431544
i: &mut uint) {
15441545
for &variant in enum_definition.variants.iter() {
15451546
let disr_val = vi[*i].disr_val;
@@ -1559,6 +1560,57 @@ fn trans_enum_def(ccx: &CrateContext, enum_definition: &ast::EnumDef,
15591560
}
15601561
}
15611562
}
1563+
1564+
enum_variant_size_lint(ccx, enum_definition, sp, id);
1565+
}
1566+
1567+
fn enum_variant_size_lint(ccx: &CrateContext, enum_def: &ast::EnumDef, sp: Span, id: ast::NodeId) {
1568+
let mut sizes = Vec::new(); // does no allocation if no pushes, thankfully
1569+
1570+
let (lvl, src) = ccx.tcx.node_lint_levels.borrow()
1571+
.find(&(id, lint::VariantSizeDifference))
1572+
.map_or((lint::Allow, lint::Default), |&(lvl,src)| (lvl, src));
1573+
1574+
if lvl != lint::Allow {
1575+
let avar = adt::represent_type(ccx, ty::node_id_to_type(ccx.tcx(), id));
1576+
match *avar {
1577+
adt::General(_, ref variants) => {
1578+
for var in variants.iter() {
1579+
let mut size = 0;
1580+
for field in var.fields.iter().skip(1) {
1581+
// skip the dicriminant
1582+
size += llsize_of_real(ccx, sizing_type_of(ccx, *field));
1583+
}
1584+
sizes.push(size);
1585+
}
1586+
},
1587+
_ => { /* its size is either constant or unimportant */ }
1588+
}
1589+
1590+
let (largest, slargest, largest_index) = sizes.iter().enumerate().fold((0, 0, 0),
1591+
|(l, s, li), (idx, &size)|
1592+
if size > l {
1593+
(size, l, idx)
1594+
} else if size > s {
1595+
(l, size, li)
1596+
} else {
1597+
(l, s, li)
1598+
}
1599+
);
1600+
1601+
// we only warn if the largest variant is at least thrice as large as
1602+
// the second-largest.
1603+
if largest > slargest * 3 && slargest > 0 {
1604+
lint::emit_lint(lvl, src,
1605+
format!("enum variant is more than three times larger \
1606+
({} bytes) than the next largest (ignoring padding)",
1607+
largest).as_slice(),
1608+
sp, lint::lint_to_str(lint::VariantSizeDifference), ccx.tcx());
1609+
1610+
ccx.sess().span_note(enum_def.variants.get(largest_index).span,
1611+
"this variant is the largest");
1612+
}
1613+
}
15621614
}
15631615

15641616
pub struct TransItemVisitor<'a> {
@@ -1605,7 +1657,7 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) {
16051657
if !generics.is_type_parameterized() {
16061658
let vi = ty::enum_variants(ccx.tcx(), local_def(item.id));
16071659
let mut i = 0;
1608-
trans_enum_def(ccx, enum_definition, item.id, vi.as_slice(), &mut i);
1660+
trans_enum_def(ccx, enum_definition, item.span, item.id, vi.as_slice(), &mut i);
16091661
}
16101662
}
16111663
ast::ItemStatic(_, m, expr) => {

branches/try2/src/librustc/middle/ty.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use back::svh::Svh;
1414
use driver::session::Session;
1515
use metadata::csearch;
1616
use mc = middle::mem_categorization;
17+
use middle::lint;
1718
use middle::const_eval;
1819
use middle::dependency_format;
1920
use middle::lang_items::{ExchangeHeapLangItem, OpaqueStructLangItem};
@@ -352,6 +353,8 @@ pub struct ctxt {
352353
pub vtable_map: typeck::vtable_map,
353354

354355
pub dependency_formats: RefCell<dependency_format::Dependencies>,
356+
357+
pub enum_lint_levels: RefCell<HashMap<ast::NodeId, (lint::Level, lint::LintSource)>>,
355358
}
356359

357360
pub enum tbox_flag {
@@ -1134,6 +1137,7 @@ pub fn mk_ctxt(s: Session,
11341137
method_map: RefCell::new(FnvHashMap::new()),
11351138
vtable_map: RefCell::new(FnvHashMap::new()),
11361139
dependency_formats: RefCell::new(HashMap::new()),
1140+
enum_lint_levels: RefCell::new(HashMap::new()),
11371141
}
11381142
}
11391143

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
//
11+
// ignore-pretty
12+
13+
#![deny(enum_size_variance)]
14+
#![allow(dead_code)]
15+
16+
enum Enum1 { }
17+
18+
enum Enum2 { A, B, C }
19+
20+
enum Enum3 { D(int), E, F }
21+
22+
enum Enum4 { H(int), I(int), J }
23+
24+
enum Enum5 { //~ ERROR three times larger
25+
L(int, int, int, int), //~ NOTE this variant is the largest
26+
M(int),
27+
N
28+
}
29+
30+
enum Enum6<T, U> {
31+
O(T),
32+
P(U),
33+
Q(int)
34+
}
35+
36+
#[allow(enum_size_variance)]
37+
enum Enum7 {
38+
R(int, int, int, int),
39+
S(int),
40+
T
41+
}
42+
pub fn main() { }

0 commit comments

Comments
 (0)