Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

segfault in panic=abort mode #62

Closed
dwrensha opened this issue May 8, 2017 · 9 comments
Closed

segfault in panic=abort mode #62

dwrensha opened this issue May 8, 2017 · 9 comments

Comments

@dwrensha
Copy link

dwrensha commented May 8, 2017

Consider the following program:

// parse.rs

extern crate fuzzy_pickles;

fn main() {
    let _ = ::fuzzy_pickles::parse_rust_file("fn main () {iwuo&=rl");
}

I expect it to exit normally with no output; the parser fails, but we discard the error. And indeed this is exactly the behavior I observe when I run the program in debug mode. However, when I run it in release mode with panic=abort, with this Cargo.toml:

# Cargo.toml

[package]
authors = ["David Renshaw <[email protected]>"]
name = "fuzzy-pickles-test"
version = "0.0.1"

[dependencies]
fuzzy-pickles = { git = "https://github.com/shepmaster/fuzzy-pickles" }

[[bin]]
name = "parse"
path = "parse.rs"

[profile.release]
panic = "abort"

then the program exits with a segmentation fault.

$ rustc --version
rustc 1.19.0-nightly (f4209651e 2017-05-05)
$ cargo build --release
   Compiling unicode-xid v0.0.4
   Compiling peresil v0.3.0 (https://github.com/shepmaster/peresil#64e6cafe)
   Compiling quote v0.3.15
   Compiling synom v0.11.3
   Compiling syn v0.11.11
   Compiling fuzzy-pickles-derive v0.1.0 (https://github.com/shepmaster/fuzzy-pickles#5e43809e)
   Compiling fuzzy-pickles v0.1.0 (https://github.com/shepmaster/fuzzy-pickles#5e43809e)
   Compiling fuzzy-pickles-test v0.0.1 (file:///home/dwrensha/Desktop/test-fuzzy-pickles)
    Finished release [optimized] target(s) in 57.96 secs
$ ./target/release/parse
Segmentation fault
@dwrensha
Copy link
Author

dwrensha commented May 8, 2017

gdb backtrace:

Program received signal SIGSEGV, Segmentation fault.
0x00005555555f2967 in fuzzy_pickles::expression_x::h9e6cba5ea5161aa4 ()
Missing separate debuginfos, use: dnf debuginfo-install libgcc-6.3.1-1.fc25.x86_64
(gdb) bt
#0  0x00005555555f2967 in fuzzy_pickles::expression_x::h9e6cba5ea5161aa4 ()
#1  0x00005555555c92b7 in _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::hf8bb5c933f031b41 ()
#2  0x00005555555e413d in fuzzy_pickles::block::hdcf4e0581a4b9d0c ()
#3  0x00005555555de897 in fuzzy_pickles::function::hc6a33a6b6ac415de ()
#4  0x0000555555588959 in _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::h2b469aa9bcf4cccf ()
#5  0x00005555555de3a9 in fuzzy_pickles::item::hc56dc041ecbfe15e ()
#6  0x00005555555d320c in fuzzy_pickles::parse_rust_file::h30d24f5809fa290d ()
#7  0x000055555555fcc4 in parse::main::hd5bcb1aa97025aba ()
#8  0x000055555562337c in panic_abort::__rust_maybe_catch_panic () at /checkout/src/libpanic_abort/lib.rs:40
#9  0x000055555561d4a2 in std::panicking::try<(),fn()> () at /checkout/src/libstd/panicking.rs:433
#10 std::panic::catch_unwind<fn(),()> () at /checkout/src/libstd/panic.rs:361
#11 std::rt::lang_start () at /checkout/src/libstd/rt.rs:57
#12 0x00007ffff71f0401 in __libc_start_main () from /lib64/libc.so.6
#13 0x000055555555a59a in _start ()

@dwrensha
Copy link
Author

dwrensha commented May 8, 2017

valgrind output:

$ valgrind ./target/release/parse          
==26792== Memcheck, a memory error detector
==26792== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==26792== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==26792== Command: ./target/release/parse
==26792== 
==26792== Conditional jump or move depends on uninitialised value(s)
==26792==    at 0x158E13: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::h913b82722c20eea8 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x162145: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::haf2919a5cee36c2c (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x196620: fuzzy_pickles::self_argument::h595398febfd8ab93 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x192FDC: fuzzy_pickles::function_header::hdf0b5739e7046115 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x19280B: fuzzy_pickles::function::hc6a33a6b6ac415de (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x13C958: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::h2b469aa9bcf4cccf (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x1923A8: fuzzy_pickles::item::hc56dc041ecbfe15e (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x18720B: fuzzy_pickles::parse_rust_file::h30d24f5809fa290d (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x113CC3: parse::main::hd5bcb1aa97025aba (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x1D737B: __rust_maybe_catch_panic (lib.rs:40)
==26792==    by 0x1D14A1: try<(),fn()> (panicking.rs:433)
==26792==    by 0x1D14A1: catch_unwind<fn(),()> (panic.rs:361)
==26792==    by 0x1D14A1: std::rt::lang_start::h8126425a59d3b4a0 (rt.rs:57)
==26792==    by 0x569B400: (below main) (in /usr/lib64/libc-2.24.so)
==26792== 
==26792== Conditional jump or move depends on uninitialised value(s)
==26792==    at 0x1623FE: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::haf2919a5cee36c2c (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x196620: fuzzy_pickles::self_argument::h595398febfd8ab93 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x192FDC: fuzzy_pickles::function_header::hdf0b5739e7046115 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x19280B: fuzzy_pickles::function::hc6a33a6b6ac415de (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x13C958: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::h2b469aa9bcf4cccf (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x1923A8: fuzzy_pickles::item::hc56dc041ecbfe15e (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x18720B: fuzzy_pickles::parse_rust_file::h30d24f5809fa290d (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x113CC3: parse::main::hd5bcb1aa97025aba (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x1D737B: __rust_maybe_catch_panic (lib.rs:40)
==26792==    by 0x1D14A1: try<(),fn()> (panicking.rs:433)
==26792==    by 0x1D14A1: catch_unwind<fn(),()> (panic.rs:361)
==26792==    by 0x1D14A1: std::rt::lang_start::h8126425a59d3b4a0 (rt.rs:57)
==26792==    by 0x569B400: (below main) (in /usr/lib64/libc-2.24.so)
==26792== 
==26792== Conditional jump or move depends on uninitialised value(s)
==26792==    at 0x162505: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::haf2919a5cee36c2c (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x196620: fuzzy_pickles::self_argument::h595398febfd8ab93 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x192FDC: fuzzy_pickles::function_header::hdf0b5739e7046115 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x19280B: fuzzy_pickles::function::hc6a33a6b6ac415de (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x13C958: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::h2b469aa9bcf4cccf (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x1923A8: fuzzy_pickles::item::hc56dc041ecbfe15e (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x18720B: fuzzy_pickles::parse_rust_file::h30d24f5809fa290d (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x113CC3: parse::main::hd5bcb1aa97025aba (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x1D737B: __rust_maybe_catch_panic (lib.rs:40)
==26792==    by 0x1D14A1: try<(),fn()> (panicking.rs:433)
==26792==    by 0x1D14A1: catch_unwind<fn(),()> (panic.rs:361)
==26792==    by 0x1D14A1: std::rt::lang_start::h8126425a59d3b4a0 (rt.rs:57)
==26792==    by 0x569B400: (below main) (in /usr/lib64/libc-2.24.so)
==26792== 
==26792== Conditional jump or move depends on uninitialised value(s)
==26792==    at 0x1448E9: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::h4521630d21966471 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x141378: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::h399d0d6b1f01d420 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x172703: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::hd475f4bf0a3a19a0 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x1A5B0F: fuzzy_pickles::expression_x::h9e6cba5ea5161aa4 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x17D2B6: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::hf8bb5c933f031b41 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x19813C: fuzzy_pickles::block::hdcf4e0581a4b9d0c (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x192896: fuzzy_pickles::function::hc6a33a6b6ac415de (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x13C958: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::h2b469aa9bcf4cccf (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x1923A8: fuzzy_pickles::item::hc56dc041ecbfe15e (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x18720B: fuzzy_pickles::parse_rust_file::h30d24f5809fa290d (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x113CC3: parse::main::hd5bcb1aa97025aba (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x1D737B: __rust_maybe_catch_panic (lib.rs:40)
==26792== 
==26792== Invalid read of size 4
==26792==    at 0x1A6967: fuzzy_pickles::expression_x::h9e6cba5ea5161aa4 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x17D2B6: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::hf8bb5c933f031b41 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x19813C: fuzzy_pickles::block::hdcf4e0581a4b9d0c (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x192896: fuzzy_pickles::function::hc6a33a6b6ac415de (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x13C958: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::h2b469aa9bcf4cccf (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x1923A8: fuzzy_pickles::item::hc56dc041ecbfe15e (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x18720B: fuzzy_pickles::parse_rust_file::h30d24f5809fa290d (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x113CC3: parse::main::hd5bcb1aa97025aba (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x1D737B: __rust_maybe_catch_panic (lib.rs:40)
==26792==    by 0x1D14A1: try<(),fn()> (panicking.rs:433)
==26792==    by 0x1D14A1: catch_unwind<fn(),()> (panic.rs:361)
==26792==    by 0x1D14A1: std::rt::lang_start::h8126425a59d3b4a0 (rt.rs:57)
==26792==    by 0x569B400: (below main) (in /usr/lib64/libc-2.24.so)
==26792==  Address 0x18adf5ec is not stack'd, malloc'd or (recently) free'd
==26792== 
==26792== 
==26792== Process terminating with default action of signal 11 (SIGSEGV)
==26792==  Access not within mapped region at address 0x18ADF5EC
==26792==    at 0x1A6967: fuzzy_pickles::expression_x::h9e6cba5ea5161aa4 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x17D2B6: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::hf8bb5c933f031b41 (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x19813C: fuzzy_pickles::block::hdcf4e0581a4b9d0c (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x192896: fuzzy_pickles::function::hc6a33a6b6ac415de (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x13C958: _$LT$peresil..Alternate$LT$$u27$pm$C$$u20$P$C$$u20$T$C$$u20$E$C$$u20$S$GT$$GT$::run_one::h2b469aa9bcf4cccf (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x1923A8: fuzzy_pickles::item::hc56dc041ecbfe15e (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x18720B: fuzzy_pickles::parse_rust_file::h30d24f5809fa290d (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x113CC3: parse::main::hd5bcb1aa97025aba (in /home/dwrensha/Desktop/test-fuzzy-pickles/target/release/parse)
==26792==    by 0x1D737B: __rust_maybe_catch_panic (lib.rs:40)
==26792==    by 0x1D14A1: try<(),fn()> (panicking.rs:433)
==26792==    by 0x1D14A1: catch_unwind<fn(),()> (panic.rs:361)
==26792==    by 0x1D14A1: std::rt::lang_start::h8126425a59d3b4a0 (rt.rs:57)
==26792==    by 0x569B400: (below main) (in /usr/lib64/libc-2.24.so)
==26792==  If you believe this happened as a result of a stack
==26792==  overflow in your program's main thread (unlikely but
==26792==  possible), you can try to increase the size of the
==26792==  main thread stack using the --main-stacksize= flag.
==26792==  The main thread stack size used in this run was 8388608.
==26792== 
==26792== HEAP SUMMARY:
==26792==     in use at exit: 32 bytes in 1 blocks
==26792==   total heap usage: 6 allocs, 5 frees, 2,000 bytes allocated
==26792== 
==26792== LEAK SUMMARY:
==26792==    definitely lost: 0 bytes in 0 blocks
==26792==    indirectly lost: 0 bytes in 0 blocks
==26792==      possibly lost: 0 bytes in 0 blocks
==26792==    still reachable: 32 bytes in 1 blocks
==26792==         suppressed: 0 bytes in 0 blocks
==26792== Rerun with --leak-check=full to see details of leaked memory
==26792== 
==26792== For counts of detected and suppressed errors, rerun with: -v
==26792== Use --track-origins=yes to see where uninitialised values come from
==26792== ERROR SUMMARY: 7 errors from 5 contexts (suppressed: 0 from 0)
Segmentation fault

@dwrensha
Copy link
Author

dwrensha commented May 8, 2017

My guess is that this is a bug in rustc.

It's interesting to note that the control flow where the error happens is similar to the control flow in a previous bug.

@shepmaster
Copy link
Owner

Whew! Any suggestions on how to minimize this in order to get something submittable as a bug report?

@dwrensha
Copy link
Author

dwrensha commented May 8, 2017

I wish I had a way to automate the minimization!

I've started an attempt to manually minimize the code...

@dwrensha
Copy link
Author

dwrensha commented May 9, 2017

Here's a smaller more self-contained program that segfaults using the latest nightly rustc 1.19.0-nightly (f1140a331 2017-05-08)

# Cargo.toml

[package]
authors = ["David Renshaw <[email protected]>"]
name = "fuzzy-pickles-test"
version = "0.0.2"

[dependencies.peresil]
git = "https://github.com/shepmaster/peresil"
features = ["combinators"]

[[bin]]
name = "parse"
path = "parse.rs"

[profile.release]
panic = "abort"
parse.rs
// parse.rs

#![feature(conservative_impl_trait)]

#[macro_use]
extern crate peresil;

use std::fmt;

use peresil::combinators::*;

type Point<'s> = TokenPoint<'s, Token>;
type Master<'s> = peresil::ParseMaster<Point<'s>, Error, State<'s>>;
type Progress<'s, T> = peresil::Progress<Point<'s>, T, Error>;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Token {
    AmpersandEquals(Extent),
    Ident(Extent),
    EndOfFile(Extent),
}

impl Token {
    fn extent(&self) -> Extent {
        use self::Token::*;
        match *self {
            AmpersandEquals(s) | EndOfFile(s) | Ident(s) => s,
        }
    }

    fn into_double_colon1(self) -> Option<Extent> { None }

    fn into_ident1(self) -> Option<Extent> {
        match self {
            Token::Ident(s) => Some(s),
            _ => None,
        }
    }

    fn into_ampersand_equals1(self) -> Option<Extent> {
        match self {
            Token::AmpersandEquals(s) => Some(s),
            _ => None,
        }
    }
}

fn main() {
    let _ = parse_rust_file();
}

#[derive(Debug)]
struct TokenPoint<'s, T: 's> {
    pub offset: usize,
    pub sub_offset: Option<u8>,
    pub s: &'s [T],
}

impl<'s, T: 's> TokenPoint<'s, T> {
    fn new(slice: &'s [T]) -> Self {
        TokenPoint {
            offset: 0,
            sub_offset: None,
            s: slice,
        }
    }

    fn advance_by(&self, offset: usize) -> Self {
        TokenPoint {
            offset: self.offset + offset,
            sub_offset: None,
            s: &self.s[offset..],
        }
    }

    fn location(&self) -> (usize, Option<u8>) {
        (self.offset, self.sub_offset)
    }
}

impl<'s, T> peresil::Point for TokenPoint<'s, T> {
    fn zero() -> Self {
        Self::new(&[])
    }
}

impl<'s, T> Copy for TokenPoint<'s, T> {}
impl<'s, T> Clone for TokenPoint<'s, T> {
    fn clone(&self) -> Self { *self }
}

impl<'s, T> PartialOrd for TokenPoint<'s, T> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl<'s, T> Ord for TokenPoint<'s, T> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.location().cmp(&other.location())
    }
}

impl<'s, T> PartialEq for TokenPoint<'s, T> {
    fn eq(&self, other: &Self) -> bool {
        self.location().eq(&other.location())
    }
}

impl<'s, T> Eq for TokenPoint<'s, T> {}

// -----

#[derive(Debug, Default)]
struct State<'s> {
    ignore_struct_literals: bool,
    tokens: &'s [Token],
}

impl<'s> State<'s> {
    #[allow(dead_code)]
    fn new(tokens: &'s [Token]) -> Self {
        State {
            tokens,
            ..State::default()
        }
    }

    fn ex(&self, start: Point, end: Point) -> Extent {
        if end.offset > start.offset {
            let (a, _) = self.tokens[start.offset].extent();
            let (_, b) = self.tokens[end.offset-1].extent();
            (a, b)
        } else {
            self.tokens[start.offset].extent()
        }
    }
}

// define an error type - emphasis on errors. Need to implement Recoverable (more to discuss.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Error {
    ExpectedAmpersandEquals,
    ExpectedDoubleColon,
    ExpectedIdent,
    ExpectedExpression,
}

impl peresil::Recoverable for Error {
    fn recoverable(&self) -> bool { true }
}

#[derive(Debug, PartialEq)]
pub enum ErrorDetail {
    Tokenizer,
    Parser(ParserErrorDetail),
}

impl From<ParserErrorDetail> for ErrorDetail {
    fn from(other: ParserErrorDetail) -> Self {
        ErrorDetail::Parser(other)
    }
}

#[derive(Debug)]
pub struct ErrorDetailText<'a> {
    detail: &'a ErrorDetail,
    text: &'a str,
}

impl<'a> fmt::Display for ErrorDetailText<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Ok(())
    }
}

#[derive(Debug, PartialEq)]
pub struct ParserErrorDetail {
    location: usize,
}

impl ParserErrorDetail {
    pub fn with_text<'a>(&'a self, text: &'a str) -> ParserErrorDetailText<'a> {
        ParserErrorDetailText { detail: self, text }
    }
}

#[derive(Debug)]
pub struct ParserErrorDetailText<'a> {
    detail: &'a ParserErrorDetail,
    text: &'a str,
}

impl<'a> fmt::Display for ParserErrorDetailText<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Ok(()) }
}

pub fn parse_rust_file() -> Result<(), ErrorDetail> {

    let tokens = &[Token::Ident((0,1)), Token::AmpersandEquals((1,3)), Token::Ident((3,4)),
                   Token::EndOfFile((4,4))];
    println!("tokens {:?}", tokens);

    let pt = Point::new(tokens);
    let mut pm = Master::with_state(State::new(tokens));
    let _ = expression(&mut pm, pt);

    Ok(())
}

// TODO: enum variants track whole extent, enum delegates

pub type Extent = (usize, usize);

#[derive(Debug, Copy, Clone)]
pub struct Ident {
    pub extent: Extent,
}

#[derive(Debug)]
pub struct Path {
    extent: Extent,
    components: Vec<Ident>,
}

#[derive(Debug)]
pub struct PathedIdent {
    extent: Extent,
    components: Vec<PathComponent>,
}

#[derive(Debug)]
struct PathComponent {
    extent: Extent,
    ident: Ident,
}

impl From<Ident> for PathedIdent {
    fn from(other: Ident) -> PathedIdent {
        PathedIdent { extent: other.extent, components: vec![
            PathComponent { extent: other.extent, ident: other, },
        ] }
    }
}

#[derive(Debug)]
enum Expression {
    AsType(AsType),
    Binary(Binary),
    Call(Call),
    FieldAccess(FieldAccess),
    Slice(Slice),
    Value(Value),
}

#[derive(Debug)]
struct FieldAccess {
    extent: Extent,
    target: Box<Expression>,
}

#[derive(Debug)]
pub struct Value {
    extent: Extent,
    name: PathedIdent,
}

#[derive(Debug)]
pub struct Call {
    extent: Extent,
    target: Box<Expression>,
    args: Vec<Expression>,
}

#[derive(Debug)]
pub struct Binary {
    extent: Extent,
    op: BinaryOp,
    lhs: Box<Expression>,
    rhs: Box<Expression>,
}

#[derive(Debug)]
pub enum BinaryOp {
    BitwiseAndAssign,
}

#[derive(Debug)]
struct AsType {
    extent: Extent,
    target: Box<Expression>,
}

#[derive(Debug)]
struct Slice {
    extent: Extent,
    target: Box<Expression>,
    index: Box<Expression>,
}

// --------------------------------------------------

enum TailedState<P, T, E> {
    Nothing(P, E),
    ValueOnly(P, T),
}

fn parse_tailed<'s, F, S, T, U>(sep: S, f: F, pm: &mut Master<'s>, pt: Point<'s>) ->
    TailedState<Point<'s>, T, Error>
    where S: Fn(&mut Master<'s>, Point<'s>) -> Progress<'s, U>,
          F: Fn(&mut Master<'s>, Point<'s>) -> Progress<'s, T>,

{
    match f(pm, pt) {
        Progress { status: peresil::Status::Failure(f), point } => {
            TailedState::Nothing(point, f)
        }
        Progress { status: peresil::Status::Success(value), point } => {
            match sep(pm, point) {
                Progress { status: peresil::Status::Failure(_), point } => {
                    TailedState::ValueOnly(point, value)
                }
                Progress { status: peresil::Status::Success(_), .. } => {
                    unimplemented!()
                }
            }
        }
    }
}

#[derive(Debug)]
struct Tailed<T> {
    values: Vec<T>,
    separator_count: usize,
    last_had_separator: bool,
}

impl<T> Default for Tailed<T> {
    fn default() -> Self {
        Tailed {
            values: Vec::new(),
            separator_count: 0,
            last_had_separator: false,
        }
    }
}

fn one_or_more_tailed<'s, S, F, T, U>(sep: S, f: F) ->
    impl FnOnce(&mut Master<'s>, Point<'s>) -> Progress<'s, Tailed<T>>
    where S: Fn(&mut Master<'s>, Point<'s>) -> Progress<'s, U>,
          F: Fn(&mut Master<'s>, Point<'s>) -> Progress<'s, T>
{
    move |pm, pt| {
        let mut tailed = Tailed::default();

        match parse_tailed(&sep, &f, pm, pt) {
            TailedState::Nothing(pt, f) => {
                return Progress::failure(pt, f);
            }
            TailedState::ValueOnly(pt, v) => {
                tailed.values.push(v);
                return Progress::success(pt, tailed);
            }
        }
    }
}

fn one_or_more_tailed_values<'s, S, F, T, U>(sep: S, f: F) ->
    impl FnOnce(&mut Master<'s>, Point<'s>) -> Progress<'s, Vec<T>>
    where S: Fn(&mut Master<'s>, Point<'s>) -> Progress<'s, U>,
          F: Fn(&mut Master<'s>, Point<'s>) -> Progress<'s, T>
{
    map(one_or_more_tailed(sep, f), |t| t.values)
}

// --------------------------------------------------

macro_rules! shim {
    ($name:ident, $matcher:expr, $error:expr) => {
        shim!($name, $matcher, $error, Extent);
    };
    ($name:ident, $matcher:expr, $error:expr, $t:ty) => {
        fn $name<'s>(pm: &mut Master<'s>, pt: Point<'s>) -> Progress<'s, $t> {
            token($matcher, $error)(pm, pt)
        }
    };
}

shim!(ident_normal, Token::into_ident1, Error::ExpectedIdent);
shim!(ampersand_equals, Token::into_ampersand_equals1, Error::ExpectedAmpersandEquals);
shim!(double_colon, Token::into_double_colon1, Error::ExpectedDoubleColon);

fn token<'s, F, T>(token_convert: F, error: Error) ->
    impl FnOnce(&mut Master<'s>, Point<'s>) -> Progress<'s, T>
    where F: Fn(Token) -> Option<T>
{
    move |_, pt| {
        let original_token = match pt.s.first() {
            Some(&token) => token,
            None => return Progress::failure(pt, error),
        };

        match token_convert(original_token) {
            Some(v) => {
                // We exactly matched the requested token
                Progress::success(pt.advance_by(1), v)
            }
            None => {
                Progress::failure(pt, error)
            }
        }
    }
}

fn ident<'s>(pm: &mut Master<'s>, pt: Point<'s>) -> Progress<'s, Ident> {
    pm.alternate(pt)
        .one(ident_normal)
        .finish()
        .map(|extent| Ident { extent })
        .map_err(|_| Error::ExpectedIdent)
}

#[derive(Debug)]
enum OperatorInfix {
    BitwiseAndAssign(Extent),
}

#[derive(Debug)]
enum OperatorPostfix {
    AsType { typ: () },
    Call { args: Vec<Expression> },
    FieldAccess { field: () },
    Slice { index: Expression },
}

#[derive(Debug)]
enum OperatorKind {
    Infix(OperatorInfix),
    Postfix(OperatorPostfix),
}

type Precedence = u8;

impl OperatorKind {
    fn precedence(&self) -> Precedence {
        use OperatorKind::*;

        match *self {
            Infix(_) => 10,
            Postfix(OperatorPostfix::Call { .. }) => 10,
            Postfix(_) => 20,
        }
    }
}

#[derive(Debug)]
enum PrefixOrAtom {
    Atom(Expression),
}

#[derive(Debug)]
enum InfixOrPostfix {
    Infix(OperatorInfix),
    Postfix(OperatorPostfix),
}

fn expression_prefix_or_atom<'s>(pm: &mut Master<'s>, pt: Point<'s>) ->
    Progress<'s, PrefixOrAtom>
{
    pm.alternate(pt)
        .one(map(expression_atom, PrefixOrAtom::Atom))
        .finish()
}

fn expression_infix_or_postfix<'s>(pm: &mut Master<'s>, pt: Point<'s>) ->
    Progress<'s, InfixOrPostfix>
{
    pm.alternate(pt)
        .one(map(operator_infix, InfixOrPostfix::Infix))
        .finish()
}

fn operator_infix<'s>(pm: &mut Master<'s>, pt: Point<'s>) ->
    Progress<'s, OperatorInfix>
{
    pm.alternate(pt)
        .one(map(ampersand_equals, OperatorInfix::BitwiseAndAssign))
        .finish()
}

fn expression_atom<'s>(pm: &mut Master<'s>, pt: Point<'s>) -> Progress<'s, Expression> {
    pm.alternate(pt)
        .one(map(expr_value, Expression::Value))
        .finish()
}

struct ShuntCar<'s, T> {
    value: T,
    spt: Point<'s>,
    ept: Point<'s>,
}

struct ShuntingYard<'s> {
    operators: Vec<ShuntCar<'s, OperatorKind>>,
    result: Vec<ShuntCar<'s, Expression>>,
}

type PointRange<'s> = std::ops::Range<Point<'s>>;
type ExprResult<'s, T> = std::result::Result<T, (Point<'s>, Error)>;

impl<'s> ShuntingYard<'s> {
    fn new() -> Self {
        ShuntingYard {
            operators: Vec::new(),
            result: Vec::new(),
        }
    }

    fn add_expression(&mut self, expr: Expression, spt: Point<'s>, ept: Point<'s>) {
        self.result.push(ShuntCar { value: expr, spt, ept });
    }

    fn add_infix(&mut self, pm: &Master, op: OperatorInfix, spt: Point<'s>, ept: Point<'s>) ->
        ExprResult<'s, ()>
    {
        let op = OperatorKind::Infix(op);
        self.apply_precedence(pm, &op)?;
        self.operators.push(ShuntCar { value: op, spt, ept });
        Ok(())
    }

    fn finish(mut self, pm: &Master, failure_point: Point<'s>) ->
        ExprResult<'s, ShuntCar<'s, Expression>>
    {
        self.apply_all(pm)?;

        let r = self.pop_expression(failure_point);
        assert_eq!(0, self.result.len());
        r
    }

    fn apply_precedence(&mut self, pm: &Master, operator: &OperatorKind) -> ExprResult<'s, ()>  {
        let op_precedence = operator.precedence();
        while self.operators.last().map_or(false, |&ShuntCar { value: ref top, .. }| top.precedence() > op_precedence) {
            let ShuntCar { value, spt, ept } = self.operators.pop()
                .expect("Cannot pop operator that was just there");
            self.apply_one(pm, value, spt..ept)?;
        }
        Ok(())
    }

    fn apply_all(&mut self, pm: &Master) -> ExprResult<'s, ()> {
        while let Some(ShuntCar { value, spt, ept }) = self.operators.pop() {
            self.apply_one(pm, value, spt..ept)?;
        }
        Ok(())
    }

    fn apply_one(&mut self, pm: &Master, op: OperatorKind, op_range: PointRange<'s>) ->
        ExprResult<'s, ()>
    {
        use OperatorKind::*;

        match op {
            Infix(OperatorInfix::BitwiseAndAssign(..)) => self.apply_binary(pm, op_range, BinaryOp::BitwiseAndAssign),
            Postfix(OperatorPostfix::FieldAccess { .. }) => unimplemented!(),
            Postfix(OperatorPostfix::Call { args }) => unimplemented!(),
            Postfix(OperatorPostfix::Slice { .. }) => unimplemented!(),
            Postfix(OperatorPostfix::AsType { .. }) => unimplemented!(),
        }
    }

    fn apply_infix<F>(&mut self, pm: &Master, op_range: PointRange<'s>, f: F) ->
        ExprResult<'s, ()>
        where F: FnOnce(Extent, Expression, Expression) -> Expression
    {
        let ShuntCar { value: rhs, ept: rexpr_ept, .. } = self.pop_expression(op_range.end)?;
        let ShuntCar { value: lhs, spt: lexpr_spt, .. } = self.pop_expression(op_range.start)?;
        let extent = pm.state.ex(lexpr_spt, rexpr_ept);
        let new_expr = f(extent, lhs, rhs);
        self.result.push(ShuntCar { value: new_expr, spt: lexpr_spt, ept: rexpr_ept });
        Ok(())
    }

    fn apply_binary(&mut self, pm: &Master, op_range: PointRange<'s>, op: BinaryOp) ->
        ExprResult<'s, ()>
    {
        self.apply_infix(pm, op_range, |extent, lhs, rhs| {
            Expression::Binary(Binary {
                extent,
                op,
                lhs: Box::new(lhs),
                rhs: Box::new(rhs),
            })
        })
    }

    fn pop_expression(&mut self, location: Point<'s>) ->
        ExprResult<'s, ShuntCar<'s, Expression>>
    {
        self.result.pop().ok_or((location, Error::ExpectedExpression))
    }
}

#[derive(Debug, Copy, Clone)]
enum ExpressionState {
    Prefix, // Also "beginning of expression"
    Infix,
    Postfix,
    Atom,
}

fn expression<'s>(pm: &mut Master<'s>, pt: Point<'s>) -> Progress<'s, Expression> {
    match expression_x(pm, pt) {
        Ok(ShuntCar { value: expr, ept, .. }) => Progress::success(ept, expr),
        Err((failure_point, err)) => Progress::failure(failure_point, err),
    }
}

fn expression_x<'s>(pm: &mut Master<'s>, mut pt: Point<'s>) ->
    ExprResult<'s, ShuntCar<'s, Expression>>
{
    let mut shunting_yard = ShuntingYard::new();
    let mut state = ExpressionState::Prefix;

    println!("expression_x");

    loop {
        println!("expression_x loop");
        match state {
            ExpressionState::Prefix |
            ExpressionState::Infix => {
                println!("branch 1");
                match expression_prefix_or_atom(pm, pt) {
                    peresil::Progress { status: peresil::Status::Success(op_or_atom), point } => {
                        match op_or_atom {
                            PrefixOrAtom::Atom(expr) => {
                                shunting_yard.add_expression(expr, pt, point);
                                state = ExpressionState::Atom;
                            }
                        }
                        pt = point;
                    }
                    peresil::Progress { status: peresil::Status::Failure(_), .. } => {
                        unimplemented!()
                    }
                }
            }
            ExpressionState::Postfix |
            ExpressionState::Atom => {
                println!("branch 2");
                match expression_infix_or_postfix(pm, pt) {
                    peresil::Progress { status: peresil::Status::Success(infix_or_postfix), point } => {
                        match infix_or_postfix {
                            InfixOrPostfix::Infix(op) => {
                                shunting_yard.add_infix(pm, op, pt, point)?;
                                state = ExpressionState::Infix;
                            }
                            InfixOrPostfix::Postfix(_) => unimplemented!(),
                        }
                        pt = point;
                    }
                    peresil::Progress { status: peresil::Status::Failure(_), point } => {
                        return shunting_yard.finish(pm, point);
                    }
                }
            }
        }
    }
}

fn expr_value<'s>(pm: &mut Master<'s>, pt: Point<'s>) -> Progress<'s, Value> {
    if pm.state.ignore_struct_literals {
        sequence!(pm, pt, {
            spt  = point;
            name = pathed_ident;
        }, |pm: &mut Master, pt| Value { extent: pm.state.ex(spt, pt), name })
    } else {
        sequence!(pm, pt, {
            spt     = point;
            name    = pathed_ident;
        }, |pm: &mut Master, pt| Value { extent: pm.state.ex(spt, pt), name })
    }
}

fn pathed_ident<'s>(pm: &mut Master<'s>, pt: Point<'s>) -> Progress<'s, PathedIdent> {
    sequence!(pm, pt, {
        spt        = point;
        components = one_or_more_tailed_values(double_colon, path_component);
    }, |pm: &mut Master, pt| PathedIdent { extent: pm.state.ex(spt, pt), components })
}

fn path_component<'s>(pm: &mut Master<'s>, pt: Point<'s>) -> Progress<'s, PathComponent> {
    sequence!(pm, pt, {
        spt       = point;
        ident     = ident;
    }, |pm: &mut Master, pt| PathComponent { extent: pm.state.ex(spt, pt), ident })
}

@dwrensha
Copy link
Author

Here's a smaller version without any dependencies. It segfaults on beta and nightly but not on stable:

# Cargo.toml

[package]
authors = ["David Renshaw <[email protected]>"]
name = "fuzzy-pickles-test"
version = "0.0.3"

[[bin]]
name = "parse"
path = "parse.rs"

[profile.release]
panic = "abort"
// parse.rs

fn main() {
    let tokens = &[Token::Ident, Token::AmpersandEquals, Token::Ident];
    let _ = expression(Point::new(tokens));
}

struct Progress<'s, T> {
    point: Point<'s>,
    status: Result<T, ()>,
}

impl<'s, T> Progress<'s, T> {
    pub fn success(point: Point<'s>, val: T) -> Self {
        Progress { point: point, status: Ok(val) }
    }

    pub fn failure(point: Point<'s>) -> Self {
        Progress { point: point, status: Err(()) }
    }

    pub fn map<F, T2>(self, f: F) -> Progress<'s, T2>
        where F: FnOnce(T) -> T2
    {
        Progress { point: self.point, status: self.status.map(f) }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Token {
    AmpersandEquals,
    Ident,
}

impl Token {
    fn into_ident(self) -> Option<Extent> {
        match self {
            Token::Ident => Some((0,0)),
            _ => None,
        }
    }

    fn into_ampersand_equals(self) -> Option<Extent> {
        match self {
            Token::AmpersandEquals => Some((0,0)),
            _ => None,
        }
    }
}

#[derive(Copy, Clone)]
struct Point<'s> {
    pub offset: usize,
    pub sub_offset: Option<u8>,
    pub s: &'s [Token],
}

impl<'s> Point<'s> {
    fn new(slice: &'s [Token]) -> Self {
        Point {
            offset: 0,
            sub_offset: None,
            s: slice,
        }
    }

    fn advance_by(&self, offset: usize) -> Self {
        Point {
            offset: self.offset + offset,
            sub_offset: None,
            s: &self.s[offset..],
        }
    }
}

pub type Extent = (usize, usize);

enum Expression {
    AsType(AsType),
    Binary(Binary),
    Call(Call),
    FieldAccess(FieldAccess),
    Slice(Slice),
    Value(Value),
}

struct FieldAccess {
    target: Box<Expression>,
}

pub struct Value;

pub struct Call {
    extent: Extent,
    target: Box<Expression>,
    args: Vec<Expression>,
}

pub struct Binary {
    lhs: Box<Expression>,
    rhs: Box<Expression>,
}

struct AsType {
    extent: Extent,
    target: Box<Expression>,
}

struct Slice {
    target: Box<Expression>,
    index: Box<Expression>,
}

fn token<'s, F, T>(token_convert: F, pt: Point<'s>) ->
    Progress<'s, T>
    where F: Fn(Token) -> Option<T>,
{
    let original_token = match pt.s.first() {
        Some(&token) => token,
        None => return Progress::failure(pt),
    };

    match token_convert(original_token) {
        Some(v) => {
            Progress::success(pt.advance_by(1), v)
        }
        None => {
            Progress::failure(pt)
        }
    }
}

enum OperatorInfix {
    BitwiseAndAssign(Extent),
}

enum OperatorPostfix {
    AsType { typ: () },
    Call { args: Vec<Expression> },
    FieldAccess { field: () },
    Slice { index: Expression },
}

enum OperatorKind {
    Infix(OperatorInfix),
    Postfix(OperatorPostfix),
}

impl OperatorKind {
    fn precedence(&self) -> u8 {
        match *self {
            OperatorKind::Infix(_) => 10,
            OperatorKind::Postfix(OperatorPostfix::Call { .. }) => 10,
            OperatorKind::Postfix(_) => 20,
        }
    }
}

enum InfixOrPostfix {
    Infix(OperatorInfix),
    Postfix(OperatorPostfix),
}

struct ShuntCar<'s, T> {
    value: T,
    spt: Point<'s>,
    ept: Point<'s>,
}

struct ShuntingYard<'s> {
    operators: Vec<ShuntCar<'s, OperatorKind>>,
    result: Vec<ShuntCar<'s, Expression>>,
}

type PointRange<'s> = std::ops::Range<Point<'s>>;
type ExprResult<'s, T> = std::result::Result<T, Point<'s>>;

impl<'s> ShuntingYard<'s> {
    fn new() -> Self {
        ShuntingYard {
            operators: Vec::new(),
            result: Vec::new(),
        }
    }

    fn add_infix(&mut self, op: OperatorInfix, spt: Point<'s>, ept: Point<'s>) -> ExprResult<'s, ()>
    {
        let op = OperatorKind::Infix(op);
        self.apply_precedence(&op)?;
        self.operators.push(ShuntCar { value: op, spt, ept });
        Ok(())
    }

    fn finish(mut self, failure_point: Point<'s>) -> ExprResult<'s, ShuntCar<'s, Expression>>
    {
        self.apply_all()?;
        self.pop_expression(failure_point)
    }

    fn apply_precedence(&mut self, operator: &OperatorKind) -> ExprResult<'s, ()>  {
        let op_precedence = operator.precedence();
        while self.operators.last().map_or(false, |&ShuntCar { value: ref top, .. }| top.precedence() > op_precedence) {
            let ShuntCar { value: _, spt, ept } = self.operators.pop().unwrap();
            self.apply_one(spt..ept)?;
        }
        Ok(())
    }

    fn apply_all(&mut self) -> ExprResult<'s, ()> {
        while let Some(ShuntCar { value: _, spt, ept }) = self.operators.pop() {
            self.apply_one(spt..ept)?;
        }
        Ok(())
    }

    fn apply_one(&mut self, op_range: PointRange<'s>) -> ExprResult<'s, ()>
    {
        self.apply_binary(op_range)
    }

    fn apply_infix<F>(&mut self, op_range: PointRange<'s>, f: F) ->
        ExprResult<'s, ()>
        where F: FnOnce(Expression, Expression) -> Expression
    {
        let ShuntCar { value: rhs, ept: rexpr_ept, .. } = self.pop_expression(op_range.end)?;
        let ShuntCar { value: lhs, spt: lexpr_spt, .. } = self.pop_expression(op_range.start)?;
        let new_expr = f(lhs, rhs);
        self.result.push(ShuntCar { value: new_expr, spt: lexpr_spt, ept: rexpr_ept });
        Ok(())
    }

    fn apply_binary(&mut self, op_range: PointRange<'s>) ->
        ExprResult<'s, ()>
    {
        self.apply_infix(op_range, |lhs, rhs| {
            Expression::Binary(Binary {
                lhs: Box::new(lhs),
                rhs: Box::new(rhs),
            })
        })
    }

    fn pop_expression(&mut self, location: Point<'s>) ->
        ExprResult<'s, ShuntCar<'s, Expression>>
    {
        self.result.pop().ok_or(location)
    }
}

enum ExpressionState {
    Prefix, // Also "beginning of expression"
    Infix,
    Atom,
}

fn expression<'s>(pt: Point<'s>) -> Progress<'s, Expression> {
    match expression_x(pt) {
        Ok(ShuntCar { value: expr, ept, .. }) => Progress::success(ept, expr),
        Err(failure_point) => Progress::failure(failure_point),
    }
}

fn expression_x<'s>(mut pt: Point<'s>) ->
    ExprResult<'s, ShuntCar<'s, Expression>>
{
    let mut shunting_yard = ShuntingYard::new();
    let mut state = ExpressionState::Prefix;
    loop {
        println!("expression_x loop");
        match state {
            ExpressionState::Prefix |
            ExpressionState::Infix => {
                println!("branch 1");
                let Progress { point, .. } =
                    token(Token::into_ident, pt).map(|_| Value).map(Expression::Value);
                state = ExpressionState::Atom;
                pt = point;
            }
            ExpressionState::Atom => {
                println!("branch 2");
                match token(Token::into_ampersand_equals, pt)
                    .map(OperatorInfix::BitwiseAndAssign).map(InfixOrPostfix::Infix) {
                    Progress { status: Ok(infix_or_postfix), point } => {
                        match infix_or_postfix {
                            InfixOrPostfix::Infix(op) => {
                                shunting_yard.add_infix(op, pt, point)?;
                                state = ExpressionState::Infix;
                            }
                            InfixOrPostfix::Postfix(_) => unimplemented!(),
                        }
                        pt = point;
                    }
                    Progress { status: Err(_), point } => {
                        return shunting_yard.finish(point);
                    }
                }
            }
        }
    }
}

@dwrensha
Copy link
Author

Reported as rust-lang/rust#41888.

@dwrensha
Copy link
Author

dwrensha commented Oct 1, 2017

Closing because the upstream issue has been fixed.

@dwrensha dwrensha closed this as completed Oct 1, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants