提交 b7f6d723 编写于 作者: M Manish Goregaokar

Rollup merge of #29776 - nikomatsakis:mir-29740, r=nrc

In previous PRs, I changed the match desugaring to generate more efficient code for ints/chars and the like. But this doesn't help when you're matching strings, ranges, or other crazy complex things (leading to #29740). This commit restructures match desugaring *yet again* to handle that case better -- basically we now degenerate to an if-else-if chain in such cases.

~~Note that this builds on https://github.com/rust-lang/rust/pull/29763 which will hopefully land soon. So ignore the first few commits.~~ landed now

r? @Aatch since he's been reviewing the other commits in this series
...@@ -85,7 +85,15 @@ pub fn match_expr(&mut self, ...@@ -85,7 +85,15 @@ pub fn match_expr(&mut self,
// this will generate code to test discriminant_lvalue and // this will generate code to test discriminant_lvalue and
// branch to the appropriate arm block // 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 // all the arm blocks will rejoin here
let end_block = self.cfg.start_new_block(); let end_block = self.cfg.start_new_block();
...@@ -279,11 +287,32 @@ struct Test<'tcx> { ...@@ -279,11 +287,32 @@ struct Test<'tcx> {
// Main matching algorithm // Main matching algorithm
impl<'a,'tcx> Builder<'a,'tcx> { 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, fn match_candidates<'pat>(&mut self,
span: Span, span: Span,
arm_blocks: &mut ArmBlocks, arm_blocks: &mut ArmBlocks,
mut candidates: Vec<Candidate<'pat, 'tcx>>, mut candidates: Vec<Candidate<'pat, 'tcx>>,
mut block: BasicBlock) mut block: BasicBlock)
-> Vec<BasicBlock>
{ {
debug!("matched_candidate(span={:?}, block={:?}, candidates={:?})", debug!("matched_candidate(span={:?}, block={:?}, candidates={:?})",
span, block, candidates); span, block, candidates);
...@@ -311,17 +340,127 @@ fn match_candidates<'pat>(&mut self, ...@@ -311,17 +340,127 @@ fn match_candidates<'pat>(&mut self,
} else { } else {
// if None is returned, then any remaining candidates // if None is returned, then any remaining candidates
// are unreachable (at least not through this path). // are unreachable (at least not through this path).
return; return vec![];
} }
} }
// If there are no candidates that still need testing, we're done. // If there are no candidates that still need testing, we're done.
// Since all matches are exhaustive, execution should never reach this point. // Since all matches are exhaustive, execution should never reach this point.
if candidates.is_empty() { 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 match_pair = &candidates.last().unwrap().match_pairs[0];
let mut test = self.test(match_pair); let mut test = self.test(match_pair);
...@@ -331,35 +470,57 @@ fn match_candidates<'pat>(&mut self, ...@@ -331,35 +470,57 @@ fn match_candidates<'pat>(&mut self,
// available // available
match test.kind { match test.kind {
TestKind::SwitchInt { switch_ty, ref mut options, ref mut indices } => { TestKind::SwitchInt { switch_ty, ref mut options, ref mut indices } => {
for candidate in &candidates { for candidate in candidates.iter().rev() {
self.add_cases_to_switch(&match_pair.lvalue, if !self.add_cases_to_switch(&match_pair.lvalue,
candidate, candidate,
switch_ty, switch_ty,
options, options,
indices); 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); debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair);
let target_blocks = self.perform_test(block, &match_pair.lvalue, &test); let target_blocks = self.perform_test(block, &match_pair.lvalue, &test);
let mut target_candidates: Vec<_> = (0..target_blocks.len()).map(|_| vec![]).collect(); let mut target_candidates: Vec<_> = (0..target_blocks.len()).map(|_| vec![]).collect();
for candidate in &candidates { // Sort the candidates into the appropriate vector in
self.sort_candidate(&match_pair.lvalue, // `target_candidates`. Note that at some point we may
&test, // encounter a candidate where the test is not relevant; at
candidate, // that point, we stop sorting.
&mut target_candidates); let tested_candidates =
} candidates.iter()
.rev()
for (target_block, target_candidates) in .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() target_blocks.into_iter()
.zip(target_candidates.into_iter()) .zip(target_candidates)
{ .flat_map(|(target_block, target_candidates)| {
self.match_candidates(span, arm_blocks, target_candidates, target_block); self.match_candidates(span,
} arm_blocks,
target_candidates,
target_block)
})
.collect();
(otherwise, tested_candidates)
} }
/// Initializes each of the bindings from the candidate by /// Initializes each of the bindings from the candidate by
......
...@@ -105,10 +105,11 @@ pub fn add_cases_to_switch<'pat>(&mut self, ...@@ -105,10 +105,11 @@ pub fn add_cases_to_switch<'pat>(&mut self,
switch_ty: Ty<'tcx>, switch_ty: Ty<'tcx>,
options: &mut Vec<ConstVal>, options: &mut Vec<ConstVal>,
indices: &mut FnvHashMap<ConstVal, usize>) indices: &mut FnvHashMap<ConstVal, usize>)
-> bool
{ {
let match_pair = match candidate.match_pairs.iter().find(|mp| mp.lvalue == *test_lvalue) { let match_pair = match candidate.match_pairs.iter().find(|mp| mp.lvalue == *test_lvalue) {
Some(match_pair) => match_pair, Some(match_pair) => match_pair,
_ => { return; } _ => { return false; }
}; };
match *match_pair.pattern.kind { match *match_pair.pattern.kind {
...@@ -121,11 +122,10 @@ pub fn add_cases_to_switch<'pat>(&mut self, ...@@ -121,11 +122,10 @@ pub fn add_cases_to_switch<'pat>(&mut self,
options.push(value.clone()); options.push(value.clone());
options.len() - 1 options.len() - 1
}); });
true
} }
PatternKind::Range { .. } => { PatternKind::Range { .. } |
}
PatternKind::Constant { .. } | PatternKind::Constant { .. } |
PatternKind::Variant { .. } | PatternKind::Variant { .. } |
PatternKind::Slice { .. } | PatternKind::Slice { .. } |
...@@ -134,6 +134,8 @@ pub fn add_cases_to_switch<'pat>(&mut self, ...@@ -134,6 +134,8 @@ pub fn add_cases_to_switch<'pat>(&mut self,
PatternKind::Binding { .. } | PatternKind::Binding { .. } |
PatternKind::Leaf { .. } | PatternKind::Leaf { .. } |
PatternKind::Deref { .. } => { PatternKind::Deref { .. } => {
// don't know how to add these patterns to a switch
false
} }
} }
} }
...@@ -284,18 +286,29 @@ fn call_comparison_fn(&mut self, ...@@ -284,18 +286,29 @@ fn call_comparison_fn(&mut self,
/// P0` to the `resulting_candidates` entry corresponding to the /// P0` to the `resulting_candidates` entry corresponding to the
/// variant `Some`. /// variant `Some`.
/// ///
/// In many cases we will add the `candidate` to more than one /// However, in some cases, the test may just not be relevant to
/// outcome. For example, say that the test is `x == 22`, but the /// candidate. For example, suppose we are testing whether `foo.x == 22`,
/// candidate is `x @ 13..55`. In that case, if the test is true, /// but in one match arm we have `Foo { x: _, ... }`... in that case,
/// then we know that the candidate applies (without this match /// the test for what value `x` has has no particular relevance
/// pair, potentially, though we don't optimize this due to /// to this candidate. In such cases, this function just returns false
/// #29623). If the test is false, the candidate may also apply /// without doing anything. This is used by the overall `match_candidates`
/// (with the match pair, still). /// 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, pub fn sort_candidate<'pat>(&mut self,
test_lvalue: &Lvalue<'tcx>, test_lvalue: &Lvalue<'tcx>,
test: &Test<'tcx>, test: &Test<'tcx>,
candidate: &Candidate<'pat, '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, // Find the match_pair for this lvalue (if any). At present,
// afaik, there can be at most one. (In the future, if we // afaik, there can be at most one. (In the future, if we
// adopted a more general `@` operator, there might be more // adopted a more general `@` operator, there might be more
...@@ -311,7 +324,7 @@ pub fn sort_candidate<'pat>(&mut self, ...@@ -311,7 +324,7 @@ pub fn sort_candidate<'pat>(&mut self,
None => { None => {
// We are not testing this lvalue. Therefore, this // We are not testing this lvalue. Therefore, this
// candidate applies to ALL outcomes. // candidate applies to ALL outcomes.
return self.add_to_all_candidate_sets(candidate, resulting_candidates); return false;
} }
}; };
...@@ -329,9 +342,10 @@ pub fn sort_candidate<'pat>(&mut self, ...@@ -329,9 +342,10 @@ pub fn sort_candidate<'pat>(&mut self,
subpatterns, subpatterns,
candidate); candidate);
resulting_candidates[variant_index].push(new_candidate); resulting_candidates[variant_index].push(new_candidate);
true
} }
_ => { _ => {
self.add_to_all_candidate_sets(candidate, resulting_candidates); false
} }
} }
} }
...@@ -349,9 +363,10 @@ pub fn sort_candidate<'pat>(&mut self, ...@@ -349,9 +363,10 @@ pub fn sort_candidate<'pat>(&mut self,
let new_candidate = self.candidate_without_match_pair(match_pair_index, let new_candidate = self.candidate_without_match_pair(match_pair_index,
candidate); candidate);
resulting_candidates[index].push(new_candidate); resulting_candidates[index].push(new_candidate);
true
} }
_ => { _ => {
self.add_to_all_candidate_sets(candidate, resulting_candidates); false
} }
} }
} }
...@@ -367,8 +382,9 @@ pub fn sort_candidate<'pat>(&mut self, ...@@ -367,8 +382,9 @@ pub fn sort_candidate<'pat>(&mut self,
let new_candidate = self.candidate_without_match_pair(match_pair_index, let new_candidate = self.candidate_without_match_pair(match_pair_index,
candidate); candidate);
resulting_candidates[0].push(new_candidate); resulting_candidates[0].push(new_candidate);
true
} else { } else {
self.add_to_all_candidate_sets(candidate, resulting_candidates); false
} }
} }
} }
...@@ -392,14 +408,6 @@ fn candidate_without_match_pair<'pat>(&mut self, ...@@ -392,14 +408,6 @@ fn candidate_without_match_pair<'pat>(&mut self,
} }
} }
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, fn candidate_after_variant_switch<'pat>(&mut self,
match_pair_index: usize, match_pair_index: usize,
adt_def: ty::AdtDef<'tcx>, adt_def: ty::AdtDef<'tcx>,
...@@ -447,5 +455,5 @@ fn error_simplifyable<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> ! ...@@ -447,5 +455,5 @@ fn error_simplifyable<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> !
} }
fn is_switch_ty<'tcx>(ty: Ty<'tcx>) -> bool { fn is_switch_ty<'tcx>(ty: Ty<'tcx>) -> bool {
ty.is_integral() || ty.is_char() ty.is_integral() || ty.is_char() || ty.is_bool()
} }
// 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.
// ignore-pretty
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() { }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册