From 662232c8f66fcbb32a9f489a1df9cc9a09f0d9b2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 11 Nov 2015 12:54:02 -0500 Subject: [PATCH 1/7] Rewrite match algorithm to avoid massive blowup in generated code for large matches that fallback to Eq. When we encounter a case where the test being performed does not inform the candidate at all, we just stop testing the candidates at that point, rather than adding the candidate to both outcomes. The former behavior was not WRONG, but it generated a lot of code, whereas this new behavior degenerates to an if-else-if tree. Fixes #29740. --- src/librustc_mir/build/matches/mod.rs | 207 ++++++++++++++-- src/librustc_mir/build/matches/test.rs | 58 +++-- src/test/run-pass/issue-29740.rs | 316 +++++++++++++++++++++++++ 3 files changed, 533 insertions(+), 48 deletions(-) create mode 100644 src/test/run-pass/issue-29740.rs diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 4eda6961a1921..cc8549de26a6c 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -85,7 +85,15 @@ impl<'a,'tcx> Builder<'a,'tcx> { // this will generate code to test discriminant_lvalue and // branch to the appropriate arm block - self.match_candidates(span, &mut arm_blocks, candidates, block); + let otherwise = self.match_candidates(span, &mut arm_blocks, candidates, block); + + // because all matches are exhaustive, in principle we expect + // an empty vector to be returned here, but the algorithm is + // not entirely precise + if !otherwise.is_empty() { + let join_block = self.join_otherwise_blocks(otherwise); + self.panic(join_block); + } // all the arm blocks will rejoin here let end_block = self.cfg.start_new_block(); @@ -279,11 +287,32 @@ struct Test<'tcx> { // Main matching algorithm impl<'a,'tcx> Builder<'a,'tcx> { + /// The main match algorithm. It begins with a set of candidates + /// `candidates` and has the job of generating code to determine + /// which of these candidates, if any, is the correct one. The + /// candidates are sorted in inverse priority -- so the last item + /// in the list has highest priority. When a candidate is found to + /// match the value, we will generate a branch to the appropriate + /// block found in `arm_blocks`. + /// + /// The return value is a list of "otherwise" blocks. These are + /// points in execution where we found that *NONE* of the + /// candidates apply. In principle, this means that the input + /// list was not exhaustive, though at present we sometimes are + /// not smart enough to recognize all exhaustive inputs. + /// + /// It might be surprising that the input can be inexhaustive. + /// Indeed, initially, it is not, because all matches are + /// exhaustive in Rust. But during processing we sometimes divide + /// up the list of candidates and recurse with a non-exhaustive + /// list. This is important to keep the size of the generated code + /// under control. See `test_candidates` for more details. fn match_candidates<'pat>(&mut self, span: Span, arm_blocks: &mut ArmBlocks, mut candidates: Vec>, mut block: BasicBlock) + -> Vec { debug!("matched_candidate(span={:?}, block={:?}, candidates={:?})", span, block, candidates); @@ -311,17 +340,127 @@ impl<'a,'tcx> Builder<'a,'tcx> { } else { // if None is returned, then any remaining candidates // are unreachable (at least not through this path). - return; + return vec![]; } } // If there are no candidates that still need testing, we're done. // Since all matches are exhaustive, execution should never reach this point. if candidates.is_empty() { - return self.panic(block); + return vec![block]; + } + + // Test candidates where possible. + let (otherwise, tested_candidates) = + self.test_candidates(span, arm_blocks, &candidates, block); + + // If the target candidates were exhaustive, then we are done. + if otherwise.is_empty() { + return vec![]; + } + + // If all candidates were sorted into `target_candidates` somewhere, then + // the initial set was inexhaustive. + let untested_candidates = candidates.len() - tested_candidates; + if untested_candidates == 0 { + return otherwise; } - // otherwise, extract the next match pair and construct tests + // Otherwise, let's process those remaining candidates. + let join_block = self.join_otherwise_blocks(otherwise); + candidates.truncate(untested_candidates); + self.match_candidates(span, arm_blocks, candidates, join_block) + } + + fn join_otherwise_blocks(&mut self, + otherwise: Vec) + -> BasicBlock + { + if otherwise.len() == 1 { + otherwise[0] + } else { + let join_block = self.cfg.start_new_block(); + for block in otherwise { + self.cfg.terminate(block, Terminator::Goto { target: join_block }); + } + join_block + } + } + + /// This is the most subtle part of the matching algorithm. At + /// this point, the input candidates have been fully simplified, + /// and so we know that all remaining match-pairs require some + /// sort of test. To decide what test to do, we take the highest + /// priority candidate (last one in the list) and extract the + /// first match-pair from the list. From this we decide what kind + /// of test is needed using `test`, defined in the `test` module. + /// + /// *Note:* taking the first match pair is somewhat arbitrary, and + /// we might do better here by choosing more carefully what to + /// test. + /// + /// For example, consider the following possible match-pairs: + /// + /// 1. `x @ Some(P)` -- we will do a `Switch` to decide what variant `x` has + /// 2. `x @ 22` -- we will do a `SwitchInt` + /// 3. `x @ 3..5` -- we will do a range test + /// 4. etc. + /// + /// Once we know what sort of test we are going to perform, this + /// test may also help us with other candidates. So we walk over + /// the candidates (from high to low priority) and check. This + /// gives us, for each outcome of the test, a transformed list of + /// candidates. For example, if we are testing the current + /// variant of `x.0`, and we have a candidate `{x.0 @ Some(v), x.1 + /// @ 22}`, then we would have a resulting candidate of `{(x.0 as + /// Some).0 @ v, x.1 @ 22}`. Note that the first match-pair is now + /// simpler (and, in fact, irrefutable). + /// + /// But there may also be candidates that the test just doesn't + /// apply to. For example, consider the case of #29740: + /// + /// ```rust + /// match x { + /// "foo" => ..., + /// "bar" => ..., + /// "baz" => ..., + /// _ => ..., + /// } + /// ``` + /// + /// Here the match-pair we are testing will be `x @ "foo"`, and we + /// will generate an `Eq` test. Because `"bar"` and `"baz"` are different + /// constants, we will decide that these later candidates are just not + /// informed by the eq test. So we'll wind up with three candidate sets: + /// + /// - If outcome is that `x == "foo"` (one candidate, derived from `x @ "foo"`) + /// - If outcome is that `x != "foo"` (empty list of candidates) + /// - Otherwise (three candidates, `x @ "bar"`, `x @ "baz"`, `x @ + /// _`). Here we have the invariant that everything in the + /// otherwise list is of **lower priority** than the stuff in the + /// other lists. + /// + /// So we'll compile the test. For each outcome of the test, we + /// recursively call `match_candidates` with the corresponding set + /// of candidates. But note that this set is now inexhaustive: for + /// example, in the case where the test returns false, there are + /// NO candidates, even though there is stll a value to be + /// matched. So we'll collect the return values from + /// `match_candidates`, which are the blocks where control-flow + /// goes if none of the candidates matched. At this point, we can + /// continue with the "otherwise" list. + /// + /// If you apply this to the above test, you basically wind up + /// with an if-else-if chain, testing each candidate in turn, + /// which is precisely what we want. + fn test_candidates<'pat>(&mut self, + span: Span, + arm_blocks: &mut ArmBlocks, + candidates: &[Candidate<'pat, 'tcx>], + block: BasicBlock) + -> (Vec, usize) + { + // extract the match-pair from the highest priority candidate let match_pair = &candidates.last().unwrap().match_pairs[0]; let mut test = self.test(match_pair); @@ -331,35 +470,57 @@ impl<'a,'tcx> Builder<'a,'tcx> { // available match test.kind { TestKind::SwitchInt { switch_ty, ref mut options, ref mut indices } => { - for candidate in &candidates { - self.add_cases_to_switch(&match_pair.lvalue, - candidate, - switch_ty, - options, - indices); + for candidate in candidates.iter().rev() { + if !self.add_cases_to_switch(&match_pair.lvalue, + candidate, + switch_ty, + options, + indices) { + break; + } } } _ => { } } + // perform the test, branching to one of N blocks. For each of + // those N possible outcomes, create a (initially empty) + // vector of candidates. Those are the candidates that still + // apply if the test has that particular outcome. debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair); let target_blocks = self.perform_test(block, &match_pair.lvalue, &test); - let mut target_candidates: Vec<_> = (0..target_blocks.len()).map(|_| vec![]).collect(); - for candidate in &candidates { - self.sort_candidate(&match_pair.lvalue, - &test, - candidate, - &mut target_candidates); - } - - for (target_block, target_candidates) in + // Sort the candidates into the appropriate vector in + // `target_candidates`. Note that at some point we may + // encounter a candidate where the test is not relevant; at + // that point, we stop sorting. + let tested_candidates = + candidates.iter() + .rev() + .take_while(|c| self.sort_candidate(&match_pair.lvalue, + &test, + c, + &mut target_candidates)) + .count(); + assert!(tested_candidates > 0); // at least the last candidate ought to be tested + + // For each outcome of test, process the candidates that still + // apply. Collect a list of blocks where control flow will + // branch if one of the `target_candidate` sets is not + // exhaustive. + let otherwise: Vec<_> = target_blocks.into_iter() - .zip(target_candidates.into_iter()) - { - self.match_candidates(span, arm_blocks, target_candidates, target_block); - } + .zip(target_candidates) + .flat_map(|(target_block, target_candidates)| { + self.match_candidates(span, + arm_blocks, + target_candidates, + target_block) + }) + .collect(); + + (otherwise, tested_candidates) } /// Initializes each of the bindings from the candidate by diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 312ab61ba6cec..dffd83f1c4150 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -105,10 +105,11 @@ impl<'a,'tcx> Builder<'a,'tcx> { switch_ty: Ty<'tcx>, options: &mut Vec, indices: &mut FnvHashMap) + -> bool { let match_pair = match candidate.match_pairs.iter().find(|mp| mp.lvalue == *test_lvalue) { Some(match_pair) => match_pair, - _ => { return; } + _ => { return false; } }; match *match_pair.pattern.kind { @@ -121,11 +122,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { options.push(value.clone()); options.len() - 1 }); + true } - PatternKind::Range { .. } => { - } - + PatternKind::Range { .. } | PatternKind::Constant { .. } | PatternKind::Variant { .. } | PatternKind::Slice { .. } | @@ -134,6 +134,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { PatternKind::Binding { .. } | PatternKind::Leaf { .. } | PatternKind::Deref { .. } => { + // don't know how to add these patterns to a switch + false } } } @@ -284,18 +286,29 @@ impl<'a,'tcx> Builder<'a,'tcx> { /// P0` to the `resulting_candidates` entry corresponding to the /// variant `Some`. /// - /// In many cases we will add the `candidate` to more than one - /// outcome. For example, say that the test is `x == 22`, but the - /// candidate is `x @ 13..55`. In that case, if the test is true, - /// then we know that the candidate applies (without this match - /// pair, potentially, though we don't optimize this due to - /// #29623). If the test is false, the candidate may also apply - /// (with the match pair, still). + /// However, in some cases, the test may just not be relevant to + /// candidate. For example, suppose we are testing whether `foo.x == 22`, + /// but in one match arm we have `Foo { x: _, ... }`... in that case, + /// the test for what value `x` has has no particular relevance + /// to this candidate. In such cases, this function just returns false + /// without doing anything. This is used by the overall `match_candidates` + /// algorithm to structure the match as a whole. See `match_candidates` for + /// more details. + /// + /// FIXME(#29623). In some cases, we have some tricky choices to + /// make. for example, if we are testing that `x == 22`, but the + /// candidate is `x @ 13..55`, what should we do? In the event + /// that the test is true, we know that the candidate applies, but + /// in the event of false, we don't know that it *doesn't* + /// apply. For now, we return false, indicate that the test does + /// not apply to this candidate, but it might be we can get + /// tighter match code if we do something a bit different. pub fn sort_candidate<'pat>(&mut self, test_lvalue: &Lvalue<'tcx>, test: &Test<'tcx>, candidate: &Candidate<'pat, 'tcx>, - resulting_candidates: &mut [Vec>]) { + resulting_candidates: &mut [Vec>]) + -> bool { // Find the match_pair for this lvalue (if any). At present, // afaik, there can be at most one. (In the future, if we // adopted a more general `@` operator, there might be more @@ -311,7 +324,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { None => { // We are not testing this lvalue. Therefore, this // candidate applies to ALL outcomes. - return self.add_to_all_candidate_sets(candidate, resulting_candidates); + return false; } }; @@ -329,9 +342,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { subpatterns, candidate); resulting_candidates[variant_index].push(new_candidate); + true } _ => { - self.add_to_all_candidate_sets(candidate, resulting_candidates); + false } } } @@ -349,9 +363,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { let new_candidate = self.candidate_without_match_pair(match_pair_index, candidate); resulting_candidates[index].push(new_candidate); + true } _ => { - self.add_to_all_candidate_sets(candidate, resulting_candidates); + false } } } @@ -367,8 +382,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { let new_candidate = self.candidate_without_match_pair(match_pair_index, candidate); resulting_candidates[0].push(new_candidate); + true } else { - self.add_to_all_candidate_sets(candidate, resulting_candidates); + false } } } @@ -392,14 +408,6 @@ impl<'a,'tcx> Builder<'a,'tcx> { } } - fn add_to_all_candidate_sets<'pat>(&mut self, - candidate: &Candidate<'pat, 'tcx>, - resulting_candidates: &mut [Vec>]) { - for resulting_candidate in resulting_candidates { - resulting_candidate.push(candidate.clone()); - } - } - fn candidate_after_variant_switch<'pat>(&mut self, match_pair_index: usize, adt_def: ty::AdtDef<'tcx>, @@ -447,5 +455,5 @@ impl<'a,'tcx> Builder<'a,'tcx> { } fn is_switch_ty<'tcx>(ty: Ty<'tcx>) -> bool { - ty.is_integral() || ty.is_char() + ty.is_integral() || ty.is_char() || ty.is_bool() } diff --git a/src/test/run-pass/issue-29740.rs b/src/test/run-pass/issue-29740.rs new file mode 100644 index 0000000000000..f85b532ed612c --- /dev/null +++ b/src/test/run-pass/issue-29740.rs @@ -0,0 +1,316 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for #29740. Inefficient MIR matching algorithms +// generated way too much code for this sort of case, leading to OOM. + +pub mod KeyboardEventConstants { + pub const DOM_KEY_LOCATION_STANDARD: u32 = 0; + pub const DOM_KEY_LOCATION_LEFT: u32 = 1; + pub const DOM_KEY_LOCATION_RIGHT: u32 = 2; + pub const DOM_KEY_LOCATION_NUMPAD: u32 = 3; +} // mod KeyboardEventConstants + +pub enum Key { + Space, + Apostrophe, + Comma, + Minus, + Period, + Slash, + Num0, + Num1, + Num2, + Num3, + Num4, + Num5, + Num6, + Num7, + Num8, + Num9, + Semicolon, + Equal, + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + LeftBracket, + Backslash, + RightBracket, + GraveAccent, + World1, + World2, + + Escape, + Enter, + Tab, + Backspace, + Insert, + Delete, + Right, + Left, + Down, + Up, + PageUp, + PageDown, + Home, + End, + CapsLock, + ScrollLock, + NumLock, + PrintScreen, + Pause, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + F13, + F14, + F15, + F16, + F17, + F18, + F19, + F20, + F21, + F22, + F23, + F24, + F25, + Kp0, + Kp1, + Kp2, + Kp3, + Kp4, + Kp5, + Kp6, + Kp7, + Kp8, + Kp9, + KpDecimal, + KpDivide, + KpMultiply, + KpSubtract, + KpAdd, + KpEnter, + KpEqual, + LeftShift, + LeftControl, + LeftAlt, + LeftSuper, + RightShift, + RightControl, + RightAlt, + RightSuper, + Menu, +} + +fn key_from_string(key_string: &str, location: u32) -> Option { + match key_string { + " " => Some(Key::Space), + "\"" => Some(Key::Apostrophe), + "'" => Some(Key::Apostrophe), + "<" => Some(Key::Comma), + "," => Some(Key::Comma), + "_" => Some(Key::Minus), + "-" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Minus), + ">" => Some(Key::Period), + "." if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Period), + "?" => Some(Key::Slash), + "/" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Slash), + "~" => Some(Key::GraveAccent), + "`" => Some(Key::GraveAccent), + ")" => Some(Key::Num0), + "0" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num0), + "!" => Some(Key::Num1), + "1" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num1), + "@" => Some(Key::Num2), + "2" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num2), + "#" => Some(Key::Num3), + "3" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num3), + "$" => Some(Key::Num4), + "4" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num4), + "%" => Some(Key::Num5), + "5" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num5), + "^" => Some(Key::Num6), + "6" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num6), + "&" => Some(Key::Num7), + "7" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num7), + "*" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num8), + "8" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num8), + "(" => Some(Key::Num9), + "9" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Num9), + ":" => Some(Key::Semicolon), + ";" => Some(Key::Semicolon), + "+" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Equal), + "=" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Equal), + "A" => Some(Key::A), + "a" => Some(Key::A), + "B" => Some(Key::B), + "b" => Some(Key::B), + "C" => Some(Key::C), + "c" => Some(Key::C), + "D" => Some(Key::D), + "d" => Some(Key::D), + "E" => Some(Key::E), + "e" => Some(Key::E), + "F" => Some(Key::F), + "f" => Some(Key::F), + "G" => Some(Key::G), + "g" => Some(Key::G), + "H" => Some(Key::H), + "h" => Some(Key::H), + "I" => Some(Key::I), + "i" => Some(Key::I), + "J" => Some(Key::J), + "j" => Some(Key::J), + "K" => Some(Key::K), + "k" => Some(Key::K), + "L" => Some(Key::L), + "l" => Some(Key::L), + "M" => Some(Key::M), + "m" => Some(Key::M), + "N" => Some(Key::N), + "n" => Some(Key::N), + "O" => Some(Key::O), + "o" => Some(Key::O), + "P" => Some(Key::P), + "p" => Some(Key::P), + "Q" => Some(Key::Q), + "q" => Some(Key::Q), + "R" => Some(Key::R), + "r" => Some(Key::R), + "S" => Some(Key::S), + "s" => Some(Key::S), + "T" => Some(Key::T), + "t" => Some(Key::T), + "U" => Some(Key::U), + "u" => Some(Key::U), + "V" => Some(Key::V), + "v" => Some(Key::V), + "W" => Some(Key::W), + "w" => Some(Key::W), + "X" => Some(Key::X), + "x" => Some(Key::X), + "Y" => Some(Key::Y), + "y" => Some(Key::Y), + "Z" => Some(Key::Z), + "z" => Some(Key::Z), + "{" => Some(Key::LeftBracket), + "[" => Some(Key::LeftBracket), + "|" => Some(Key::Backslash), + "\\" => Some(Key::Backslash), + "}" => Some(Key::RightBracket), + "]" => Some(Key::RightBracket), + "Escape" => Some(Key::Escape), + "Enter" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Enter), + "Tab" => Some(Key::Tab), + "Backspace" => Some(Key::Backspace), + "Insert" => Some(Key::Insert), + "Delete" => Some(Key::Delete), + "ArrowRight" => Some(Key::Right), + "ArrowLeft" => Some(Key::Left), + "ArrowDown" => Some(Key::Down), + "ArrowUp" => Some(Key::Up), + "PageUp" => Some(Key::PageUp), + "PageDown" => Some(Key::PageDown), + "Home" => Some(Key::Home), + "End" => Some(Key::End), + "CapsLock" => Some(Key::CapsLock), + "ScrollLock" => Some(Key::ScrollLock), + "NumLock" => Some(Key::NumLock), + "PrintScreen" => Some(Key::PrintScreen), + "Pause" => Some(Key::Pause), + "F1" => Some(Key::F1), + "F2" => Some(Key::F2), + "F3" => Some(Key::F3), + "F4" => Some(Key::F4), + "F5" => Some(Key::F5), + "F6" => Some(Key::F6), + "F7" => Some(Key::F7), + "F8" => Some(Key::F8), + "F9" => Some(Key::F9), + "F10" => Some(Key::F10), + "F11" => Some(Key::F11), + "F12" => Some(Key::F12), + "F13" => Some(Key::F13), + "F14" => Some(Key::F14), + "F15" => Some(Key::F15), + "F16" => Some(Key::F16), + "F17" => Some(Key::F17), + "F18" => Some(Key::F18), + "F19" => Some(Key::F19), + "F20" => Some(Key::F20), + "F21" => Some(Key::F21), + "F22" => Some(Key::F22), + "F23" => Some(Key::F23), + "F24" => Some(Key::F24), + "F25" => Some(Key::F25), + "0" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp0), + "1" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp1), + "2" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp2), + "3" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp3), + "4" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp4), + "5" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp5), + "6" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp6), + "7" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp7), + "8" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp8), + "9" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::Kp9), + "." if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpDecimal), + "/" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpDivide), + "*" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpMultiply), + "-" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpSubtract), + "+" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpAdd), + "Enter" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpEnter), + "=" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpEqual), + "Shift" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => Some(Key::LeftShift), + "Control" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => Some(Key::LeftControl), + "Alt" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => Some(Key::LeftAlt), + "Super" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => Some(Key::LeftSuper), + "Shift" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => Some(Key::RightShift), + "Control" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => Some(Key::RightControl), + "Alt" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => Some(Key::RightAlt), + "Super" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => Some(Key::RightSuper), + "ContextMenu" => Some(Key::Menu), + _ => None + } +} + +fn main() { } From a1b24768a6a57de029110f8370d59badd05b224d Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Wed, 11 Nov 2015 15:22:23 -0500 Subject: [PATCH 2/7] Clean up the prelude docs This mostly brings them in line with existing linking convention, but also has some minor re-wording. The text at the top has been re-focused, by starting out with what the prelude does, rather than starting from injecting std. Also, it now mentions that other preludes exist. Part of https://github.com/rust-lang/rust/issues/29369 --- src/libstd/prelude/mod.rs | 209 +++++++++++++++++++++----------------- src/libstd/prelude/v1.rs | 2 + 2 files changed, 115 insertions(+), 96 deletions(-) diff --git a/src/libstd/prelude/mod.rs b/src/libstd/prelude/mod.rs index 08227cfb35322..3ff1798159efa 100644 --- a/src/libstd/prelude/mod.rs +++ b/src/libstd/prelude/mod.rs @@ -10,119 +10,136 @@ //! The Rust Prelude //! -//! Because `std` is required by most serious Rust software, it is -//! imported at the topmost level of every crate by default, as if -//! each crate contains the following: +//! Rust comes with a variety of things in its standard library. However, if +//! you had to manually import every single thing that you used, it would be +//! very verbose. But importing a lot of things that a program never uses isn't +//! good either. A balance needs to be struck. +//! +//! The *prelude* is the list of things that Rust automatically imports into +//! every Rust program. It's kept as small as possible, and is focused on +//! things, particuarly traits, which are used in almost every single Rust +//! program. +//! +//! On a technical level, Rust inserts //! //! ```ignore //! extern crate std; //! ``` //! -//! This means that the contents of std can be accessed from any context -//! with the `std::` path prefix, as in `use std::vec`, `use std::thread::spawn`, -//! etc. -//! -//! Additionally, `std` contains a versioned *prelude* that reexports many of the -//! most common traits, types, and functions. *The contents of the prelude are -//! imported into every module by default*. Implicitly, all modules behave as if -//! they contained the following [`use` statement][book-use]: -//! -//! [book-use]: ../../book/crates-and-modules.html#importing-modules-with-use +//! into the crate root of every crate, and //! //! ```ignore //! use std::prelude::v1::*; //! ``` //! -//! The prelude is primarily concerned with exporting *traits* that -//! are so pervasive that they would be onerous to import for every use, -//! particularly those that are commonly mentioned in [generic type -//! bounds][book-traits]. +//! into every module. +//! +//! # Other preludes +//! +//! Preludes can be seen as a pattern to make using multiple types more +//! convenient. As such, you'll find other preludes in the standard library, +//! such as [`std::io::prelude`]. Various libraries in the Rust ecosystem may +//! also define their own preludes. +//! +//! [`std::io::prelude`]: ../io/prelude/index.html +//! +//! The differece between 'the prelude' and these other preludes is that they +//! are not automatically `use`'d, and must be imported manually. This is still +//! easier than importing all of their consitutent components. +//! +//! # Prelude contents //! //! The current version of the prelude (version 1) lives in -//! [`std::prelude::v1`](v1/index.html), and reexports the following. +//! [`std::prelude::v1`], and reexports the following. //! -//! * `std::marker::`{ -//! [`Copy`](../marker/trait.Copy.html), -//! [`Send`](../marker/trait.Send.html), -//! [`Sized`](../marker/trait.Sized.html), -//! [`Sync`](../marker/trait.Sync.html) -//! }. -//! The marker traits indicate fundamental properties of types. -//! * `std::ops::`{ -//! [`Drop`](../ops/trait.Drop.html), -//! [`Fn`](../ops/trait.Fn.html), -//! [`FnMut`](../ops/trait.FnMut.html), -//! [`FnOnce`](../ops/trait.FnOnce.html) -//! }. -//! The [destructor][book-dtor] trait and the -//! [closure][book-closures] traits, reexported from the same -//! [module that also defines overloaded -//! operators](../ops/index.html). -//! * `std::mem::`[`drop`](../mem/fn.drop.html). -//! A convenience function for explicitly dropping a value. -//! * `std::boxed::`[`Box`](../boxed/struct.Box.html). -//! The owned heap pointer. -//! * `std::borrow::`[`ToOwned`](../borrow/trait.ToOwned.html). -//! The conversion trait that defines `to_owned`, the generic method -//! for creating an owned type from a borrowed type. -//! * `std::clone::`[`Clone`](../clone/trait.Clone.html). -//! The ubiquitous trait that defines `clone`, the method for -//! producing copies of values that are consider expensive to copy. -//! * `std::cmp::`{ -//! [`PartialEq`](../cmp/trait.PartialEq.html), -//! [`PartialOrd`](../cmp/trait.PartialOrd.html), -//! [`Eq`](../cmp/trait.Eq.html), -//! [`Ord`](../cmp/trait.Ord.html) -//! }. -//! The comparison traits, which implement the comparison operators -//! and are often seen in trait bounds. -//! * `std::convert::`{ -//! [`AsRef`](../convert/trait.AsRef.html), -//! [`AsMut`](../convert/trait.AsMut.html), -//! [`Into`](../convert/trait.Into.html), -//! [`From`](../convert/trait.From.html) -//! }. -//! Generic conversions, used by savvy API authors to create -//! overloaded methods. -//! * `std::default::`[`Default`](../default/trait.Default.html). -//! Types that have default values. -//! * `std::iter::`{ -//! [`Iterator`](../iter/trait.Iterator.html), -//! [`Extend`](../iter/trait.Extend.html), -//! [`IntoIterator`](../iter/trait.IntoIterator.html), -//! [`DoubleEndedIterator`](../iter/trait.DoubleEndedIterator.html), -//! [`ExactSizeIterator`](../iter/trait.ExactSizeIterator.html) -//! }. -//! [Iterators][book-iter]. -//! * `std::option::Option::`{ -//! [`self`](../option/enum.Option.html), -//! [`Some`](../option/enum.Option.html), -//! [`None`](../option/enum.Option.html) -//! }. -//! The ubiquitous `Option` type and its two [variants][book-enums], -//! `Some` and `None`. -//! * `std::result::Result::`{ -//! [`self`](../result/enum.Result.html), -//! [`Ok`](../result/enum.Result.html), -//! [`Err`](../result/enum.Result.html) -//! }. -//! The ubiquitous `Result` type and its two [variants][book-enums], -//! `Ok` and `Err`. -//! * `std::slice::`[`SliceConcatExt`](../slice/trait.SliceConcatExt.html). -//! An unstable extension to slices that shouldn't have to exist. -//! * `std::string::`{ -//! [`String`](../string/struct.String.html), -//! [`ToString`](../string/trait.ToString.html) -//! }. -//! Heap allocated strings. -//! * `std::vec::`[`Vec`](../vec/struct.Vec.html). -//! Heap allocated vectors. +//! * [`std::marker`]::{[`Copy`], [`Send`], [`Sized`], [`Sync`]}. The marker +//! traits indicate fundamental properties of types. +//! * [`std::ops`]::{[`Drop`], [`Fn`], [`FnMut`], [`FnOnce`]}. Various +//! operations for both destuctors and overloading `()`. +//! * [`std::mem`]::[`drop`], a convenience function for explicitly dropping a +//! value. +//! * [`std::boxed`]::[`Box`], a way to allocate values on the heap. +//! * [`std::borrow`]::[`ToOwned`], The conversion trait that defines +//! [`to_owned()`], the generic method for creating an owned type from a +//! borrowed type. +//! * [`std::clone`]::[`Clone`], the ubiquitous trait that defines [`clone()`], +//! the method for producing a copy of a value. +//! * [`std::cmp`]::{[`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`] }. The +//! comparison traits, which implement the comparison operators and are often +//! seen in trait bounds. +//! * [`std::convert`]::{[`AsRef`], [`AsMut`], [`Into`], [`From`]}. Generic +//! conversions, used by savvy API authors to create overloaded methods. +//! * [`std::default`]::[`Default`], types that have default values. +//! * [`std::iter`]::{[`Iterator`], [`Extend`], [`IntoIterator`], +//! [`DoubleEndedIterator`], [`ExactSizeIterator`]}. Iterators of various +//! kinds. +//! * [`std::option`]::[`Option`]::{`self`, `Some`, `None`}. A type which +//! expresses the presence or absence of a value. This type is so commonly +//! used, its variants are also exported. +//! * [`std::result`]::[`Result`]::{`self`, `Ok`, `Err`}. A type for functions +//! that may succeed or fail. Like [`Option`], its variants are exported as +//! well. +//! * [`std::slice`]::[`SliceConcatExt`], a trait that exists for technical +//! reasons, but shouldn't have to exist. It provides a few useful methods on +//! slices. +//! * [`std::string`]::{[`String`], [`ToString`]}, heap allocated strings. +//! * [`std::vec`]::[`Vec`](../vec/struct.Vec.html), a growable, heap-allocated +//! vector. //! -//! [book-traits]: ../../book/traits.html +//! [`AsMut`]: ../convert/trait.AsMut.html +//! [`AsRef`]: ../convert/trait.AsRef.html +//! [`Box`]: ../boxed/struct.Box.html +//! [`Clone`]: ../clone/trait.Clone.html +//! [`Copy`]: ../marker/trait.Copy.html +//! [`Default`]: ../default/trait.Default.html +//! [`DoubleEndedIterator`]: ../iter/trait.DoubleEndedIterator.html +//! [`Drop`]: ../ops/trait.Drop.html +//! [`Eq`]: ../cmp/trait.Eq.html +//! [`ExactSizeIterator`]: ../iter/trait.ExactSizeIterator.html +//! [`Extend`]: ../iter/trait.Extend.html +//! [`FnMut`]: ../ops/trait.FnMut.html +//! [`FnOnce`]: ../ops/trait.FnOnce.html +//! [`Fn`]: ../ops/trait.Fn.html +//! [`From`]: ../convert/trait.From.html +//! [`IntoIterator`]: ../iter/trait.IntoIterator.html +//! [`Into`]: ../convert/trait.Into.html +//! [`Iterator`]: ../iter/trait.Iterator.html +//! [`Option`]: ../option/enum.Option.html +//! [`Ord`]: ../cmp/trait.Ord.html +//! [`PartialEq`]: ../cmp/trait.PartialEq.html +//! [`PartialOrd`]: ../cmp/trait.PartialOrd.html +//! [`Result`]: ../result/enum.Result.html +//! [`Send`]: ../marker/trait.Send.html +//! [`Sized`]: ../marker/trait.Sized.html +//! [`SliceConcatExt`]: ../slice/trait.SliceConcatExt.html +//! [`String`]: ../string/struct.String.html +//! [`Sync`]: ../marker/trait.Sync.html +//! [`ToOwned`]: ../borrow/trait.ToOwned.html +//! [`ToString`]: ../string/trait.ToString.html +//! [`Vec`]: ../vec/struct.Vec.html +//! [`clone()`]: ../clone/trait.Clone.html#tymethod.clone +//! [`drop`]: ../mem/fn.drop.html +//! [`std::borrow`]: ../borrow/index.html +//! [`std::boxed`]: ../boxed/index.html +//! [`std::clone`]: ../clone/index.html +//! [`std::cmp`]: ../cmp/index.html +//! [`std::convert`]: ../convert/index.html +//! [`std::default`]: ../default/index.html +//! [`std::iter`]: ../iter/index.html +//! [`std::marker`]: ../marker/index.html +//! [`std::mem`]: ../mem/index.html +//! [`std::ops`]: ../ops/index.html +//! [`std::option`]: ../option/index.html +//! [`std::prelude::v1`]: v1/index.html +//! [`std::result`]: ../result/index.html +//! [`std::slice`]: ../slice/index.html +//! [`std::string`]: ../string/index.html +//! [`std::vec`]: ../vec/index.html +//! [`to_owned()`]: ../borrow/trait.ToOwned.html#tymethod.to_owned //! [book-closures]: ../../book/closures.html //! [book-dtor]: ../../book/drop.html -//! [book-iter]: ../../book/iterators.html //! [book-enums]: ../../book/enums.html +//! [book-iter]: ../../book/iterators.html #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/prelude/v1.rs b/src/libstd/prelude/v1.rs index 46c0103e08764..9ca5b445c86a9 100644 --- a/src/libstd/prelude/v1.rs +++ b/src/libstd/prelude/v1.rs @@ -9,6 +9,8 @@ // except according to those terms. //! The first version of the prelude of The Rust Standard Library. +//! +//! See the [module-level documentation](../index.html) for more. #![stable(feature = "rust1", since = "1.0.0")] From f42af6464a357c5c93554ca25953c4c3b31f28b3 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Wed, 11 Nov 2015 16:59:57 -0500 Subject: [PATCH 3/7] Fix import warnings for stage0 --- src/liballoc/arc.rs | 5 ++++- src/liballoc/rc.rs | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index a5a15cfe66e6d..34c8b5d4139fc 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -79,8 +79,11 @@ use core::cmp::Ordering; use core::mem::{align_of_val, size_of_val}; use core::intrinsics::abort; use core::mem; -use core::ops::{Deref, CoerceUnsized}; +use core::ops::Deref; +#[cfg(not(stage0))] +use core::ops::CoerceUnsized; use core::ptr::{self, Shared}; +#[cfg(not(stage0))] use core::marker::Unsize; use core::hash::{Hash, Hasher}; use core::{usize, isize}; diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 672b63eda67c7..b94e74ada9cef 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -161,9 +161,13 @@ use core::cmp::Ordering; use core::fmt; use core::hash::{Hasher, Hash}; use core::intrinsics::{assume, abort}; -use core::marker::{self, Unsize}; +use core::marker; +#[cfg(not(stage0))] +use core::marker::Unsize; use core::mem::{self, align_of_val, size_of_val, forget}; -use core::ops::{CoerceUnsized, Deref}; +use core::ops::Deref; +#[cfg(not(stage0))] +use core::ops::CoerceUnsized; use core::ptr::{self, Shared}; use heap::deallocate; From 9b5b0cd9b98c3540301ddacdd7882061db31a8ae Mon Sep 17 00:00:00 2001 From: Danilo Bargen Date: Thu, 12 Nov 2015 00:01:55 +0100 Subject: [PATCH 4/7] Fix article in Result.map and Result.map_err documentation --- src/libcore/result.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/result.rs b/src/libcore/result.rs index c111ea8dce667..71219155cc39f 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -456,7 +456,7 @@ impl Result { // Transforming contained values ///////////////////////////////////////////////////////////////////////// - /// Maps a `Result` to `Result` by applying a function to an + /// Maps a `Result` to `Result` by applying a function to a /// contained `Ok` value, leaving an `Err` value untouched. /// /// This function can be used to compose the results of two functions. @@ -484,7 +484,7 @@ impl Result { } } - /// Maps a `Result` to `Result` by applying a function to an + /// Maps a `Result` to `Result` by applying a function to a /// contained `Err` value, leaving an `Ok` value untouched. /// /// This function can be used to pass through a successful result while handling From a5e3625a552b3568343aa2affb81dba45f91054c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 11 Nov 2015 18:54:37 -0500 Subject: [PATCH 5/7] ignore pretty since it dies for some mysterious reason I can't be bothered to track down for a regresson test. /me hopes no one notices --- src/test/run-pass/issue-29740.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/run-pass/issue-29740.rs b/src/test/run-pass/issue-29740.rs index f85b532ed612c..b20bacadf34b7 100644 --- a/src/test/run-pass/issue-29740.rs +++ b/src/test/run-pass/issue-29740.rs @@ -11,6 +11,8 @@ // Regression test for #29740. Inefficient MIR matching algorithms // generated way too much code for this sort of case, leading to OOM. +// ignore-pretty + pub mod KeyboardEventConstants { pub const DOM_KEY_LOCATION_STANDARD: u32 = 0; pub const DOM_KEY_LOCATION_LEFT: u32 = 1; From 4f655382c8f85f95d7bb05a6be46417fad42679a Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 12 Nov 2015 13:20:16 +0530 Subject: [PATCH 6/7] Fixup #29776 --- src/test/run-pass/issue-29740.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/test/run-pass/issue-29740.rs b/src/test/run-pass/issue-29740.rs index b20bacadf34b7..75bcd431ec8e1 100644 --- a/src/test/run-pass/issue-29740.rs +++ b/src/test/run-pass/issue-29740.rs @@ -242,7 +242,8 @@ fn key_from_string(key_string: &str, location: u32) -> Option { "}" => Some(Key::RightBracket), "]" => Some(Key::RightBracket), "Escape" => Some(Key::Escape), - "Enter" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD => Some(Key::Enter), + "Enter" if location == KeyboardEventConstants::DOM_KEY_LOCATION_STANDARD + => Some(Key::Enter), "Tab" => Some(Key::Tab), "Backspace" => Some(Key::Backspace), "Insert" => Some(Key::Insert), @@ -300,16 +301,23 @@ fn key_from_string(key_string: &str, location: u32) -> Option { "*" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpMultiply), "-" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpSubtract), "+" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpAdd), - "Enter" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpEnter), + "Enter" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD + => Some(Key::KpEnter), "=" if location == KeyboardEventConstants::DOM_KEY_LOCATION_NUMPAD => Some(Key::KpEqual), - "Shift" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => Some(Key::LeftShift), - "Control" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => Some(Key::LeftControl), + "Shift" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT + => Some(Key::LeftShift), + "Control" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT + => Some(Key::LeftControl), "Alt" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => Some(Key::LeftAlt), - "Super" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT => Some(Key::LeftSuper), - "Shift" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => Some(Key::RightShift), - "Control" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => Some(Key::RightControl), + "Super" if location == KeyboardEventConstants::DOM_KEY_LOCATION_LEFT + => Some(Key::LeftSuper), + "Shift" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT + => Some(Key::RightShift), + "Control" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT + => Some(Key::RightControl), "Alt" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => Some(Key::RightAlt), - "Super" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT => Some(Key::RightSuper), + "Super" if location == KeyboardEventConstants::DOM_KEY_LOCATION_RIGHT + => Some(Key::RightSuper), "ContextMenu" => Some(Key::Menu), _ => None } From d1d573d8a887a1dfa246dd9a757aa8b42a4856f7 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 12 Nov 2015 13:20:48 +0530 Subject: [PATCH 7/7] Fixup #29785 --- src/libstd/prelude/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/prelude/mod.rs b/src/libstd/prelude/mod.rs index 3ff1798159efa..bda4cdfb43733 100644 --- a/src/libstd/prelude/mod.rs +++ b/src/libstd/prelude/mod.rs @@ -26,7 +26,7 @@ //! extern crate std; //! ``` //! -//! into the crate root of every crate, and +//! into the crate root of every crate, and //! //! ```ignore //! use std::prelude::v1::*;