about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_mir/build/matches/mod.rs207
-rw-r--r--src/librustc_mir/build/matches/test.rs58
-rw-r--r--src/test/run-pass/issue-29740.rs316
3 files changed, 533 insertions, 48 deletions
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 4eda6961a19..cc8549de26a 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<Candidate<'pat, 'tcx>>,
                               mut block: BasicBlock)
+                              -> Vec<BasicBlock>
     {
         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>)
+                             -> 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<BasicBlock>, 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 312ab61ba6c..dffd83f1c41 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<ConstVal>,
                                      indices: &mut FnvHashMap<ConstVal, usize>)
+                                     -> 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<Candidate<'pat, 'tcx>>]) {
+                                resulting_candidates: &mut [Vec<Candidate<'pat, 'tcx>>])
+                                -> 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<Candidate<'pat, 'tcx>>]) {
-        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 00000000000..f85b532ed61
--- /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 <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.
+
+// 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<Key> {
+    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() { }