+ fn parse_switch<T>(
+ &mut self,
+ basic_blocks: &HashMap<IdRef, BasicBlock>,
+ label_id: IdRef,
+ basic_block: &BasicBlock,
+ targets: &[(T, IdRef)],
+ default_label: IdRef,
+ merge_block: IdRef,
+ ) -> Node {
+ get_basic_block(basic_blocks, merge_block).set_kind(BlockKind::SwitchMerge);
+ let mut last_target = None;
+ for &(_, target) in targets {
+ if Some(target) == last_target {
+ continue;
+ }
+ last_target = Some(target);
+ if target != merge_block {
+ get_basic_block(basic_blocks, target)
+ .set_kind(BlockKind::SwitchCase(SwitchCaseKind::Normal));
+ }
+ }
+ if default_label != merge_block {
+ get_basic_block(basic_blocks, default_label)
+ .set_kind(BlockKind::SwitchCase(SwitchCaseKind::Default));
+ }
+ let old_switch = self.push_switch(ParseStateSwitch {
+ default_label: default_label,
+ fallthrough: None,
+ merge_label: merge_block,
+ merges: vec![],
+ fallthrough_target: None,
+ });
+ let default_node = if default_label != merge_block {
+ Some(self.parse(basic_blocks, default_label))
+ } else {
+ None
+ };
+ let mut default_fallthrough = self.get_switch().fallthrough.take();
+ let mut default_fallthrough_target = self.get_switch().fallthrough_target.take();
+ let mut cases = Vec::with_capacity(targets.len());
+ struct Case {
+ node: Node,
+ fallthrough: Option<Rc<SwitchFallthroughNode>>,
+ fallthrough_target: Option<IdRef>,
+ }
+ let mut last_target = None;
+ for (index, &(_, target)) in targets.iter().enumerate() {
+ if Some(target) == last_target {
+ continue;
+ }
+ last_target = Some(target);
+ let node = self.parse(basic_blocks, target);
+ let fallthrough_target = self.get_switch().fallthrough_target.take();
+ if let Some(fallthrough_target) = fallthrough_target {
+ if default_label != fallthrough_target {
+ assert_eq!(
+ Some(fallthrough_target),
+ targets.get(index + 1).map(|v| v.1),
+ "invalid fallthrough branch"
+ );
+ }
+ }
+ cases.push(Case {
+ node,
+ fallthrough: self.get_switch().fallthrough.take(),
+ fallthrough_target,
+ });
+ }
+ let switch = self.pop_switch(old_switch);
+ let mut before_default_cases = None;
+ let mut output_cases = vec![];
+ let mut fallthroughs = vec![];
+ fallthroughs.extend(default_fallthrough);
+ for (
+ index,
+ Case {
+ node,
+ fallthrough,
+ fallthrough_target,
+ },
+ ) in cases.into_iter().enumerate()
+ {
+ if Some(node.label()) == default_fallthrough_target {
+ if before_default_cases.is_none() {
+ before_default_cases = Some(mem::replace(&mut output_cases, vec![]));
+ } else {
+ assert!(output_cases.is_empty(), "invalid fallthrough branch");
+ }
+ }
+ output_cases.push(node);
+ fallthroughs.extend(fallthrough);
+ if Some(default_label) == fallthrough_target {
+ assert!(before_default_cases.is_none());
+ before_default_cases = Some(mem::replace(&mut output_cases, vec![]));
+ }
+ }
+ let before_default_cases =
+ before_default_cases.unwrap_or_else(|| mem::replace(&mut output_cases, vec![]));
+ let default = if let Some(default_node) = default_node {
+ Some(SwitchDefault {
+ default_case: default_node,
+ after_default_cases: output_cases,
+ })
+ } else {
+ None
+ };
+ let next = self.parse(basic_blocks, merge_block);
+ let retval = Rc::new(SwitchNode {
+ label: label_id,
+ instructions: basic_block.get_instructions(),
+ before_default_cases,
+ default,
+ next,
+ });
+ for fallthrough in fallthroughs {
+ fallthrough.switch.replace(Rc::downgrade(&retval));
+ }
+ for merge in switch.merges {
+ merge.switch.replace(Rc::downgrade(&retval));
+ }
+ retval.into()
+ }
+ #[cfg_attr(feature = "cargo-clippy", allow(clippy::cyclomatic_complexity))]