diff --git a/Cargo.lock b/Cargo.lock index f218fa45..88f19571 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -529,6 +529,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +[[package]] +name = "final_fn" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe1fa59a36a9928c12b6c4e9658f8b84d43e7578cb6d532b069ce84dcd0afda6" + [[package]] name = "fn-error-context" version = "0.2.0" @@ -565,6 +571,7 @@ dependencies = [ "contracts", "env_logger", "expect-test", + "final_fn", "formality-macros", "lazy_static", "stacker", diff --git a/book/src/formality_core/parse.md b/book/src/formality_core/parse.md index b82f7a5f..f728ff82 100644 --- a/book/src/formality_core/parse.md +++ b/book/src/formality_core/parse.md @@ -19,14 +19,33 @@ struct MyEnum { } ``` -### Ambiguity and precedence +### Ambiguity and greedy parsing -When parsing an enum there will be multiple possibilities. We will attempt to parse them all. If more than one succeeds, the parser will attempt to resolve the ambiguity. Ambiguity can be resolved in two ways: +When parsing an enum there will be multiple possibilities. We will attempt to parse them all. If more than one succeeds, the parser will attempt to resolve the ambiguity by looking for the **longest match**. However, we don't just consider the number of characters, we look for a **reduction prefix**: -* Explicit precedence: By default, every variant has precedence 0, but you can override this by annotating variants with `#[precedence(N)]` (where `N` is some integer). This will override the precedence for that variant. Variants with higher precedences are preferred. -* Reduction prefix: When parsing, we track the list of things we had to parse. If there are two variants at the same precedence level, but one of them had to parse strictly more things than the other and in the same way, we'll prefer the longer one. So for example if one variant parsed a `Ty` and the other parsed a `Ty Ty`, we'd take the `Ty Ty`. +* When parsing, we track the list of things we had to parse. If there are two variants at the same precedence level, but one of them had to parse strictly more things than the other and in the same way, we'll prefer the longer one. So for example if one variant parsed a `Ty` and the other parsed a `Ty Ty`, we'd take the `Ty Ty`. + * When considering whether a reduction is "significant", we take casts into account. See `ActiveVariant::mark_as_cast_variant` for a more detailed explanation and set of examples. -Otherwise, the parser will panic and report ambiguity. The parser panics rather than returning an error because ambiguity doesn't mean that there is no way to parse the given text as the nonterminal -- rather that there are multiple ways. Errors mean that the text does not match the grammar for that nonterminal. +### Precedence and left-recursive grammars + +We support left-recursive grammars like this one from the `parse-torture-tests`: + +```rust +{{#include ../../../tests/parser-torture-tests/src/path.rs:path}} +``` + +We also support ambiguous grammars. For example, you can code up arithmetic expressions like this: + + +```rust +{{#include ../../../tests/parser-torture-tests/src/left_associative.rs:Expr}} +``` + +When specifying the `#[precedence]` of a variant, the default is left-associativity, which can be written more explicitly as `#[precedence(L, left)]`. If you prefer, you can specify right-associativity (`#[precedence(L, right)]`) or non-associativity `#[precedence(L, none)]`. This affects how things of the same level are parsed: + +* `1 + 1 + 1` when left-associative is `(1 + 1) + 1` +* `1 + 1 + 1` when right-associative is `1 + (1 + 1)` +* `1 + 1 + 1` when none-associative is an error. ### Symbols @@ -39,11 +58,20 @@ A grammar consists of a series of *symbols*. Each symbol matches some text in th * If fields have names, then `$field` should name the field. * For position fields (e.g., the T and U in `Mul(Expr, Expr)`), use `$v0`, `$v1`, etc. * Exception: `$$` is treated as the terminal `'$'`. -* Nonterminals can also accept modes: +* Nonterminals have various modes: * `$field` -- just parse the field's type * `$*field` -- the field must be a `Vec` -- parse any number of `T` instances. Something like `[ $*field ]` would parse `[f1 f2 f3]`, assuming `f1`, `f2`, and `f3` are valid values for `field`. * `$,field` -- similar to the above, but uses a comma separated list (with optional trailing comma). So `[ $,field ]` will parse something like `[f1, f2, f3]`. * `$?field` -- will parse `field` and use `Default::default()` value if not present. + * `$` -- parse ``, where `field: Vec` + * `$` -- parse ``, where `field: Vec`, but accept empty string as empty vector + * `$(field)` -- parse `(E1, E2, E3)`, where `field: Vec` + * `$(?field)` -- parse `(E1, E2, E3)`, where `field: Vec`, but accept empty string as empty vector + * `$[field]` -- parse `[E1, E2, E3]`, where `field: Vec` + * `$[?field]` -- parse `[E1, E2, E3]`, where `field: Vec`, but accept empty string as empty vector + * `${field}` -- parse `{E1, E2, E3}`, where `field: Vec` + * `${?field}` -- parse `{E1, E2, E3}`, where `field: Vec`, but accept empty string as empty vector + * `$:guard ` -- parses `` but only if the keyword `guard` is present. For example, `$:where $,where_clauses` would parse `where WhereClause1, WhereClause2, WhereClause3` ### Greediness diff --git a/crates/formality-core/Cargo.toml b/crates/formality-core/Cargo.toml index 8908712c..54f3f6bd 100644 --- a/crates/formality-core/Cargo.toml +++ b/crates/formality-core/Cargo.toml @@ -20,6 +20,7 @@ tracing-tree = { version = "0.2" } formality-macros = { version = "0.1.0", path = "../formality-macros" } anyhow = "1.0.75" contracts = "0.6.3" +final_fn = "0.1.0" [dev-dependencies] expect-test = "1.4.1" diff --git a/crates/formality-core/src/binder.rs b/crates/formality-core/src/binder.rs index 94f10efe..525dea75 100644 --- a/crates/formality-core/src/binder.rs +++ b/crates/formality-core/src/binder.rs @@ -261,14 +261,16 @@ where T: std::fmt::Debug, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "<")?; - for (kind, i) in self.kinds.iter().zip(0..) { - if i > 0 { - write!(f, ", ")?; + if !self.kinds.is_empty() { + write!(f, "{}", L::BINDING_OPEN)?; + for (kind, i) in self.kinds.iter().zip(0..) { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{:?}", kind)?; } - write!(f, "{:?}", kind)?; + write!(f, "{} ", L::BINDING_CLOSE)?; } - write!(f, "> ")?; write!(f, "{:?}", &self.term)?; Ok(()) } diff --git a/crates/formality-core/src/language.rs b/crates/formality-core/src/language.rs index 02aeaaaf..91abf1a1 100644 --- a/crates/formality-core/src/language.rs +++ b/crates/formality-core/src/language.rs @@ -1,6 +1,7 @@ use crate::cast::UpcastFrom; use crate::term::CoreTerm; use crate::variable::{CoreBoundVar, CoreExistentialVar, CoreUniversalVar, CoreVariable}; +use crate::DowncastTo; use std::fmt::Debug; use std::hash::Hash; @@ -20,7 +21,8 @@ pub trait Language: 'static + Copy + Ord + Hash + Debug + Default { + UpcastFrom> + UpcastFrom> + UpcastFrom> - + UpcastFrom>; + + UpcastFrom> + + DowncastTo>; /// The token (typically `<`) used to open binders. const BINDING_OPEN: char; diff --git a/crates/formality-core/src/lib.rs b/crates/formality-core/src/lib.rs index 53e54a13..1caba694 100644 --- a/crates/formality-core/src/lib.rs +++ b/crates/formality-core/src/lib.rs @@ -32,6 +32,7 @@ pub mod language; pub mod parse; pub mod substitution; pub mod term; +pub mod util; pub mod variable; pub mod visit; @@ -95,18 +96,27 @@ macro_rules! declare_language { ) => { $(#[$the_lang_m:meta])* $the_lang_v mod $the_lang { - use super::*; + use $crate::language::Language; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] pub struct FormalityLang; - impl $crate::language::Language for FormalityLang { - const NAME: &'static str = $name; - type Kind = $kind; - type Parameter = $param; - const BINDING_OPEN: char = $binding_open; - const BINDING_CLOSE: char = $binding_close; - const KEYWORDS: &'static [&'static str] = &[$($kw),*]; + // This module may seem weird -- it permits us to import `super::*` + // so that all the types in `$kind` and `$param` are valid without + // importing `super::*` into the entire module. This not only makes + // things a bit nicer, since those imports are not needed and could + // cause weird behavior, it avoids a cycle when users + // do `pub use $lang::grammar::*`. + mod __hygiene { + use super::super::*; + impl $crate::language::Language for super::FormalityLang { + const NAME: &'static str = $name; + type Kind = $kind; + type Parameter = $param; + const BINDING_OPEN: char = $binding_open; + const BINDING_CLOSE: char = $binding_close; + const KEYWORDS: &'static [&'static str] = &[$($kw),*]; + } } $crate::trait_alias! { @@ -125,15 +135,19 @@ macro_rules! declare_language { pub trait Term = $crate::term::CoreTerm } - pub type Variable = $crate::variable::CoreVariable; - pub type ExistentialVar = $crate::variable::CoreExistentialVar; - pub type UniversalVar = $crate::variable::CoreUniversalVar; - pub type BoundVar = $crate::variable::CoreBoundVar; - pub type DebruijnIndex = $crate::variable::DebruijnIndex; - pub type VarIndex = $crate::variable::VarIndex; - pub type Binder = $crate::binder::CoreBinder; - pub type Substitution = $crate::substitution::CoreSubstitution; - pub type VarSubstitution = $crate::substitution::CoreVarSubstitution; + /// Grammar items to be included in this language. + pub mod grammar { + use super::FormalityLang; + pub type Variable = $crate::variable::CoreVariable; + pub type ExistentialVar = $crate::variable::CoreExistentialVar; + pub type UniversalVar = $crate::variable::CoreUniversalVar; + pub type BoundVar = $crate::variable::CoreBoundVar; + pub type DebruijnIndex = $crate::variable::DebruijnIndex; + pub type VarIndex = $crate::variable::VarIndex; + pub type Binder = $crate::binder::CoreBinder; + pub type Substitution = $crate::substitution::CoreSubstitution; + pub type VarSubstitution = $crate::substitution::CoreVarSubstitution; + } /// Parses `text` as a term with no bindings in scope. #[track_caller] @@ -161,7 +175,7 @@ macro_rules! declare_language { pub fn term_with(bindings: impl IntoIterator, text: &str) -> $crate::Fallible where T: Parse, - B: $crate::Upcast<(String, $param)>, + B: $crate::Upcast<(String, ::Parameter)>, { $crate::parse::core_term_with::(bindings, text) } diff --git a/crates/formality-core/src/parse.rs b/crates/formality-core/src/parse.rs index 3170e45c..598af984 100644 --- a/crates/formality-core/src/parse.rs +++ b/crates/formality-core/src/parse.rs @@ -14,7 +14,7 @@ use std::fmt::Debug; /// Trait for parsing a [`Term`](`crate::term::Term`) as input. /// Typically this is auto-generated with the `#[term]` procedural macro, /// but you can implement it by hand if you want a very customized parse. -pub trait CoreParse: Sized + Debug + Clone + Eq { +pub trait CoreParse: Sized + Debug + Clone + Eq + 'static { /// Parse a single instance of this type, returning an error if no such /// instance is present. /// @@ -24,7 +24,7 @@ pub trait CoreParse: Sized + Debug + Clone + Eq { } mod parser; -pub use parser::{skip_whitespace, ActiveVariant, Parser}; +pub use parser::{skip_whitespace, ActiveVariant, Parser, Precedence}; /// Parses `text` as a term with the given bindings in scope. /// @@ -58,7 +58,7 @@ where } /// Record from a successful parse. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct SuccessfulParse<'t, T> { /// The new point in the input, after we've consumed whatever text we have. text: &'t str, @@ -76,21 +76,15 @@ pub struct SuccessfulParse<'t, T> { /// reduction. reductions: Vec<&'static str>, + /// The precedence of this parse, which is derived from the value given + /// to `parse_variant`. + precedence: Precedence, + /// The value produced. value: T, } impl<'t, T> SuccessfulParse<'t, T> { - #[track_caller] - pub fn new(text: &'t str, reductions: Vec<&'static str>, value: T) -> Self { - // assert!(!reductions.is_empty()); - Self { - text, - reductions, - value, - } - } - /// Extract the value parsed and the remaining text, /// ignoring the reductions. pub fn finish(self) -> (T, &'t str) { @@ -103,6 +97,7 @@ impl<'t, T> SuccessfulParse<'t, T> { SuccessfulParse { text: self.text, reductions: self.reductions, + precedence: self.precedence, value: op(self.value), } } @@ -117,6 +112,7 @@ where SuccessfulParse { text: term.text, reductions: term.reductions, + precedence: term.precedence, value: term.value.upcast(), } } @@ -172,7 +168,7 @@ pub type ParseResult<'t, T> = Result, Set> pub type TokenResult<'t, T> = Result<(T, &'t str), Set>>; /// Tracks the variables in scope at this point in parsing. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct Scope { bindings: Vec<(String, CoreParameter)>, } @@ -222,10 +218,7 @@ where { fn parse<'t>(scope: &Scope, text: &'t str) -> ParseResult<'t, Self> { Parser::single_variant(scope, text, "Vec", |p| { - p.expect_char('[')?; - let v = p.comma_nonterminal()?; - p.expect_char(']')?; - Ok(v) + p.delimited_nonterminal('[', false, ']') }) } } @@ -277,7 +270,13 @@ where { fn parse<'t>(scope: &Scope, text: &'t str) -> ParseResult<'t, Self> { Parser::single_variant(scope, text, "Binder", |p| { - p.expect_char(L::BINDING_OPEN)?; + match p.expect_char(L::BINDING_OPEN) { + Ok(()) => {} + Err(_) => { + return Ok(CoreBinder::dummy(p.nonterminal()?)); + } + } + let bindings: Vec> = p.comma_nonterminal()?; p.expect_char(L::BINDING_CLOSE)?; diff --git a/crates/formality-core/src/parse/parser.rs b/crates/formality-core/src/parse/parser.rs index 1224455e..96a63521 100644 --- a/crates/formality-core/src/parse/parser.rs +++ b/crates/formality-core/src/parse/parser.rs @@ -3,11 +3,16 @@ use std::str::FromStr; use crate::{ language::{CoreParameter, HasKind, Language}, - set, Downcast, DowncastFrom, Set, Upcast, + parse::parser::left_recursion::{CurrentState, LeftRight}, + set, + variable::CoreVariable, + Downcast, DowncastFrom, Set, Upcast, }; use super::{CoreParse, ParseError, ParseResult, Scope, SuccessfulParse, TokenResult}; +mod left_recursion; + /// Create this struct when implementing the [`CoreParse`][] trait. /// Each `Parser` corresponds to some symbol in the grammar. /// You create a parser and then you invoke the `parse_variant` @@ -21,18 +26,107 @@ use super::{CoreParse, ParseError, ParseResult, Scope, SuccessfulParse, TokenRes pub struct Parser<'s, 't, T, L> where L: Language, + T: Debug + Clone + Eq + 'static, { scope: &'s Scope, start_text: &'t str, - #[allow(dead_code)] - tracing_span: tracing::span::EnteredSpan, nonterminal_name: &'static str, - successes: Vec<(SuccessfulParse<'t, T>, Precedence)>, + successes: Vec>, failures: Set>, + min_precedence_level: usize, +} + +/// The *precedence* of a variant determines how to manage +/// recursive invocations. +/// +/// The general rule is that an expression +/// with lower-precedence cannot be embedded +/// into an expression of higher-precedence. +/// So given `1 + 2 * 3`, the `+` cannot be a +/// (direct) child of the `*`, because `+` is +/// lower precedence. +/// +/// The tricky bit is what happens with *equal* +/// precedence. In that case, we have to consider +/// the [`Associativity`][] (see enum for details). +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct Precedence { + level: usize, + associativity: Associativity, +} + +/// Determines what happens when you have equal precedence. +/// The result is dependent on whether you are embedding +/// in left position (i.e., a recurrence before having parsed any +/// tokens) or right position (a recurrence after parsed tokens). +/// So given `1 + 2 + 3`, `1 + 2` is a *left* occurrence of the second `+`. +/// And `2 + 3` is a *right* occurence of the first `+`. +/// +/// With `Associativity::Left`, equal precedence is allowed in left matches +/// but not right. So `1 + 2 + 3` parses as `(1 + 2) + 3`, as you would expect. +/// +/// With `Associativity::Right`, equal precedence is allowed in right matches +/// but not left. So `1 + 2 + 3` parses as `1 + (2 + 3)`. That's probably not what you wanted +/// for arithemetic expressions, but could be useful for (say) curried function types, +/// where `1 -> 2 -> 3` should parse as `1 -> (2 -> 3)`. +/// +/// With `Associativity::None`, equal precedence is not allowed anywhere, so +/// `1 + 2 + 3` is just an error and you have to explicitly add parentheses. +/// +/// Use `Precedence::default` for cases where precedence is not relevant. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Associativity { + Left, + Right, + None, + Both, +} + +impl Precedence { + /// Left associative with the given precedence level + pub fn left(level: usize) -> Self { + Self::new(level, Associativity::Left) + } + + /// Right associative with the given precedence level + pub fn right(level: usize) -> Self { + Self::new(level, Associativity::Right) + } + + /// Non-associative with the given precedence level + pub fn none(level: usize) -> Self { + Self::new(level, Associativity::None) + } + + /// Construct a new precedence. + fn new(level: usize, associativity: Associativity) -> Self { + // We require level to be STRICTLY LESS than usize::MAX + // so that we can always add 1. + assert!(level < std::usize::MAX); + Self { + level, + associativity: associativity, + } + } } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] -struct Precedence(usize); +impl Default for Precedence { + fn default() -> Self { + // Default precedence: + // + // Use MAX-1 because we sometimes try to add 1 when we detect recursion, and we don't want overflow. + // + // Use Right associativity because if you have a variant like `T = [T]` + // then you want to be able to (by default) embed arbitrary T in that recursive location. + // If you use LEFT or NONE, then this recursive T would have a minimum level of std::usize::MAX + // (and hence never be satisfied). + // + // Using RIGHT feels a bit weird here but seems to behave correctly all the time. + // It's tempting to add something like "Both" or "N/A" that would just not set a min + // prec level when there's recursion. + Self::new(std::usize::MAX - 1, Associativity::Both) + } +} /// The "active variant" struct is the main struct /// you will use if you are writing custom parsing logic. @@ -53,15 +147,18 @@ pub struct ActiveVariant<'s, 't, L> where L: Language, { + precedence: Precedence, scope: &'s Scope, - text: &'t str, + start_text: &'t str, + current_text: &'t str, reductions: Vec<&'static str>, + is_cast_variant: bool, } impl<'s, 't, T, L> Parser<'s, 't, T, L> where L: Language, - T: Debug + Eq, + T: Debug + Clone + Eq + 'static, { /// Shorthand to create a parser for a nonterminal with a single variant, /// parsed by the function `op`. @@ -71,45 +168,62 @@ where scope: &'s Scope, text: &'t str, nonterminal_name: &'static str, - op: impl FnOnce(&mut ActiveVariant<'s, 't, L>) -> Result>>, + mut op: impl FnMut(&mut ActiveVariant<'s, 't, L>) -> Result>>, ) -> ParseResult<'t, T> { - let mut result = Parser::new(scope, text, nonterminal_name); - result.parse_variant(nonterminal_name, 0, op); - result.finish() + Parser::multi_variant(scope, text, nonterminal_name, |parser| { + parser.parse_variant(nonterminal_name, Precedence::default(), &mut op); + }) } - /// Creates a new parser. You should then invoke `parse_variant` 0 or more times for - /// each of the possibilities and finally invoke `finish`. + /// Creates a new parser that can accommodate multiple variants. + /// + /// Invokes `op` to do the parsing; `op` should call `parse_variant` 0 or more times for + /// each of the possibilities. The best result (if any) will then be returned. /// /// The method [`single_variant`] is more convenient if you have exactly one variant. - pub fn new(scope: &'s Scope, text: &'t str, nonterminal_name: &'static str) -> Self { - let tracing_span = tracing::span!( - tracing::Level::TRACE, - "nonterminal", - name = nonterminal_name, - ?scope, - ?text - ) - .entered(); + pub fn multi_variant( + scope: &'s Scope, + text: &'t str, + nonterminal_name: &'static str, + mut op: impl FnMut(&mut Self), + ) -> ParseResult<'t, T> { + let text = skip_whitespace(text); + + left_recursion::enter(scope, text, |min_precedence_level| { + let tracing_span = tracing::span!( + tracing::Level::TRACE, + "nonterminal", + name = nonterminal_name, + ?scope, + ?text + ); + let guard = tracing_span.enter(); + + let mut parser = Self { + scope, + start_text: text, + nonterminal_name, + successes: vec![], + failures: set![], + min_precedence_level, + }; - Self { - scope, - start_text: text, - nonterminal_name, - tracing_span, - successes: vec![], - failures: set![], - } + op(&mut parser); + + parser.finish(guard) + }) } /// Shorthand for `parse_variant` where the parsing operation is to /// parse the type `V` and then upcast it to the desired result type. - pub fn parse_variant_cast(&mut self, variant_precedence: usize) + /// Also marks the variant as a cast variant. + pub fn parse_variant_cast(&mut self, variant_precedence: Precedence) where V: CoreParse + Upcast, { let variant_name = std::any::type_name::(); Self::parse_variant(self, variant_name, variant_precedence, |p| { + p.mark_as_cast_variant(); let v: V = p.nonterminal()?; Ok(v.upcast()) }) @@ -123,22 +237,28 @@ where pub fn parse_variant( &mut self, variant_name: &'static str, - variant_precedence: usize, + variant_precedence: Precedence, op: impl FnOnce(&mut ActiveVariant<'s, 't, L>) -> Result>>, ) { let span = tracing::span!( tracing::Level::TRACE, "variant", name = variant_name, - variant_precedence = variant_precedence + ?variant_precedence, ); let guard = span.enter(); - let mut active_variant = ActiveVariant { - scope: self.scope, - text: self.start_text, - reductions: vec![], - }; + if variant_precedence.level < self.min_precedence_level { + tracing::trace!( + "variant has precedence level {} which is below parser minimum of {}", + variant_precedence.level, + self.min_precedence_level, + ); + return; + } + + let mut active_variant = + ActiveVariant::new(variant_precedence, self.scope, self.start_text); let result = op(&mut active_variant); // Drop the guard here so that the "success" or "error" results appear outside the variant span. @@ -147,15 +267,18 @@ where match result { Ok(value) => { - active_variant.reductions.push(variant_name); - self.successes.push(( - SuccessfulParse { - text: active_variant.text, - reductions: active_variant.reductions, - value, - }, - Precedence(variant_precedence), - )); + // Subtle: for cast variants, don't record the variant name in the reduction lits, + // as it doesn't carry semantic weight. See `mark_as_cast_variant` for more details. + if !active_variant.is_cast_variant { + active_variant.reductions.push(variant_name); + } + + self.successes.push(SuccessfulParse { + text: active_variant.current_text, + reductions: active_variant.reductions, + precedence: variant_precedence, + value, + }); tracing::trace!("success: {:?}", self.successes.last().unwrap()); } @@ -173,7 +296,7 @@ where } } - pub fn finish(self) -> ParseResult<'t, T> { + fn finish(self, guard: tracing::span::Entered<'_>) -> ParseResult<'t, T> { // If we did not parse anything successfully, then return an error. // There are two possibilities: some of our variants may have made // progress but ultimately failed. If so, self.failures will be non-empty, @@ -184,6 +307,8 @@ where // observe that we did not consume any tokens and will ignore our messawge // and put its own (e.g., "failed to find a Y here"). if self.successes.is_empty() { + // It's better to print this result alongside the main parsing section. + drop(guard); return if self.failures.is_empty() { tracing::trace!("parsing failed: no variants were able to consume a single token"); Err(ParseError::at( @@ -216,7 +341,9 @@ where .zip(0..) .all(|(s_j, j)| i == j || Self::is_preferable(s_i, s_j)) { - let (s_i, _) = self.successes.into_iter().skip(i).next().unwrap(); + let s_i = self.successes.into_iter().skip(i).next().unwrap(); + // It's better to print this result alongside the main parsing section. + drop(guard); tracing::trace!("best parse = `{:?}`", s_i); return Ok(s_i); } @@ -229,18 +356,12 @@ where ); } - fn is_preferable( - s_i: &(SuccessfulParse, Precedence), - s_j: &(SuccessfulParse, Precedence), - ) -> bool { - let (parse_i, prec_i) = s_i; - let (parse_j, prec_j) = s_j; - + fn is_preferable(s_i: &SuccessfulParse, s_j: &SuccessfulParse) -> bool { fn has_prefix(l1: &[T], l2: &[T]) -> bool { l1.len() > l2.len() && (0..l2.len()).all(|i| l1[i] == l2[i]) } - prec_i > prec_j || has_prefix(&parse_i.reductions, &parse_j.reductions) + has_prefix(&s_i.reductions, &s_j.reductions) } } @@ -248,19 +369,82 @@ impl<'s, 't, L> ActiveVariant<'s, 't, L> where L: Language, { + fn new(precedence: Precedence, scope: &'s Scope, start_text: &'t str) -> Self { + let start_text = skip_whitespace(start_text); + Self { + precedence, + scope, + start_text, + current_text: start_text, + reductions: vec![], + is_cast_variant: false, + } + } + fn current_state(&self) -> CurrentState { + // Determine whether we are in Left or Right position -- Left means + // that we have not yet consumed any tokens. Right means that we have. + // See `LeftRight` type for more details. + // + // Subtle-ish: this comparison assumes there is no whitespace, + // but we establish that invariant in `Self::new`. + debug_assert_eq!(self.start_text, skip_whitespace(self.start_text)); + let left_right = if self.start_text == self.current_text { + LeftRight::Left + } else { + LeftRight::Right + }; + + CurrentState { + left_right, + precedence: self.precedence, + current_text: self.current_text, + } + } + /// The current text remaining to be consumed. pub fn text(&self) -> &'t str { - self.text + self.current_text } /// Skips whitespace in the input, producing no reduction. pub fn skip_whitespace(&mut self) { - self.text = skip_whitespace(self.text); + self.current_text = skip_whitespace(self.current_text); } /// Skips a comma in the input, producing no reduction. pub fn skip_trailing_comma(&mut self) { - self.text = skip_trailing_comma(self.text); + self.current_text = skip_trailing_comma(self.current_text); + } + + /// Marks this variant as an cast variant, + /// which means there is no semantic difference + /// between the thing you parsed and the reduced form. + /// We do this automatically for enum variants marked + /// as `#[cast]` or calls to `parse_variant_cast`. + /// + /// Cast variants interact differently with ambiguity detection. + /// Consider this grammar: + /// + /// ```text + /// X = Y | Z // X has two variants + /// Y = A // Y has 1 variant + /// Z = A B // Z has 1 variant + /// A = "a" // A has 1 variant + /// B = "b" // B has 1 variant + /// ``` + /// + /// If you mark the two `X` variants (`X = Y` and `X = Z`) + /// as cast variants, then the input `"a b"` is considered + /// unambiguous and is parsed as `X = (Z = (A = "a') (B = "b))` + /// with no remainder. + /// + /// If you don't mark those variants as cast variants, + /// then we consider this *ambiguous*, because + /// it could be that you want `X = (Y = (A = "a"))` with + /// a remainder of `"b"`. This is appropriate + /// if choosing Y vs Z has different semantic meaning. + pub fn mark_as_cast_variant(&mut self) { + self.is_cast_variant = true; } /// Expect *exactly* the given text (after skipping whitespace) @@ -312,7 +496,7 @@ where /// Consume next identifier-like string, requiring that it be equal to `expected`. #[tracing::instrument(level = "trace", ret)] pub fn expect_keyword(&mut self, expected: &str) -> Result<(), Set>> { - let text0 = self.text; + let text0 = self.current_text; match self.identifier_like_string() { Ok(ident) if &*ident == expected => Ok(()), _ => Err(ParseError::at( @@ -322,37 +506,62 @@ where } } - /// Reject next identifier-like string if it is one of the given list of keywords. - /// Does not consume any input. - /// You can this to implement positional keywords -- just before parsing an identifier or variable, - /// you can invoke `reject_custom_keywords` to reject anything that you don't want to permit in this position. - #[tracing::instrument(level = "trace", ret)] - pub fn reject_custom_keywords(&self, keywords: &[&str]) -> Result<(), Set>> { + /// Accepts any of the given keywords. + #[tracing::instrument(level = "trace", skip(self), ret)] + pub fn expect_keyword_in(&mut self, expected: &[&str]) -> Result>> { + let text0 = self.current_text; + match self.identifier_like_string() { + Ok(ident) if expected.iter().any(|&kw| ident == kw) => Ok(ident), + _ => Err(ParseError::at( + skip_whitespace(text0), + format!("expected any of `{:?}`", expected), + )), + } + } + + /// Attempts to execute `op` and, if it successfully parses, then returns an error + /// with the value (which is meant to be incorporated into a par) + /// If `op` fails to parse then the result is `Ok`. + #[tracing::instrument(level = "trace", skip(self, op, err), ret)] + pub fn reject( + &self, + op: impl Fn(&mut ActiveVariant<'s, 't, L>) -> Result>>, + err: impl FnOnce(T) -> Set>, + ) -> Result<(), Set>> { let mut this = ActiveVariant { - text: self.text, + precedence: self.precedence, + start_text: self.start_text, + current_text: self.current_text, reductions: vec![], scope: self.scope, + is_cast_variant: false, }; - match this.identifier_like_string() { - Ok(ident) => { - if keywords.iter().any(|&kw| kw == ident) { - return Err(ParseError::at( - self.text, - format!("expected identified, found keyword `{ident:?}`"), - )); - } - - Ok(()) - } - + match op(&mut this) { + Ok(value) => Err(err(value)), Err(_) => Ok(()), } } + /// Reject next identifier-like string if it is one of the given list of keywords. + /// Does not consume any input. + /// You can this to implement positional keywords -- just before parsing an identifier or variable, + /// you can invoke `reject_custom_keywords` to reject anything that you don't want to permit in this position. + pub fn reject_custom_keywords(&self, keywords: &[&str]) -> Result<(), Set>> { + self.reject( + |p| p.expect_keyword_in(keywords), + |ident| { + ParseError::at( + self.current_text, + format!("expected identified, found keyword `{ident:?}`"), + ) + }, + ) + } + /// Extracts a string that meets the regex for an identifier /// (but it could also be a keyword). - #[tracing::instrument(level = "trace", ret)] + #[tracing::instrument(level = "trace", skip(self), ret)] pub fn identifier_like_string(&mut self) -> Result>> { self.string( |ch| matches!(ch, 'a'..='z' | 'A'..='Z' | '_'), @@ -365,7 +574,7 @@ where /// following the usual rules. **Disallows language keywords.** /// If you want to disallow additional keywords, /// see the `reject_custom_keywords` method. - #[tracing::instrument(level = "trace", ret)] + #[tracing::instrument(level = "trace", skip(self), ret)] pub fn identifier(&mut self) -> Result>> { self.reject_custom_keywords(L::KEYWORDS)?; self.identifier_like_string() @@ -379,16 +588,34 @@ where op: impl FnOnce(&mut ActiveVariant<'_, 't, L>) -> R, ) -> R { let mut av = ActiveVariant { + precedence: self.precedence, scope: &scope, - text: self.text, + start_text: self.start_text, + current_text: self.current_text, reductions: vec![], + is_cast_variant: false, }; let result = op(&mut av); - self.text = av.text; + self.current_text = av.current_text; self.reductions.extend(av.reductions); result } + /// Returns an error if an in-scope variable name is found. + /// The derive automatically inserts calls to this for all other variants + /// if any variant is declared `#[variable]`. + pub fn reject_variable(&self) -> Result<(), Set>> { + self.reject::>( + |p| p.variable(), + |var| { + ParseError::at( + self.current_text, + format!("found unexpected in-scope variable {:?}", var), + ) + }, + ) + } + /// Parses the next identifier as a variable in scope /// with the kind appropriate for the return type `R`. /// @@ -410,14 +637,14 @@ where /// It also allows parsing where you use variables to stand for /// more complex parameters, which is kind of combining parsing /// and substitution and can be convenient in tests. - #[tracing::instrument(level = "trace", ret)] + #[tracing::instrument(level = "trace", skip(self), ret)] pub fn variable(&mut self) -> Result>> where R: Debug + DowncastFrom>, { self.skip_whitespace(); let type_name = std::any::type_name::(); - let text0 = self.text; + let text0 = self.current_text; let id = self.identifier()?; match self.scope.lookup(&id) { Some(parameter) => match parameter.downcast() { @@ -436,13 +663,13 @@ where } /// Extract a number from the input, erroring if the input does not start with a number. - #[tracing::instrument(level = "trace", ret)] + #[tracing::instrument(level = "trace", skip(self), ret)] pub fn number(&mut self) -> Result>> where T: FromStr + std::fmt::Debug, { let description = std::any::type_name::(); - let text0 = self.text; + let text0 = self.current_text; let s = self.string(char::is_numeric, char::is_numeric, description)?; match T::from_str(&s) { Ok(t) => Ok(t), @@ -469,7 +696,7 @@ where ) -> Result>> { self.skip_whitespace(); let value; - (value, self.text) = op(self.text)?; + (value, self.current_text) = op(self.current_text)?; Ok(value) } @@ -478,11 +705,21 @@ where pub fn nonterminal(&mut self) -> Result>> where T: CoreParse, - L: Language, { self.nonterminal_with(T::parse) } + /// Gives an error if `T` is parsable here. + pub fn reject_nonterminal(&mut self) -> Result<(), Set>> + where + T: CoreParse, + { + self.reject( + |p| p.nonterminal::(), + |value| ParseError::at(self.text(), format!("unexpected `{value:?}`")), + ) + } + /// Try to parse the current point as `T` and return `None` if there is nothing there. /// /// **NB:** If the parse partially succeeds, i.e., we are able to consume some tokens @@ -492,12 +729,12 @@ where /// because we consumed the open paren `(` from `T` but then encountered an error /// looking for the closing paren `)`. #[track_caller] - #[tracing::instrument(level = "Trace", ret)] + #[tracing::instrument(level = "Trace", skip(self), ret)] pub fn opt_nonterminal(&mut self) -> Result, Set>> where T: CoreParse, { - let text0 = self.text; + let text0 = self.current_text; match self.nonterminal() { Ok(v) => Ok(Some(v)), Err(mut errs) => { @@ -505,7 +742,7 @@ where if errs.is_empty() { // If no errors consumed anything, then self.text // must not have advanced. - assert_eq!(skip_whitespace(text0), self.text); + assert_eq!(skip_whitespace(text0), self.current_text); Ok(None) } else { Err(errs) @@ -516,6 +753,7 @@ where /// Continue parsing instances of `T` while we can. /// This is a greedy parse. + #[tracing::instrument(level = "Trace", skip(self), ret)] pub fn many_nonterminal(&mut self) -> Result, Set>> where T: CoreParse, @@ -527,6 +765,33 @@ where Ok(result) } + #[tracing::instrument(level = "Trace", skip(self), ret)] + pub fn delimited_nonterminal( + &mut self, + open: char, + optional: bool, + close: char, + ) -> Result, Set>> + where + T: CoreParse, + { + // Look for the opening delimiter. + // If we don't find it, then this is either an empty vector (if optional) or an error (otherwise). + match self.expect_char(open) { + Ok(()) => {} + Err(errs) => { + return if optional { Ok(vec![]) } else { Err(errs) }; + } + } + + // Now parse the contents. + let result = self.comma_nonterminal()?; + + self.expect_char(close)?; + + Ok(result) + } + /// Parse multiple instances of `T` separated by commas. #[track_caller] pub fn comma_nonterminal(&mut self) -> Result, Set>> @@ -560,11 +825,12 @@ where let SuccessfulParse { text, reductions, + precedence: _, value, - } = op(self.scope, self.text)?; + } = left_recursion::recurse(self.current_state(), || op(self.scope, self.current_text))?; // Adjust our point in the input text - self.text = text; + self.current_text = text; // Some value was produced, so there must have been a reduction assert!(!reductions.is_empty()); diff --git a/crates/formality-core/src/parse/parser/left_recursion.rs b/crates/formality-core/src/parse/parser/left_recursion.rs new file mode 100644 index 00000000..862f77f4 --- /dev/null +++ b/crates/formality-core/src/parse/parser/left_recursion.rs @@ -0,0 +1,466 @@ +//! Support left-recursive grammars. This is basically just a fixed point +//! operation, but we re-implement it to avoid having to return multiple +//! success values, since we know that's not really needed here. +//! +//! This unfortunately requires unsafe and even `type_id`. This is because +//! we need to be generic over the language `L` and the result type +//! `T` in our `thread_local!` and you can't have generic thread-local values. +//! So we have to erase types. Annoying! + +use std::{any::TypeId, cell::RefCell, fmt::Debug, ops::ControlFlow}; + +use crate::{ + language::Language, + parse::{parser::Associativity, ParseError, ParseResult, Scope, SuccessfulParse}, +}; + +use super::Precedence; + +thread_local! { + static STACK: RefCell> = Default::default() +} + +/// Tracks an active parse that is taking place. +struct StackEntry { + /// The scope pointer: we use `()` instead of `Scope` + scope: *const (), + + /// The starting text: we use `*const` instead of `&'t str` + start_text: *const str, + + current_state: Option, + + /// The TypeId of the type `T`. + type_id: TypeId, + + /// The intermediate value produced. If `Some`, this is a pointer + /// to a `SuccessfulParse<'t, T>`. + value: Option<*const ()>, + + /// + observed: bool, +} + +#[derive(Copy, Clone, Debug)] +pub(super) struct CurrentState { + pub left_right: LeftRight, + pub precedence: Precedence, + pub current_text: *const str, +} + +/// Determines the kind of recursion the current variant +/// would have if it recursed. For example, given a grammar +/// with a variant +/// +/// ```text +/// E = E + E +/// ```` +/// +/// when `E` recurses, the first `E` is considered `Left` +/// because it occurs before any tokens have been consumed. +/// The second `E` is considered `Right`. +/// +/// This terminology is a bit weird if you have three recursions, +/// e.g. `E = E + E + E`. Really we should consider any further +/// recursions as `Other`, I suppose, but I'm too lazy to deal with that +/// right now. +#[derive(Copy, Clone, Debug)] +pub(super) enum LeftRight { + /// Have not yet consumed any tokens. + Left, + + /// Consumed some tokens. + Right, +} + +impl StackEntry { + pub fn new(scope: &Scope, start_text: &str) -> Self + where + L: Language, + T: Clone + 'static, + { + Self { + scope: erase_type(scope), + current_state: None, + start_text, + type_id: TypeId::of::(), + value: None, + observed: false, + } + } + + pub fn matches_start_state(&self, scope: &Scope, start_text: &str) -> bool + where + L: Language, + T: Clone + 'static, + { + let scope: *const () = erase_type(scope); + let start_text: *const str = start_text; + let type_id = TypeId::of::(); + scope == self.scope && start_text == self.start_text && self.type_id == type_id + } + + /// True if a call to parse a value of type `T` with the given scope scope/text + /// matches the current state of this stack frame -- this means that it is a recursive + /// call from this stack frame (directly or indirectly). + /// + /// # Example + /// + /// Consider this grammar: + /// + /// ```text + /// E = E + E + /// | ( E ) + /// | integer + /// ``` + /// + /// and sample input `"1 + (2 + 3)"`. + /// + /// We will start with the variant `E = E + E`. This will recursively try to parse + /// an `E` two times. + /// Each time it will invoke [`recurse`][] which will record the current text. + /// + /// The first time, the current text will be `"1 + (2 + 3)"`, same as the start text. + /// When we start parsing `E`, we'll invoke [`enter`][] and see a (left) match. + /// + /// Later, after we've parsed `1 + `, we'll try to parse another `E`, but with + /// the current text `"(2 + 3)"`. We'll again see a (right) match and eventually + /// reach the variant `E = ( E )`. + /// + /// This variant will recurse *again*. But this time the current text is `"2 + 3)"`. + /// This is not considered a recursive match. + pub fn matches_current_state<'t, L, T>( + &self, + scope: &Scope, + start_text: &'t str, + ) -> Option + where + L: Language, + T: 'static, + { + // Convert incoming scope/text to raw pointers. + let scope: *const () = erase_type(scope); + let type_id = TypeId::of::(); + + // Scope and type-id must match. + if scope != self.scope || type_id != self.type_id { + return None; + } + + // Start text must match *current* text of this frame. + let start_text: *const str = start_text; + let Some(current_state) = &self.current_state else { + panic!("observed a stack frame with no current state (forgot to call `recuse`?"); + }; + if start_text != current_state.current_text { + return None; + } + + // OK, we have a match! + Some(*current_state) + } + + /// UNSAFE: Caller must guarantee that `self.value` pointer is valid. + pub unsafe fn observe<'t, T>(&mut self, start_text: &'t str) -> Option> + where + T: Clone + 'static, + { + assert_eq!(self.type_id, TypeId::of::()); + assert_eq!(self.start_text, start_text); // must be left-recursion + assert!(self.current_state.is_some()); + + self.observed = true; + + let ptr = self.value?; + let ptr = ptr as *const SuccessfulParse<'t, T>; + // UNSAFE: We rely on the caller to entry ptr is valid. + let ptr = unsafe { &*ptr }; + + Some(ptr.clone()) + } +} + +pub fn enter<'s, 't, L, T>( + scope: &'s Scope, + text: &'t str, + mut op: impl FnMut(usize) -> ParseResult<'t, T>, +) -> ParseResult<'t, T> +where + L: Language, + T: Debug + Clone + Eq + 'static, +{ + tracing::trace!( + "enter<{}>(scope={:?}, text={:?})", + std::any::type_name::(), + scope, + text + ); + + // First check whether we are already parsing this same text in this same scope as this same type. + let mut min_precedence_level = 0; + let return_value = STACK.with_borrow_mut(|stack| { + for entry in stack.iter_mut().rev() { + match entry.matches_current_state::(scope, text) { + // Keep searching. + None => (), + + // If this is left-recursion, then we will always return some value, but which value + // depends on a few factors. Consider `E = int | E + E | E * E` as our example. Because this + // is left-recursion, we know we are recursively parsing the first `E` in the `E + E` + // or `E * E` variant. + // + // The easy case is where there is no previous result. In that case, we return an error. + // This consitutes the 'base case' for a recursive grammar. + // We will only successfully parse the `int` case on that first round, but then we will try again. + // + // Otherwise, we have a prevous result for E, and we need to decide whether we can + // embed it into the variant we are currently parsing. Here we must consider the + // precedence of the variant we are parsing as well as the precedence level of the value + // we are attempting to reuse. The general rule is that you can embed higher precedence things + // into lower precedence things but not vice versa. + // + // If the current variant is left associative (as are all variants in our example), + // then we can accept values with the same precedence level as the variant or higher. + // So if the variant is `E * E` (precedence level = 2), we would only accept precedence + // level 2 (`E * E`) or 3 (`int`). If the variant were `E + E`, we could acccept anything. + // + // If the current variant is right or none associative, then we can accept values with + // strictly higher precedence level. So e.g. if `E + E` were level 1 and non-associative, + // then it would accept only things at level 2 or higher. + Some(CurrentState { + left_right: LeftRight::Left, + precedence: current_precedence, + .. + }) => { + let previous_result = unsafe { + // UNSAFE: See [1] below for justification. + entry.observe::(text) + }; + tracing::trace!( + "found left-recursive stack entry with precedence {:?}, previous_result = {:?}", + current_precedence, + previous_result + ); + let Some(previous_result) = previous_result else { + // Case 1: no previous value. + return ControlFlow::Break(Err(ParseError::at( + text, + format!( + "left-recursion on `{}` with no previous value", + std::any::type_name::() + ), + ))); + }; + + // If there is a previous value, check the precedence as described above. + let precedence_valid = match current_precedence.associativity { + Associativity::Left => { + previous_result.precedence.level >= current_precedence.level + } + Associativity::Right | Associativity::None => { + previous_result.precedence.level > current_precedence.level + } + Associativity::Both => true, + }; + tracing::trace!( + "precedence_valid = {}", + precedence_valid, + ); + if !precedence_valid { + return ControlFlow::Break(Err(ParseError::at( + text, + format!( + "left-recursion with invalid precedence \ + (current variant has precedence {:?}, previous value has level {})", + current_precedence, previous_result.precedence.level, + ), + ))); + } + + // Return the previous value. + return ControlFlow::Break(Ok(previous_result)); + + // [1] UNSAFE: We need to justify that `entry.value` will be valid. + // + // Each entry in `stack` corresponds to an active stack frame `F` on this thread + // and each entry in `stack` is only mutated by `F` + // + // The value in `entry.value` will either be `None` (in which case it is valid) + // or `Some(p)` where `p` is a pointer. + // + // `p` will have been assigned by `F` just before invoking `op()`. It is a reference + // to the last value in a vector owned by `F`. Since `F` is still active, that vector + // is still valid. The borrow to produce `p` is valid (by inspection) because there are no + // accesses to the vector until `op` completes + // (and, to arrive at this code, `op` has not yet completed). + } + + // If this is right-recursion, then we will not reuse the previous value, but we will + // consult the level of the variant to limit the variants we consider in this parse. + // Consider `E = int | E + E | E * E` as our example. Because this + // is right-recursion, we know we are recursively parsing the SECOND `E` in the `E + E` + // or `E * E` variant. + // + // If the current variant is left or none associative (as in our example), + // then we set the minimum precedence level to **one higher** than the variant's precedence. + // So if `E * E` is the current variant, we would only permit precedence level 2 (the `int` variant). + // So if `E + E` is the current variant, we would permit precedence level 1 or 2. + // + // If the current variant is right associative, then we can accept values with + // equal precedence. + Some(CurrentState { + left_right: LeftRight::Right, + precedence: current_precedence, + .. + }) => { + tracing::trace!( + "found right-recursive stack entry with precedence {:?}", + current_precedence, + ); + match current_precedence.associativity { + Associativity::Left | Associativity::None => { + min_precedence_level = current_precedence.level + 1; + } + Associativity::Right => { + min_precedence_level = current_precedence.level; + } + Associativity::Both => {} + }; + break; + } + } + } + + stack.push(StackEntry::new::(scope, text)); + ControlFlow::Continue(()) + }); + + if let ControlFlow::Break(return_value) = return_value { + return return_value; + } + tracing::trace!("min_precedence_level = {}", min_precedence_level,); + + // Access the top stack frame. Use a macro because we don't support closures + // that are generic over the return type. + macro_rules! with_top { + (|$top:ident| $body:expr) => { + STACK.with_borrow_mut(|stack| { + let $top = stack.last_mut().unwrap(); + assert!($top.matches_start_state::(scope, text)); + $body + }) + }; + } + + // Pop the stack before we return + final_fn::final_fn!(STACK.with_borrow_mut(|stack| { + let top = stack.pop().unwrap(); + assert!(top.matches_start_state::(scope, text)); + })); + + // EXAMPLE: Consider this grammar + // + // ``` + // Expr = Expr '+' Expr + // | Integer + // ``` + // + // and this input `2 + 3`. We process this in rounds. + // + // Round 0: Previous value `value` is `None`. When we go to parse expr, it will recurse, + // which will yield an error that consumes zero tokens. We will then attempt integer, + // which succeeds, yielding a parsed result of `2` with remainder `+ 3`. + // + // Round 1: We store `(2, "+ 3")` as the previous result and try again. When we go to parse `Expr`, + // there are two options. First, we successfully parse as an integer just like before. + // But also we are able to parse as `Expr + Expr`, because the left recursive reference to `Expr` yields `2` + // and we can continue and parse `2 + 3`. The `Parser` prefers this longer result and so we get + // `2 + 3` as the final result. + // + // Round 2: We store `(2+3, "")` as the previous result and try again. *This time* when we recurse, + // we get `2` again! The reason why is a bit surprising. The parse of `2` succeeds with remainder + // `"+ 3"`. But when we go parse `Expr + Expr`, the first `Expr` result yields `2 + 3` and there are no more + // tokens, so that arm fails. In our loop below, we search back through the result and find that `2` has already + // occurred, so we take `2 + 3` as the best overall parse. + // + // It's a bit subtle why this is ok. It's relying on some properties of grammars and parsing. + // To be more obviously correct we would want to return sets of successful results. + // In particular, the assumption is that `op` is always returning a best result (if any) and panicking on + // ambiguity. + + // First round parse is a bit special, because if we get an error here, we can just return immediately, + // as there is no base case to build from. + let mut values = vec![]; + match op(min_precedence_level) { + Ok(v) => values.push(v), + Err(errs) => return Err(errs), + }; + + // Check whether there was recursion to begin with. + let observed = with_top!(|top| top.observed); + if !observed { + return Ok(values.pop().unwrap()); // If not, we are done. + } + + // OK, this is the interesting case. We may be able to get a better parse. + loop { + tracing::trace!( + "reparsing of left-recursive grammar: values = {:#?}", + values + ); + + // If we have an intermediate value, update the stack entry to point at. + // This takes a borrow of `value` but converts it into a raw pointer. + // This borrow lasts until after `op` is complete. + let best_value = values.last().unwrap(); + with_top!(|top| { + top.value = Some(erase_type(best_value)); + }); + + // Invoke the operation. As noted above, if we get a failed parse NOW, + // we know we already found the best result, so we can just use it. + let Ok(value1) = op(min_precedence_level) else { + return Ok(values.pop().unwrap()); // If not, we are done. + }; + + tracing::trace!("left-recursive grammar yielded: value1 = {:?}", value1); + + // If we got back on the previous results we saw, then we're entering + // a loop and we can stop and take the best one (which should also be the longest). + // In our example, this occurs when we parse `6` -- the first result + // succeeds, but we have to try again to see if there's a more complex + // expression that can be produced (there isn't). + if values.iter().any(|v| *v == value1) { + return Ok(values.pop().unwrap()); // If not, we are done. + } + + // Otherwise, we have to try again. + values.push(value1); + } +} + +pub fn recurse<'s, 't, R>(current_state: CurrentState, op: impl FnOnce() -> R) -> R { + STACK.with_borrow_mut(|stack| { + let top = stack.last_mut().unwrap(); + assert!( + top.current_state.is_none(), + "top of stack already has a current state" + ); + top.current_state = Some(current_state); + }); + + final_fn::final_fn!(STACK.with_borrow_mut(|stack| { + let top = stack.last_mut().unwrap(); + assert!( + top.current_state.is_some(), + "top of stack no longer has a current state" + ); + top.current_state = None; + })); + + op() +} + +fn erase_type(s: &T) -> *const () { + s as *const T as *const () +} diff --git a/crates/formality-core/src/util.rs b/crates/formality-core/src/util.rs new file mode 100644 index 00000000..691505de --- /dev/null +++ b/crates/formality-core/src/util.rs @@ -0,0 +1,9 @@ +/// Returns true if `t` is the default value for `t`. +/// Used by the "derive" code for `Debug`. +pub fn is_default(t: &T) -> bool +where + T: Default + Eq, +{ + let default_value: T = Default::default(); + default_value == *t +} diff --git a/crates/formality-macros/src/debug.rs b/crates/formality-macros/src/debug.rs index ec0241c3..98401013 100644 --- a/crates/formality-macros/src/debug.rs +++ b/crates/formality-macros/src/debug.rs @@ -1,4 +1,5 @@ extern crate proc_macro; + use convert_case::{Case, Casing}; use proc_macro2::{Ident, Literal, TokenStream}; use quote::{quote, quote_spanned}; @@ -20,6 +21,7 @@ pub(crate) fn derive_debug_with_spec( .into_compile_error(); } + let default_debug = default_debug_variant(&s); let debug_arms = s.each_variant(|v| debug_variant(v, external_spec)); s.gen_impl(quote! { @@ -29,15 +31,50 @@ pub(crate) fn derive_debug_with_spec( fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { #[allow(unused_assignments)] - match self { - #debug_arms + if fmt.alternate() { + #default_debug + } else { + match self { + #debug_arms + } + Ok(()) } - Ok(()) } } }) } +fn default_debug_variant(s: &synstructure::Structure) -> TokenStream { + let arms = s.each_variant(|v| { + let fields: TokenStream = v.bindings().iter().map(|bi| { + if let Some(name) = &bi.ast().ident { + let name = as_literal(name); + quote_spanned!(name.span() => .field(#name, #bi)) + } else { + quote_spanned!(bi.span() => .field(#bi)) + } + }).collect(); + let variant_name = as_literal(v.ast().ident); + match v.ast().fields { + syn::Fields::Named(_) => { + quote_spanned!(variant_name.span() => fmt.debug_struct(#variant_name) #fields .finish()) + } + syn::Fields::Unnamed(_) => { + quote_spanned!(variant_name.span() => fmt.debug_tuple(#variant_name) #fields .finish()) + } + syn::Fields::Unit => { + quote_spanned!(variant_name.span() => fmt.debug_tuple(#variant_name) .finish()) + } + } + }); + + quote_spanned! { s.ast().span() => + match self { + #arms + } + } +} + fn debug_variant( variant: &synstructure::VariantInfo, external_spec: Option<&FormalitySpec>, @@ -153,46 +190,7 @@ fn debug_variant_with_attr( } stream.extend(match op { - spec::FormalitySpecSymbol::Field { - name, - mode: FieldMode::Single | FieldMode::Optional, - } => { - quote_spanned! { - name.span() => - write!(fmt, "{}", sep)?; - write!(fmt, "{:?}", #name)?; - sep = " "; - } - } - - spec::FormalitySpecSymbol::Field { - name, - mode: FieldMode::Many, - } => { - quote_spanned! { - name.span() => - for e in #name { - write!(fmt, "{}", sep)?; - write!(fmt, "{:?}", e)?; - sep = " "; - } - } - } - - spec::FormalitySpecSymbol::Field { - name, - mode: FieldMode::Comma, - } => { - quote_spanned! { - name.span() => - for e in #name { - write!(fmt, "{}", sep)?; - write!(fmt, "{:?}", e)?; - sep = ", "; - } - sep = " "; - } - } + spec::FormalitySpecSymbol::Field { name, mode } => debug_field_with_mode(name, mode), spec::FormalitySpecSymbol::Keyword { ident } => { let literal = as_literal(ident); @@ -242,6 +240,75 @@ fn debug_variant_with_attr( stream } +fn debug_field_with_mode(name: &Ident, mode: &FieldMode) -> TokenStream { + match mode { + FieldMode::Single | FieldMode::Optional => { + quote_spanned! { name.span() => + write!(fmt, "{}", sep)?; + write!(fmt, "{:?}", #name)?; + sep = " "; + } + } + + FieldMode::Many => { + quote_spanned! { name.span() => + for e in #name { + write!(fmt, "{}", sep)?; + write!(fmt, "{:?}", e)?; + sep = " "; + } + } + } + + FieldMode::Comma => { + quote_spanned! { name.span() => + for e in #name { + write!(fmt, "{}", sep)?; + write!(fmt, "{:?}", e)?; + sep = ", "; + } + sep = " "; + } + } + + FieldMode::DelimitedVec { + open, + optional, + close, + } => { + let open = Literal::character(*open); + let close = Literal::character(*close); + quote_spanned! { name.span() => + if !#optional || !#name.is_empty() { + write!(fmt, "{}", sep)?; + write!(fmt, "{}", #open)?; + sep = ""; + for e in #name { + write!(fmt, "{}", sep)?; + write!(fmt, "{:?}", e)?; + sep = ", "; + } + write!(fmt, "{}", #close)?; + sep = " "; + } + } + } + + FieldMode::Guarded { guard, mode } => { + let guard = as_literal(guard); + let base = debug_field_with_mode(name, mode); + + quote_spanned! { name.span() => + if !::formality_core::util::is_default(#name) { + write!(fmt, "{}{}", sep, #guard)?; + sep = " "; + #base + } + } + } + } +} + fn get_grammar_attr(attrs: &[Attribute]) -> Option> { let attr = attrs.iter().find(|a| a.path().is_ident("grammar"))?; Some(attr.parse_args()) diff --git a/crates/formality-macros/src/parse.rs b/crates/formality-macros/src/parse.rs index bd05ac29..65c45651 100644 --- a/crates/formality-macros/src/parse.rs +++ b/crates/formality-macros/src/parse.rs @@ -1,7 +1,7 @@ extern crate proc_macro; use convert_case::{Case, Casing}; -use proc_macro2::{Ident, Literal, TokenStream}; +use proc_macro2::{Ident, Literal, Span, TokenStream}; use quote::{quote, quote_spanned}; use syn::{spanned::Spanned, Attribute}; use synstructure::BindingInfo; @@ -37,11 +37,18 @@ pub(crate) fn derive_parse_with_spec( } } + // Determine whether *any* variant is marked as `#[variable]`. + // If so, all *other* variants will automatically reject in-scope variable names. + let any_variable_variant = s + .variants() + .iter() + .any(|v| has_variable_attr(v.ast().attrs)); + let mut parse_variants = TokenStream::new(); for variant in s.variants() { - let variant_name = as_literal(variant.ast().ident); - let v = parse_variant(variant, external_spec)?; - let precedence = precedence(&variant.ast().attrs)?.literal(); + let variant_name = Literal::string(&format!("{}::{}", s.ast().ident, variant.ast().ident)); + let v = parse_variant(variant, external_spec, any_variable_variant)?; + let precedence = precedence(&variant.ast().attrs)?.expr(); parse_variants.extend(quote_spanned!( variant.ast().ident.span() => __parser.parse_variant(#variant_name, #precedence, |__p| { #v }); @@ -55,9 +62,9 @@ pub(crate) fn derive_parse_with_spec( gen impl parse::CoreParse for @Self { fn parse<'t>(scope: &parse::Scope, text: &'t str) -> parse::ParseResult<'t, Self> { - let mut __parser = parse::Parser::new(scope, text, #type_name); - #parse_variants; - __parser.finish() + parse::Parser::multi_variant(scope, text, #type_name, |__parser| { + #parse_variants; + }) } } })) @@ -66,17 +73,27 @@ pub(crate) fn derive_parse_with_spec( fn parse_variant( variant: &synstructure::VariantInfo, external_spec: Option<&FormalitySpec>, + any_variable_variant: bool, ) -> syn::Result { let ast = variant.ast(); + let has_variable_attr = has_variable_attr(ast.attrs); + let mut stream = TokenStream::default(); + + // If there are variable variants -- but this is not one -- then we always begin by rejecting + // variable names. This avoids ambiguity in a case like `for { X : Debug }`, where we + // want to parse `X` as a type variable. + if any_variable_variant && !has_variable_attr { + stream.extend(quote_spanned!(ast.ident.span() => __p.reject_variable()?;)); + } // When invoked like `#[term(foo)]`, use the spec from `foo` if let Some(spec) = external_spec { - return parse_variant_with_attr(variant, spec); + return parse_variant_with_attr(variant, spec, stream); } // Else, look for a `#[grammar]` attribute on the variant if let Some(attr) = get_grammar_attr(ast.attrs) { - return parse_variant_with_attr(variant, &attr?); + return parse_variant_with_attr(variant, &attr?, stream); } // If no `#[grammar(...)]` attribute is provided, then we provide default behavior. @@ -85,34 +102,35 @@ fn parse_variant( // No bindings (e.g., `Foo`) -- just parse a keyword `foo` let literal = Literal::string(&to_parse_ident(ast.ident)); let construct = variant.construct(|_, _| quote! {}); - Ok(quote_spanned! { + stream.extend(quote_spanned! { ast.ident.span() => __p.expect_keyword(#literal)?; Ok(#construct) - }) - } else if has_variable_attr(variant.ast().attrs) { + }); + } else if has_variable_attr { // Has the `#[variable]` attribute -- parse an identifier and then check to see if it is present // in the scope. If so, downcast it and check that it has the correct kind. - Ok(quote_spanned! { + stream.extend(quote_spanned! { ast.ident.span() => let v = __p.variable()?; Ok(v) - }) + }); } else if has_cast_attr(variant.ast().attrs) { // Has the `#[cast]` attribute -- just parse the bindings (comma separated, if needed) let build: Vec = parse_bindings(variant.bindings()); let construct = variant.construct(field_ident); - Ok(quote_spanned! { + stream.extend(quote_spanned! { ast.ident.span() => + __p.mark_as_cast_variant(); #(#build)* Ok(#construct) - }) + }); } else { // Otherwise -- parse `variant(binding0, ..., bindingN)` let literal = Literal::string(&to_parse_ident(ast.ident)); let build: Vec = parse_bindings(variant.bindings()); let construct = variant.construct(field_ident); - Ok(quote_spanned! { + stream.extend(quote_spanned! { ast.ident.span() => __p.expect_keyword(#literal)?; __p.expect_char('(')?; @@ -120,8 +138,10 @@ fn parse_variant( __p.skip_trailing_comma(); __p.expect_char(')')?; Ok(#construct) - }) + }); } + + Ok(stream) } /// When a type is given a formality attribute, we use that to guide parsing: @@ -143,49 +163,15 @@ fn parse_variant( fn parse_variant_with_attr( variant: &synstructure::VariantInfo, spec: &FormalitySpec, + mut stream: TokenStream, ) -> syn::Result { - let mut stream = TokenStream::new(); - for symbol in &spec.symbols { stream.extend(match symbol { - spec::FormalitySpecSymbol::Field { - name, - mode: FieldMode::Single, - } => { - quote_spanned! { - name.span() => - let #name = __p.nonterminal()?; - } - } - - spec::FormalitySpecSymbol::Field { - name, - mode: FieldMode::Optional, - } => { - quote_spanned! { - name.span() => - let #name = __p.opt_nonterminal()?; - let #name = #name.unwrap_or_default(); - } - } - - spec::FormalitySpecSymbol::Field { - name, - mode: FieldMode::Many, - } => { - quote_spanned! { - name.span() => - let #name = __p.many_nonterminal()?; - } - } - - spec::FormalitySpecSymbol::Field { - name, - mode: FieldMode::Comma, - } => { + spec::FormalitySpecSymbol::Field { name, mode } => { + let initializer = parse_field_mode(name.span(), mode); quote_spanned! { name.span() => - let #name = __p.comma_nonterminal()?; + let #name = #initializer; } } @@ -222,6 +208,62 @@ fn parse_variant_with_attr( Ok(stream) } +fn parse_field_mode(span: Span, mode: &FieldMode) -> TokenStream { + match mode { + FieldMode::Single => { + quote_spanned! { + span => + __p.nonterminal()? + } + } + + FieldMode::Optional => { + quote_spanned! { + span => + __p.opt_nonterminal()?.unwrap_or_default() + } + } + + FieldMode::Many => { + quote_spanned! { + span => + __p.many_nonterminal()? + } + } + + FieldMode::DelimitedVec { + open, + optional, + close, + } => { + let open = Literal::character(*open); + let close = Literal::character(*close); + quote_spanned! { + span => + __p.delimited_nonterminal(#open, #optional, #close)? + } + } + FieldMode::Comma => { + quote_spanned! { + span => + __p.comma_nonterminal()? + } + } + + FieldMode::Guarded { guard, mode } => { + let guard_keyword = as_literal(guard); + let initializer = parse_field_mode(span, mode); + quote_spanned! { + span => + match __p.expect_keyword(#guard_keyword) { + Ok(()) => #initializer, + Err(_) => Default::default(), + } + } + } + } +} + fn get_grammar_attr(attrs: &[Attribute]) -> Option> { let attr = attrs.iter().find(|a| a.path().is_ident("grammar"))?; Some(attr.parse_args()) diff --git a/crates/formality-macros/src/precedence.rs b/crates/formality-macros/src/precedence.rs index 4eee2f66..0350016f 100644 --- a/crates/formality-macros/src/precedence.rs +++ b/crates/formality-macros/src/precedence.rs @@ -1,16 +1,39 @@ use std::str::FromStr; -use proc_macro2::{Literal, TokenStream}; +use proc_macro2::{Ident, Literal, Span, TokenStream}; +use quote::quote; use syn::spanned::Spanned; -#[derive(Default, Debug)] -pub(crate) struct Precedence { - pub level: usize, +#[derive(Debug)] +pub(crate) enum Precedence { + Defaulted, + Parsed { + level: usize, + + /// Will be either the name of a construct method on the + /// `Parser::Associativity` type: `left``, `right``, or `none`. + associativity: Ident, + }, } impl Precedence { - pub fn literal(&self) -> Literal { - Literal::usize_unsuffixed(self.level) + pub fn expr(&self) -> TokenStream { + match self { + Precedence::Defaulted => quote!(formality_core::parse::Precedence::default()), + Precedence::Parsed { + level, + associativity, + } => { + let level = Literal::usize_unsuffixed(*level); + quote!(formality_core::parse::Precedence::#associativity(#level)) + } + } + } +} + +impl Default for Precedence { + fn default() -> Self { + Precedence::Defaulted } } @@ -18,7 +41,7 @@ impl syn::parse::Parse for Precedence { fn parse(input: syn::parse::ParseStream) -> syn::Result { let token_stream: TokenStream = input.parse()?; let span = token_stream.span(); - let mut tokens = token_stream.into_iter(); + let mut tokens = token_stream.into_iter().peekable(); let Some(token) = tokens.next() else { return Err(syn::Error::new(span, "precedence expected")); @@ -42,10 +65,49 @@ impl syn::parse::Parse for Precedence { } } + const VALID_ASSOCIATIVITIES: &[&str] = &["left", "right", "none"]; + let associativity = if let Some(comma_token) = tokens.next() { + match &comma_token { + proc_macro2::TokenTree::Punct(punct) if punct.as_char() == ',' => { + match tokens.next() { + Some(proc_macro2::TokenTree::Ident(ident)) + if VALID_ASSOCIATIVITIES + .iter() + .any(|a| *a == ident.to_string()) => + { + ident + } + + _ => { + return Err(syn::Error::new( + comma_token.span(), + &format!( + "expected valid associativity after comma, one of `{:?}`", + VALID_ASSOCIATIVITIES + ), + )); + } + } + } + + _ => { + return Err(syn::Error::new( + comma_token.span(), + "extra `,` followed by associativity", + )); + } + } + } else { + Ident::new("left", Span::call_site()) + }; + if let Some(token) = tokens.next() { return Err(syn::Error::new(token.span(), "extra tokens")); } - Ok(Precedence { level }) + Ok(Precedence::Parsed { + level, + associativity, + }) } } diff --git a/crates/formality-macros/src/spec.rs b/crates/formality-macros/src/spec.rs index 23c1cf80..21549981 100644 --- a/crates/formality-macros/src/spec.rs +++ b/crates/formality-macros/src/spec.rs @@ -1,4 +1,7 @@ -use proc_macro2::{Ident, Punct, TokenStream, TokenTree}; +use std::{iter::Peekable, sync::Arc}; + +use proc_macro2::{Group, Ident, Punct, TokenStream, TokenTree}; +use syn::spanned::Spanned; /// The "formality spec" guides parsing and serialization. /// @@ -33,6 +36,26 @@ pub enum FieldMode { /// $x -- just parse `x` Single, + Guarded { + guard: Ident, + mode: Arc, + }, + + /// $ -- `x` is a `Vec`, parse `` + /// $[x] -- `x` is a `Vec`, parse `[E0,...,En]` + /// $(x) -- `x` is a `Vec`, parse `(E0,...,En)` + /// $ -- `x` is a `Vec`, parse `` or empty list + /// $[?x] -- `x` is a `Vec`, parse `[E0,...,En]` or empty list + /// $(?x) -- `x` is a `Vec`, parse `(E0,...,En)` or empty list + /// + /// If the next op is a fixed character, stop parsing when we see that. + /// Otherwise parse as many we can greedily. + DelimitedVec { + open: char, + optional: bool, + close: char, + }, + /// $*x -- `x` is a `Vec`, parse multiple `E` /// /// If the next op is a fixed character, stop parsing when we see that. @@ -70,24 +93,19 @@ fn token_stream_to_symbols( while let Some(token) = tokens.next() { match token { proc_macro2::TokenTree::Group(v) => { - let (open_text, close_text) = match v.delimiter() { - proc_macro2::Delimiter::Parenthesis => (Some('('), Some(')')), - proc_macro2::Delimiter::Brace => (Some('{'), Some('}')), - proc_macro2::Delimiter::Bracket => (Some('['), Some(']')), - proc_macro2::Delimiter::None => (None, None), - }; - - if let Some(ch) = open_text { + let open_close = open_close(&v); + + if let Some((ch, _)) = open_close { symbols.push(Delimeter { text: ch }); } token_stream_to_symbols(symbols, v.stream())?; - if let Some(ch) = close_text { + if let Some((_, ch)) = open_close { symbols.push(Delimeter { text: ch }); } } proc_macro2::TokenTree::Ident(ident) => symbols.push(Keyword { ident }), proc_macro2::TokenTree::Punct(punct) => match punct.as_char() { - '$' => symbols.push(parse_variable_binding(punct, &mut tokens)?), + '$' => symbols.push(parse_variable_binding(&punct, &mut tokens)?), _ => symbols.push(Char { punct }), }, proc_macro2::TokenTree::Literal(_) => { @@ -107,46 +125,201 @@ fn token_stream_to_symbols( /// or we could also see a `$`, in which case user wrote `$$`, and we treat that as a single /// `$` sign. fn parse_variable_binding( - dollar_token: Punct, - tokens: &mut impl Iterator, + dollar_token: &Punct, + tokens: &mut dyn Iterator, ) -> syn::Result { - let error = || { - let message = "expected field name or field mode (`,`, `*`)"; - Err(syn::Error::new(dollar_token.span(), message)) - }; + let mut tokens = tokens.peekable(); - let mut next_token = match tokens.next() { - Some(v) => v, - None => return error(), + let Some(token) = tokens.peek() else { + return error( + dollar_token, + "incomplete field reference; use `$$` if you just want a dollar sign", + ); }; - // If there is a field mode (e.g., `*`) present, parse it. - let mode = match next_token { - TokenTree::Ident(_) => FieldMode::Single, - TokenTree::Punct(punct) => { - let mode = match punct.as_char() { - ',' => FieldMode::Comma, - '*' => FieldMode::Many, - '?' => FieldMode::Optional, - '$' => return Ok(FormalitySpecSymbol::Char { punct }), - _ => return error(), + return match token { + // $x + TokenTree::Ident(_) => { + parse_variable_binding_name(dollar_token, FieldMode::Single, &mut tokens) + } + + // $$ + TokenTree::Punct(punct) if punct.as_char() == '$' => { + let Some(TokenTree::Punct(punct)) = tokens.next() else { + unreachable!() }; + Ok(FormalitySpecSymbol::Char { punct }) + } + + // $,x + TokenTree::Punct(punct) if punct.as_char() == ',' => { + tokens.next(); + parse_variable_binding_name(dollar_token, FieldMode::Comma, &mut tokens) + } - next_token = match tokens.next() { - Some(v) => v, - None => return error(), + // $*x + TokenTree::Punct(punct) if punct.as_char() == '*' => { + tokens.next(); + parse_variable_binding_name(dollar_token, FieldMode::Many, &mut tokens) + } + + // $?x + TokenTree::Punct(punct) if punct.as_char() == '?' => { + tokens.next(); + parse_variable_binding_name(dollar_token, FieldMode::Optional, &mut tokens) + } + + // $:guard $x + TokenTree::Punct(punct) if punct.as_char() == ':' => { + let guard_token = tokens.next().unwrap(); + parse_guarded_variable_binding(dollar_token, guard_token, &mut tokens) + } + + // $ or $ + TokenTree::Punct(punct) if punct.as_char() == '<' => { + tokens.next(); + + // consume `x` or `?x` + let result = parse_delimited(dollar_token, '<', '>', &mut tokens)?; + + // we should see a `>` next + match tokens.next() { + Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => Ok(result), + _ => error(dollar_token, "expected a `>` to end this field reference"), + } + } + + // $(x) or $(?x) + // $[x] or $[?x] + // ${x} or ${?x} + TokenTree::Group(_) => { + let Some(TokenTree::Group(group)) = tokens.next() else { + unreachable!() + }; + let Some((open, close)) = open_close(&group) else { + return error(&group, "did not expect a spliced macro_rules group here"); }; - mode + // consume `x` or `?x` + let mut group_tokens = group.stream().into_iter().peekable(); + let result = parse_delimited(dollar_token, open, close, &mut group_tokens)?; + + // there shouldn't be anything else in the token tree + if let Some(t) = group_tokens.next() { + return error( + &t, + "extra characters in delimited field reference after field name", + ); + } + Ok(result) } - TokenTree::Group(_) | TokenTree::Literal(_) => return error(), - }; - // Extract the name of the field. - let name = match next_token { - TokenTree::Ident(name) => name, - _ => return error(), + _ => error(dollar_token, "invalid field reference"), }; - Ok(FormalitySpecSymbol::Field { name, mode }) + fn parse_guarded_variable_binding( + dollar_token: &Punct, + guard_token: TokenTree, + tokens: &mut Peekable>, + ) -> syn::Result { + // The next token should be an identifier + let Some(TokenTree::Ident(guard_ident)) = tokens.next() else { + return error( + &guard_token, + "expected an identifier after a `:` in a field reference", + ); + }; + + // The next token should be a `$`, beginning another variable binding + let next_dollar_token = match tokens.next() { + Some(TokenTree::Punct(next_dollar_token)) if next_dollar_token.as_char() == '$' => { + next_dollar_token + } + + _ => { + return error( + &dollar_token, + "expected another `$` field reference to follow the `:` guard", + ); + } + }; + + // Then should come another field reference. + let FormalitySpecSymbol::Field { name, mode } = + parse_variable_binding(&next_dollar_token, tokens)? + else { + return error( + &next_dollar_token, + "`$:` must be followed by another field reference, not a `$$` literal", + ); + }; + + let guard_mode = FieldMode::Guarded { + guard: guard_ident, + mode: Arc::new(mode), + }; + + Ok(FormalitySpecSymbol::Field { + name: name, + mode: guard_mode, + }) + } + + fn parse_delimited( + dollar_token: &Punct, + open: char, + close: char, + tokens: &mut Peekable>, + ) -> syn::Result { + // Check for a `?` and consume it, if present. + let optional = match tokens.peek() { + Some(TokenTree::Punct(punct)) if punct.as_char() == '?' => { + tokens.next(); // drop `?` character + true + } + + _ => false, + }; + + parse_variable_binding_name( + dollar_token, + FieldMode::DelimitedVec { + open, + optional, + close, + }, + tokens, + ) + } + + fn parse_variable_binding_name( + dollar_token: &Punct, + mode: FieldMode, + tokens: &mut impl Iterator, + ) -> syn::Result { + // Extract the name of the field. + let name = match tokens.next() { + Some(TokenTree::Ident(name)) => name, + _ => return error(dollar_token, "expected field name"), + }; + + Ok(FormalitySpecSymbol::Field { name, mode }) + } + + fn error(at_token: &impl Spanned, message: impl ToString) -> syn::Result { + let mut message = message.to_string(); + if message.is_empty() { + message = "invalid field reference in grammar".into(); + } + Err(syn::Error::new(at_token.span(), message)) + } +} + +fn open_close(g: &Group) -> Option<(char, char)> { + match g.delimiter() { + proc_macro2::Delimiter::Parenthesis => Some(('(', ')')), + proc_macro2::Delimiter::Brace => Some(('{', '}')), + proc_macro2::Delimiter::Bracket => Some(('[', ']')), + proc_macro2::Delimiter::None => None, + } } diff --git a/crates/formality-prove/src/decls.rs b/crates/formality-prove/src/decls.rs index 814e9fa9..38307b36 100644 --- a/crates/formality-prove/src/decls.rs +++ b/crates/formality-prove/src/decls.rs @@ -111,7 +111,7 @@ pub struct ImplDecl { } /// Data bound under the generics from [`ImplDecl`][] -#[term($trait_ref where $where_clause)] +#[term($trait_ref $:where $where_clause)] pub struct ImplDeclBoundData { /// The trait ref that is implemented pub trait_ref: TraitRef, @@ -129,7 +129,7 @@ pub struct NegImplDecl { } /// Data bound under the impl generics for a negative impl -#[term(!$trait_ref where $where_clause)] +#[term(!$trait_ref $:where $where_clause)] pub struct NegImplDeclBoundData { pub trait_ref: TraitRef, pub where_clause: Wcs, @@ -209,7 +209,7 @@ pub struct TraitInvariantBoundData { } /// The "bound data" for a [`TraitDecl`][] -- i.e., what is covered by the forall. -#[term(where $where_clause)] +#[term($:where $where_clause)] pub struct TraitDeclBoundData { /// The where-clauses declared on the trait pub where_clause: Wcs, @@ -231,7 +231,7 @@ impl AliasEqDecl { } /// Data bound under the impl generics for a [`AliasEqDecl`][] -#[term($alias = $ty where $where_clause)] +#[term($alias = $ty $:where $where_clause)] pub struct AliasEqDeclBoundData { /// The alias that is equal pub alias: AliasTy, @@ -258,7 +258,7 @@ impl AliasBoundDecl { } } -#[term($alias : $ensures where $where_clause)] +#[term($alias : $ensures $:where $where_clause)] pub struct AliasBoundDeclBoundData { pub alias: AliasTy, // FIXME: this is currently encoded as something like ` [T: Foo]` where @@ -281,7 +281,7 @@ pub struct AdtDecl { } /// The "bound data" for a [`AdtDecl`][]. -#[term(where $where_clause)] +#[term($:where $where_clause)] pub struct AdtDeclBoundData { /// The where-clauses declared on the ADT, pub where_clause: Wcs, diff --git a/crates/formality-prove/src/prove/minimize/test.rs b/crates/formality-prove/src/prove/minimize/test.rs index 6242d8a2..a80688a2 100644 --- a/crates/formality-prove/src/prove/minimize/test.rs +++ b/crates/formality-prove/src/prove/minimize/test.rs @@ -17,42 +17,13 @@ fn minimize_a() { let (env, subst) = env.existential_substitution(&term); let term = term.instantiate_with(&subst).unwrap(); - expect![[r#" - ( - Env { - variables: [ - ?ty_1, - ?ty_2, - ?ty_3, - ], - coherence_mode: false, - }, - [ - ?ty_1, - ?ty_3, - ], - ) - "#]] - .assert_debug_eq(&(&env, &term)); + expect!["(Env { variables: [?ty_1, ?ty_2, ?ty_3], coherence_mode: false }, [?ty_1, ?ty_3])"] + .assert_eq(&format!("{:?}", (&env, &term))); let (mut env_min, term_min, m) = minimize(env, term); - expect![[r#" - ( - Env { - variables: [ - ?ty_0, - ?ty_1, - ], - coherence_mode: false, - }, - [ - ?ty_0, - ?ty_1, - ], - ) - "#]] - .assert_debug_eq(&(&env_min, &term_min)); + expect!["(Env { variables: [?ty_0, ?ty_1], coherence_mode: false }, [?ty_0, ?ty_1])"] + .assert_eq(&format!("{:?}", (&env_min, &term_min))); let ty0 = term_min[0].as_variable().unwrap(); let ty1 = term_min[1].as_variable().unwrap(); diff --git a/crates/formality-prove/src/test/adt_wf.rs b/crates/formality-prove/src/test/adt_wf.rs index d3bf00d9..5a633722 100644 --- a/crates/formality-prove/src/test/adt_wf.rs +++ b/crates/formality-prove/src/test/adt_wf.rs @@ -10,7 +10,7 @@ use crate::{decls::Decls, prove::prove}; fn decls() -> Decls { Decls { trait_decls: vec![term("trait Foo where {}")], - impl_decls: vec![term("impl<> Foo(u32) where {}")], + impl_decls: vec![term("impl Foo(u32) where {}")], adt_decls: vec![term("adt X where {Foo(T)}")], ..Decls::empty() } diff --git a/crates/formality-prove/src/test/magic_copy.rs b/crates/formality-prove/src/test/magic_copy.rs index 04ded95b..0dafe603 100644 --- a/crates/formality-prove/src/test/magic_copy.rs +++ b/crates/formality-prove/src/test/magic_copy.rs @@ -15,7 +15,7 @@ fn decls() -> Decls { ], impl_decls: vec![ term("impl Magic(T) where {Magic(T)}"), - term("impl<> Copy(u32) where {}"), + term("impl Copy(u32) where {}"), ], ..Decls::empty() } diff --git a/crates/formality-prove/src/test/simple_impl.rs b/crates/formality-prove/src/test/simple_impl.rs index ded1a560..774b312e 100644 --- a/crates/formality-prove/src/test/simple_impl.rs +++ b/crates/formality-prove/src/test/simple_impl.rs @@ -11,7 +11,7 @@ fn decls() -> Decls { trait_decls: vec![term("trait Debug where {}")], impl_decls: vec![ term("impl Debug(Vec) where {Debug(T)}"), - term("impl<> Debug(u32) where {}"), + term("impl Debug(u32) where {}"), ], ..Decls::empty() } diff --git a/crates/formality-rust/src/grammar.rs b/crates/formality-rust/src/grammar.rs index 424a3790..f36f52f9 100644 --- a/crates/formality-rust/src/grammar.rs +++ b/crates/formality-rust/src/grammar.rs @@ -96,7 +96,7 @@ impl Struct { } } -#[term(where $where_clauses { $,fields })] +#[term($:where $,where_clauses { $,fields })] pub struct StructBoundData { pub where_clauses: Vec, pub fields: Vec, @@ -148,7 +148,7 @@ pub struct Adt { pub binder: Binder, } -#[term(where $where_clauses { $,variants })] +#[term($:where $,where_clauses { $,variants })] pub struct AdtBoundData { pub where_clauses: Vec, pub variants: Vec, @@ -179,7 +179,7 @@ impl TraitBinder { } } -#[term(where $where_clauses { $*trait_items })] +#[term($:where $,where_clauses { $*trait_items })] pub struct TraitBoundData { pub where_clauses: Vec, pub trait_items: Vec, @@ -199,7 +199,7 @@ pub struct Fn { pub binder: Binder, } -#[term(($,input_tys) -> $output_ty where $where_clauses $body)] +#[term($(input_tys) -> $output_ty $:where $,where_clauses $body)] pub struct FnBoundData { pub input_tys: Vec, pub output_ty: Ty, @@ -232,7 +232,7 @@ pub struct AssociatedTy { pub binder: Binder, } -#[term(: $ensures where $where_clauses)] +#[term(: $ensures $:where $,where_clauses)] pub struct AssociatedTyBoundData { /// So e.g. `type Item : [Sized]` would be encoded as ` (I: Sized)`. pub ensures: Vec, @@ -252,7 +252,7 @@ impl TraitImpl { } } -#[term($trait_id < $,trait_parameters > for $self_ty where $where_clauses { $*impl_items })] +#[term($trait_id $ for $self_ty $:where $,where_clauses { $*impl_items })] pub struct TraitImplBoundData { pub trait_id: TraitId, pub self_ty: Ty, @@ -272,7 +272,7 @@ pub struct NegTraitImpl { pub binder: Binder, } -#[term(!$trait_id < $,trait_parameters > for $self_ty where $where_clauses { })] +#[term(!$trait_id $ for $self_ty $:where $,where_clauses { })] pub struct NegTraitImplBoundData { pub trait_id: TraitId, pub self_ty: Ty, @@ -300,7 +300,7 @@ pub struct AssociatedTyValue { pub binder: Binder, } -#[term(= $ty where $where_clauses)] +#[term(= $ty $:where $,where_clauses)] pub struct AssociatedTyValueBoundData { pub where_clauses: Vec, pub ty: Ty, @@ -338,7 +338,7 @@ impl WhereClause { #[term] pub enum WhereClauseData { - #[grammar($v0 : $v1 < $,v2 >)] + #[grammar($v0 : $v1 $)] IsImplemented(Ty, TraitId, Vec), #[grammar($v0 => $v1)] @@ -367,7 +367,7 @@ impl WhereBound { #[term] pub enum WhereBoundData { - #[grammar($v0 < $,v1 >)] + #[grammar($v0 $)] IsImplemented(TraitId, Vec), #[grammar($v0)] diff --git a/crates/formality-rust/src/prove.rs b/crates/formality-rust/src/prove.rs index e9193567..f7bc8996 100644 --- a/crates/formality-rust/src/prove.rs +++ b/crates/formality-rust/src/prove.rs @@ -6,11 +6,8 @@ use crate::grammar::{ }; use formality_core::{seq, Set, To, Upcast, Upcasted}; use formality_prove as prove; -use formality_types::{ - grammar::{ - AdtId, AliasTy, Binder, ParameterKind, Predicate, Relation, TraitId, Ty, Wc, Wcs, PR, - }, - rust::BoundVar, +use formality_types::grammar::{ + AdtId, AliasTy, Binder, BoundVar, ParameterKind, Predicate, Relation, TraitId, Ty, Wc, Wcs, PR, }; impl Program { diff --git a/crates/formality-rust/src/test.rs b/crates/formality-rust/src/test.rs index eeb90a7f..e6960df8 100644 --- a/crates/formality-rust/src/test.rs +++ b/crates/formality-rust/src/test.rs @@ -11,7 +11,7 @@ fn test_parse_rust_like_trait_impl_syntax() { let r: Program = term( "[ crate core { - impl PartialEq for B where [] { + impl PartialEq for B { } } @@ -20,7 +20,7 @@ fn test_parse_rust_like_trait_impl_syntax() { // Note: the for etc are correctly accounted. expect_test::expect![[r#" - [crate core { impl PartialEq < ^ty0_0 > for ^ty0_1 where [] { } }] + [crate core { impl PartialEq < ^ty0_0 > for ^ty0_1 { } }] "#]] .assert_debug_eq(&r); } @@ -30,7 +30,7 @@ fn test_parse_rust_like_trait_syntax() { let r: Program = term( "[ crate core { - trait Foo where [A : Bar] { + trait Foo where A : Bar { } } @@ -39,7 +39,7 @@ fn test_parse_rust_like_trait_syntax() { // Note: two type parameters, and the 0th one is self: expect_test::expect![[r#" - [crate core { trait Foo where [^ty0_1 : Bar < ^ty0_0 >] { } }] + [crate core { trait Foo where ^ty0_1 : Bar < ^ty0_0 > { } }] "#]] .assert_debug_eq(&r); } @@ -49,7 +49,7 @@ fn test_parse_rust_like_struct_syntax() { let r: Program = term( "[ crate core { - struct Foo where [] { + struct Foo { a : A, } } @@ -58,7 +58,7 @@ fn test_parse_rust_like_struct_syntax() { // Note: two type parameters, and the 0th one is self: expect_test::expect![[r#" - [crate core { struct Foo where [] { a : ^ty0_0 } }] + [crate core { struct Foo { a : ^ty0_0 } }] "#]] .assert_debug_eq(&r); } diff --git a/crates/formality-rust/src/trait_binder.rs b/crates/formality-rust/src/trait_binder.rs index bd947f7f..8647a10d 100644 --- a/crates/formality-rust/src/trait_binder.rs +++ b/crates/formality-rust/src/trait_binder.rs @@ -79,6 +79,7 @@ where T: Term, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // In Debug output, we include the `Self` to avoid confusion -- is this good? write!(f, "{:?}", self.explicit_binder) } } @@ -90,9 +91,17 @@ where #[tracing::instrument(level = "trace", ret)] fn parse<'t>(scope: &Scope, text: &'t str) -> ParseResult<'t, Self> { Parser::single_variant(scope, text, "TraitBinder", |p| { - p.expect_char('<')?; - let mut bindings: Vec> = p.comma_nonterminal()?; - p.expect_char('>')?; + let mut bindings = match p.expect_char('<') { + Ok(()) => { + let bindings: Vec> = p.comma_nonterminal()?; + p.expect_char('>')?; + bindings + } + Err(_) => { + // If we don't see a `<`, assume there are no add'l bound variables. + vec![] + } + }; // insert the `Self` binding at position 0 let bound_var = BoundVar::fresh(ParameterKind::Ty); diff --git a/crates/formality-types/src/grammar.rs b/crates/formality-types/src/grammar.rs index 285576f6..34df24e3 100644 --- a/crates/formality-types/src/grammar.rs +++ b/crates/formality-types/src/grammar.rs @@ -5,10 +5,7 @@ mod kinded; mod ty; mod wc; -pub use crate::rust::{ - Binder, BoundVar, DebruijnIndex, ExistentialVar, Substitution, UniversalVar, VarIndex, - VarSubstitution, Variable, -}; +pub use crate::rust::grammar::*; pub use consts::*; pub use formulas::*; pub use ids::*; diff --git a/crates/formality-types/src/grammar/consts.rs b/crates/formality-types/src/grammar/consts.rs index 496ea76f..ca9bb73f 100644 --- a/crates/formality-types/src/grammar/consts.rs +++ b/crates/formality-types/src/grammar/consts.rs @@ -46,7 +46,6 @@ pub enum ConstData { Value(ValTree, Ty), #[variable] - #[precedence(1)] Variable(Variable), } diff --git a/crates/formality-types/src/grammar/ty.rs b/crates/formality-types/src/grammar/ty.rs index 0d4580d3..e04cfaaa 100644 --- a/crates/formality-types/src/grammar/ty.rs +++ b/crates/formality-types/src/grammar/ty.rs @@ -4,11 +4,11 @@ use std::sync::Arc; mod debug_impls; mod parse_impls; mod term_impls; -use crate::rust::{BoundVar, Variable}; use formality_core::{DowncastTo, To, Upcast, UpcastFrom}; use super::{ - consts::Const, AdtId, AssociatedItemId, Binder, ExistentialVar, FnId, TraitId, UniversalVar, + consts::Const, AdtId, AssociatedItemId, Binder, BoundVar, ExistentialVar, FnId, TraitId, + UniversalVar, Variable, }; #[term] @@ -107,7 +107,6 @@ pub enum TyData { #[cast] PredicateTy(PredicateTy), #[variable] - #[precedence(1)] Variable(Variable), } diff --git a/crates/formality-types/src/grammar/ty/parse_impls.rs b/crates/formality-types/src/grammar/ty/parse_impls.rs index 4765ac1a..4c593196 100644 --- a/crates/formality-types/src/grammar/ty/parse_impls.rs +++ b/crates/formality-types/src/grammar/ty/parse_impls.rs @@ -1,6 +1,8 @@ //! Handwritten parser impls. -use formality_core::parse::{ActiveVariant, CoreParse, ParseError, ParseResult, Parser, Scope}; +use formality_core::parse::{ + ActiveVariant, CoreParse, ParseError, ParseResult, Parser, Precedence, Scope, +}; use formality_core::Upcast; use formality_core::{seq, Set}; @@ -16,94 +18,93 @@ use crate::rust::FormalityLang as Rust; // Implement custom parsing for rigid types. impl CoreParse for RigidTy { fn parse<'t>(scope: &Scope, text: &'t str) -> ParseResult<'t, Self> { - let mut parser: Parser<'_, '_, RigidTy, Rust> = Parser::new(scope, text, "AliasTy"); - - // Parse a `ScalarId` (and upcast it to `RigidTy`) with the highest - // precedence. If someone writes `u8`, we always interpret it as a - // scalar-id. - parser.parse_variant_cast::(1); - - // Parse something like `Id<...>` as an ADT. - parser.parse_variant("Adt", 0, |p| { - let name: AdtId = p.nonterminal()?; - let parameters: Vec = parse_parameters(p)?; - Ok(RigidTy { - name: name.upcast(), - parameters, - }) - }); - - // Parse `&` - parser.parse_variant("Ref", 0, |p| { - p.expect_char('&')?; - let lt: Lt = p.nonterminal()?; - let ty: Ty = p.nonterminal()?; - Ok(RigidTy { - name: RigidName::Ref(RefKind::Shared), - parameters: seq![lt.upcast(), ty.upcast()], - } - .upcast()) - }); - - parser.parse_variant("RefMut", 0, |p| { - p.expect_char('&')?; - p.expect_keyword("mut")?; - let lt: Lt = p.nonterminal()?; - let ty: Ty = p.nonterminal()?; - Ok(RigidTy { - name: RigidName::Ref(RefKind::Mut), - parameters: seq![lt.upcast(), ty.upcast()], - }) - }); - - parser.parse_variant("Tuple", 0, |p| { - p.expect_char('(')?; - p.reject_custom_keywords(&["alias", "rigid", "predicate"])?; - let types: Vec = p.comma_nonterminal()?; - p.expect_char(')')?; - let name = RigidName::Tuple(types.len()); - Ok(RigidTy { - name, - parameters: types.upcast(), - }) - }); - - parser.finish() + Parser::multi_variant(scope, text, "RigidTy", |parser| { + // Parse a `ScalarId` (and upcast it to `RigidTy`) with the highest + // precedence. If someone writes `u8`, we always interpret it as a + // scalar-id. + parser.parse_variant_cast::(Precedence::default()); + + // Parse something like `Id<...>` as an ADT. + parser.parse_variant("Adt", Precedence::default(), |p| { + // Don't accept scalar-ids as Adt names. + p.reject_nonterminal::()?; + + let name: AdtId = p.nonterminal()?; + let parameters: Vec = parse_parameters(p)?; + Ok(RigidTy { + name: name.upcast(), + parameters, + }) + }); + + // Parse `&` + parser.parse_variant("Ref", Precedence::default(), |p| { + p.expect_char('&')?; + let lt: Lt = p.nonterminal()?; + let ty: Ty = p.nonterminal()?; + Ok(RigidTy { + name: RigidName::Ref(RefKind::Shared), + parameters: seq![lt.upcast(), ty.upcast()], + } + .upcast()) + }); + + parser.parse_variant("RefMut", Precedence::default(), |p| { + p.expect_char('&')?; + p.expect_keyword("mut")?; + let lt: Lt = p.nonterminal()?; + let ty: Ty = p.nonterminal()?; + Ok(RigidTy { + name: RigidName::Ref(RefKind::Mut), + parameters: seq![lt.upcast(), ty.upcast()], + }) + }); + + parser.parse_variant("Tuple", Precedence::default(), |p| { + p.expect_char('(')?; + p.reject_custom_keywords(&["alias", "rigid", "predicate"])?; + let types: Vec = p.comma_nonterminal()?; + p.expect_char(')')?; + let name = RigidName::Tuple(types.len()); + Ok(RigidTy { + name, + parameters: types.upcast(), + }) + }); + }) } } // ANCHOR_END: RigidTy_impl impl CoreParse for AliasTy { fn parse<'t>(scope: &Scope, text: &'t str) -> ParseResult<'t, Self> { - let mut parser: Parser<'_, '_, AliasTy, Rust> = Parser::new(scope, text, "AliasTy"); - - parser.parse_variant("associated type", 0, |p| { - p.expect_char('<')?; - let ty0: Ty = p.nonterminal()?; - let () = p.expect_keyword("as")?; - let trait_id: TraitId = p.nonterminal()?; - let trait_parameters1 = parse_parameters(p)?; - p.expect_char('>')?; - p.expect_char(':')?; - p.expect_char(':')?; - let item_id: AssociatedItemId = p.nonterminal()?; - let item_parameters = parse_parameters(p)?; - let name = AssociatedTyName { - trait_id, - item_id, - item_arity: item_parameters.len(), - }; - let parameters: Vec = std::iter::once(ty0.upcast()) - .chain(trait_parameters1) - .chain(item_parameters) - .collect(); - Ok(AliasTy { - name: name.upcast(), - parameters, - }) - }); - - parser.finish() + Parser::multi_variant(scope, text, "AliasTy", |parser| { + parser.parse_variant("associated type", Precedence::default(), |p| { + p.expect_char('<')?; + let ty0: Ty = p.nonterminal()?; + let () = p.expect_keyword("as")?; + let trait_id: TraitId = p.nonterminal()?; + let trait_parameters1 = parse_parameters(p)?; + p.expect_char('>')?; + p.expect_char(':')?; + p.expect_char(':')?; + let item_id: AssociatedItemId = p.nonterminal()?; + let item_parameters = parse_parameters(p)?; + let name = AssociatedTyName { + trait_id, + item_id, + item_arity: item_parameters.len(), + }; + let parameters: Vec = std::iter::once(ty0.upcast()) + .chain(trait_parameters1) + .chain(item_parameters) + .collect(); + Ok(AliasTy { + name: name.upcast(), + parameters, + }) + }); + }) } } @@ -122,19 +123,17 @@ fn parse_parameters<'t>( // writing tests so much more pleasant. impl CoreParse for ConstData { fn parse<'t>(scope: &Scope, text: &'t str) -> ParseResult<'t, Self> { - let mut parser: Parser<'_, '_, ConstData, Rust> = Parser::new(scope, text, "Ty"); - - parser.parse_variant("Variable", 1, |p| p.variable()); - - parser.parse_variant_cast::(1); - - parser.parse_variant("Int", 0, |p| { - let n: u128 = p.number()?; - p.expect_char('_')?; - let ty: Ty = p.nonterminal()?; - Ok(ConstData::Value(Scalar::new(n).upcast(), ty)) - }); - - parser.finish() + Parser::multi_variant(scope, text, "ConstData", |parser| { + parser.parse_variant("Variable", Precedence::default(), |p| p.variable()); + + parser.parse_variant_cast::(Precedence::default()); + + parser.parse_variant("Int", Precedence::default(), |p| { + let n: u128 = p.number()?; + p.expect_char('_')?; + let ty: Ty = p.nonterminal()?; + Ok(ConstData::Value(Scalar::new(n).upcast(), ty)) + }); + }) } } diff --git a/crates/formality-types/src/grammar/wc.rs b/crates/formality-types/src/grammar/wc.rs index a3231c00..b3dae1b5 100644 --- a/crates/formality-types/src/grammar/wc.rs +++ b/crates/formality-types/src/grammar/wc.rs @@ -9,6 +9,7 @@ use crate::grammar::PR; use super::{Binder, BoundVar, Parameter, Predicate, Relation, TraitRef}; #[term($set)] +#[derive(Default)] pub struct Wcs { set: Set, } @@ -93,12 +94,6 @@ impl DowncastTo<(Wc, Wcs)> for Wcs { } } -impl UpcastFrom<()> for Wcs { - fn upcast_from((): ()) -> Self { - Wcs::t() - } -} - impl DowncastTo<()> for Wcs { fn downcast_to(&self) -> Option<()> { if self.set.is_empty() { diff --git a/crates/formality-types/src/lib.rs b/crates/formality-types/src/lib.rs index 6773d975..89adb25c 100644 --- a/crates/formality-types/src/lib.rs +++ b/crates/formality-types/src/lib.rs @@ -16,8 +16,9 @@ formality_core::declare_language! { "enum", "union", "const", - "ty", - "lt", + "true", + "false", + "static", ]; } } diff --git a/examples/formality-eg/grammar.rs b/examples/formality-eg/grammar.rs new file mode 100644 index 00000000..a6dc25fc --- /dev/null +++ b/examples/formality-eg/grammar.rs @@ -0,0 +1,131 @@ +use formality_core::{cast_impl, language::HasKind, term}; +use std::sync::Arc; + +pub use crate::eg::grammar::*; + +#[cfg(test)] +mod test; + +#[term] +#[derive(Copy)] +pub enum Kind { + #[grammar(type)] + Ty, +} + +#[term] +pub enum Parameter { + #[cast] + Ty(Ty), +} + +#[term] +pub enum Ty { + Integer, + + #[cast] + StructTy(StructTy), + + #[variable] + Var(Variable), +} + +#[term($id $)] +pub struct StructTy { + pub id: StructId, + pub parameters: Vec, +} + +#[term(struct $id $bound)] +pub struct StructDecl { + id: StructId, + bound: Binder, +} + +#[term({ $,fields })] +pub struct StructBoundData { + fields: Vec, +} + +#[term($name : $ty)] +pub struct FieldDecl { + name: FieldId, + ty: Ty, +} + +#[term(fn $id $bound)] +pub struct FnDecl { + id: FnId, + bound: Binder, +} + +#[term($(fn_parameters) -> $return_ty { $body })] +pub struct FnBoundData { + pub fn_parameters: Vec, + pub return_ty: Ty, + pub body: Expr, +} + +#[term($id : $ty)] +pub struct LocalVariableDecl { + pub id: LocalVarId, + pub ty: Ty, +} + +#[term] +pub enum Expr { + #[cast] + LocalVar(LocalVarId), + + #[cast] + IntegerLiteral(usize), + + #[grammar($v0 { $,v1 })] + StructLiteral(StructTy, Vec), + + #[grammar($v0 + $v1)] + #[precedence(1)] + Add(Arc, Arc), + + #[grammar($v0 - $v1)] + #[precedence(1)] + Sub(Arc, Arc), + + #[grammar($v0 * $v1)] + #[precedence(2)] + Mul(Arc, Arc), + + #[grammar($v0 / $v1)] + #[precedence(2)] + Div(Arc, Arc), + + #[grammar(($v0))] + Paren(Arc), + + #[grammar(let $v0 = $v1 in $v2)] + LetIn(LocalVarId, Arc, Arc), +} + +#[term($id : $expr)] +pub struct FieldExpr { + pub id: FieldId, + pub expr: Expr, +} + +formality_core::id!(StructId); +formality_core::id!(FieldId); +formality_core::id!(FnId); +formality_core::id!(LocalVarId); + +cast_impl!((Variable) <: (Ty) <: (Parameter)); +cast_impl!((BoundVar) <: (Variable) <: (Parameter)); +cast_impl!((ExistentialVar) <: (Variable) <: (Parameter)); +cast_impl!((UniversalVar) <: (Variable) <: (Parameter)); + +impl HasKind for Parameter { + fn kind(&self) -> Kind { + match self { + Parameter::Ty(_) => Kind::Ty, + } + } +} diff --git a/examples/formality-eg/grammar/test.rs b/examples/formality-eg/grammar/test.rs new file mode 100644 index 00000000..919fa4f5 --- /dev/null +++ b/examples/formality-eg/grammar/test.rs @@ -0,0 +1,84 @@ +use formality_core::test; + +use crate::eg::term; + +use super::{Expr, StructDecl, Ty}; + +#[test] +fn test_struct_decl() { + let r: StructDecl = term("struct Point { x: integer, y: integer }"); + expect_test::expect![[r#" + StructDecl { + id: Point, + bound: { x : integer, y : integer }, + } + "#]] + .assert_debug_eq(&r); +} + +#[test] +fn test_struct_ty_empty_args() { + let r: Ty = term("Point"); + expect_test::expect![[r#" + StructTy( + StructTy { + id: Point, + parameters: [], + }, + ) + "#]] + .assert_debug_eq(&r); +} + +#[test] +fn test_struct_ty_no_args() { + let r: Ty = term("Point"); + expect_test::expect![[r#" + StructTy( + StructTy { + id: Point, + parameters: [], + }, + ) + "#]] + .assert_debug_eq(&r); +} + +#[test] +fn test_vec_int_ty() { + let r: Ty = term("Vec"); + expect_test::expect![[r#" + StructTy( + StructTy { + id: Vec, + parameters: [ + Ty( + Integer, + ), + ], + }, + ) + "#]] + .assert_debug_eq(&r); +} + +#[test] +fn test_expression() { + let r: Expr = term("3 + 5 * 6"); + expect_test::expect![[r#" + Add( + IntegerLiteral( + 3, + ), + Mul( + IntegerLiteral( + 5, + ), + IntegerLiteral( + 6, + ), + ), + ) + "#]] + .assert_debug_eq(&r); +} diff --git a/examples/formality-eg/main.rs b/examples/formality-eg/main.rs new file mode 100644 index 00000000..012159f6 --- /dev/null +++ b/examples/formality-eg/main.rs @@ -0,0 +1,27 @@ +mod grammar; +mod type_system; + +formality_core::declare_language! { + mod eg { + const NAME = "Eg"; + type Kind = crate::grammar::Kind; + type Parameter = crate::grammar::Parameter; + const BINDING_OPEN = '<'; + const BINDING_CLOSE = '>'; + const KEYWORDS = [ + "struct", + "fn", + "let", + "in", + "integer", + ]; + } +} + +// Default language for our crate +use eg::FormalityLang; +use formality_core::Fallible; + +fn main() -> Fallible<()> { + Ok(()) +} diff --git a/examples/formality-eg/type_system.rs b/examples/formality-eg/type_system.rs new file mode 100644 index 00000000..e69de29b diff --git a/fixme_tests/basic--impl_Debug_for_i32.rs b/fixme_tests/basic--impl_Debug_for_i32.rs index 4bf328fa..88bb64af 100644 --- a/fixme_tests/basic--impl_Debug_for_i32.rs +++ b/fixme_tests/basic--impl_Debug_for_i32.rs @@ -3,9 +3,9 @@ const PROGRAM: &str = "[ crate core { - trait Debug<> where [] { } + trait Debug { } - impl<> Debug<> for i32 where [] { } + impl Debug for i32 { } } ]"; @@ -19,7 +19,7 @@ fn test_i32() { "#]] .assert_debug_eq(&formality_rust::test_can_prove_where_clause( PROGRAM, - "i32: Debug<>", + "i32: Debug", )); } @@ -33,6 +33,6 @@ fn test_u32() { "#]] .assert_debug_eq(&formality_rust::test_can_prove_where_clause( PROGRAM, - "u32: Debug<>", + "u32: Debug", )); } diff --git a/fixme_tests/basic--supertraits-hr.rs b/fixme_tests/basic--supertraits-hr.rs index 6c9c74b0..b5f014a1 100644 --- a/fixme_tests/basic--supertraits-hr.rs +++ b/fixme_tests/basic--supertraits-hr.rs @@ -3,8 +3,8 @@ const PROGRAM: &str = "[ crate core { - trait Sub<> where [for Self: Super] { } - trait Super where [] { } + trait Sub where for Self: Super { } + trait Super { } } ]"; diff --git a/fixme_tests/basic--supertraits.rs b/fixme_tests/basic--supertraits.rs index b64c60e6..bfa82f2c 100644 --- a/fixme_tests/basic--supertraits.rs +++ b/fixme_tests/basic--supertraits.rs @@ -3,12 +3,12 @@ const PROGRAM: &str = "[ crate core { - trait Eq<> where [Self: PartialEq<>] { } - trait PartialEq<> where [] { } + trait Eq where Self: PartialEq { } + trait PartialEq { } // ComparableBase is a supertype, but `T: Eq` is not. - trait Comparable where [T: Eq<>, Self: ComparableBase<>] { } - trait ComparableBase<> where [] { } + trait Comparable where T: Eq, Self: ComparableBase { } + trait ComparableBase { } } ]"; diff --git a/fixme_tests/coinductive.rs b/fixme_tests/coinductive.rs index 4baff7f4..60887ffb 100644 --- a/fixme_tests/coinductive.rs +++ b/fixme_tests/coinductive.rs @@ -6,11 +6,11 @@ fn magic_copy() { const PROGRAM: &str = "[ crate core { - struct Foo<> where [] {} - trait Copy<> where [] {} - trait Magic<> where [Self: Copy<>] {} + struct Foo {} + trait Copy {} + trait Magic where Self: Copy {} - impl Magic<> for T where [T: Magic<>] {} + impl Magic for T where T: Magic {} } ]"; @@ -28,7 +28,7 @@ fn magic_copy() { "#]] .assert_debug_eq(&formality_rust::test_can_prove_where_clause( PROGRAM, - "Foo: Magic<>", + "Foo: Magic", )); } @@ -37,13 +37,13 @@ fn magic_copy() { fn magic_copy_impl_for_all_copy() { const PROGRAM: &str = "[ crate core { - struct Foo<> where [] {} - struct Vec where [] {} + struct Foo {} + struct Vec {} - trait Copy<> where [] {} - trait Magic<> where [Self: Copy<>] {} + trait Copy {} + trait Magic where Self: Copy {} - impl Magic<> for T where [T: Copy<>] {} + impl Magic for T where T: Copy {} } ]"; @@ -82,17 +82,17 @@ fn magic_copy_impl_for_all_copy() { fn magic_vec_t() { const PROGRAM: &str = "[ crate core { - struct Foo<> where [] {} - struct Vec where [] {} + struct Foo {} + struct Vec {} - trait Copy<> where [] {} - trait Magic<> where [Self: Copy<>] {} + trait Copy {} + trait Magic where Self: Copy {} - impl Magic<> for Vec where [T: Magic<>] { + impl Magic for Vec where T: Magic { // FIXME: We need to test that this impl can prove T: Copy, // but how to do it? } - impl Copy<> for Vec where [T: Magic<>] {} + impl Copy for Vec where T: Magic {} } ]"; @@ -110,12 +110,12 @@ fn magic_vec_t() { // formality-rust // [(Rust/Program -// (term ([(crate TheCrate { (struct Foo[] where [] { (counter : i32) }) -// (struct Bar[] where [] { (counter : i32) }) +// (term ([(crate TheCrate { (struct Foo[] { (counter : i32) }) +// (struct Bar[] { (counter : i32) }) // (trait Magic[] where [(Self : Copy[])] {}) -// (trait Copy[] where [] {}) +// (trait Copy[] {}) // (impl[(type T)] Magic[] for T where [(T : Magic[])] {}) -// (impl[] Copy[] for (Bar < >) where [] {}) +// (impl[] Copy[] for (Bar < >) {}) // })] // TheCrate))) // ] @@ -158,12 +158,12 @@ fn magic_vec_t() { // redex-let* // formality-rust -// [(Rust/Program (term ([(crate C { (struct Foo[] where [] { (counter : i32) }) -// (struct Bar[] where [] { (counter : i32) }) +// [(Rust/Program (term ([(crate C { (struct Foo[] { (counter : i32) }) +// (struct Bar[] { (counter : i32) }) // (trait Magic[] where [(Self : Copy[])] {}) // (trait Copy[] where [(Self : Magic[])] {}) // (impl[(type T)] Magic[] for T where [(T : Magic[])] {}) -// (impl[] Copy[] for (Bar < >) where [] {}) +// (impl[] Copy[] for (Bar < >) {}) // })] C)))] // (; All decls in crate are considered 'ok'. @@ -209,13 +209,13 @@ fn magic_vec_t() { // (redex-let* // formality-rust -// [([Rust/CrateItemDecl_core ...] (term [(struct Foo[] where [] {}) -// (trait Copy[] where [] {}) -// (trait Partial[] where [(Self : Copy())] {}) -// (trait Complete[] where [(Self : Partial())] {}) -// (impl[(type T)] Partial[] for T where [(T : Complete())] {})])) +// [([Rust/CrateItemDecl_core ...] (term [(struct Foo[] {}) +// (trait Copy[] {}) +// (trait Partial[] where (Self : Copy()) {}) +// (trait Complete[] where (Self : Partial()) {}) +// (impl[(type T)] Partial[] for T where (T : Complete()) {})])) // (Rust/Program_A (term ([(crate A { Rust/CrateItemDecl_core ... -// (impl[(type T)] Complete[] for T where [] {}) +// (impl[(type T)] Complete[] for T {}) // })] // A))) // (Rust/Program_B (term ([(crate B { Rust/CrateItemDecl_core ... diff --git a/fixme_tests/impl-vs-trait-fn.rs b/fixme_tests/impl-vs-trait-fn.rs index 736cee9a..ffa48d6e 100644 --- a/fixme_tests/impl-vs-trait-fn.rs +++ b/fixme_tests/impl-vs-trait-fn.rs @@ -6,15 +6,15 @@ fn absolutely_same() { const PROGRAM: &str = "[ crate core { - trait Debug<> where [] {} - trait Display<> where [] {} + trait Debug {} + trait Display {} - trait Get<> where [] { - fn get(&mut l T) -> () where [T: Debug<>]; + trait Get { + fn get(&mut l T) -> () where [T: Debug]; } - impl<> Get<> for () where [] { - fn get(&mut l T) -> () where [T: Debug<>] { + impl Get for () { + fn get(&mut l T) -> () where T: Debug { trusted } } @@ -34,16 +34,16 @@ fn absolutely_same() { fn different_self_type_mut_vs_sh() { const PROGRAM: &str = "[ crate core { - trait Debug<> where [] {} - trait Display<> where [] {} + trait Debug {} + trait Display {} - trait Get<> where [] { - fn get(&mut l T) -> () where [T: Debug<>]; + trait Get { + fn get(&mut l T) -> () where [T: Debug]; // -------- } - impl<> Get<> for () where [] { - fn get(&l T) -> () where [T: Debug<>] { + impl Get for () { + fn get(&l T) -> () where T: Debug { // ---- trusted } @@ -54,7 +54,7 @@ fn different_self_type_mut_vs_sh() { expect_test::expect![[r#" Err( Error { - context: "check_trait_impl(impl <> Get((rigid tuple(0))) where [] { fn get [(rigid &(shared) ^lt0_1 ^ty0_0)] -> (rigid tuple(0)) where [well_formed((rigid &(shared) ^lt0_1 ^ty0_0)), well_formed((rigid tuple(0))), is_implemented(Debug(^ty0_0))] })", + context: "check_trait_impl(impl <> Get((rigid tuple(0))) { fn get [(rigid &(shared) ^lt0_1 ^ty0_0)] -> (rigid tuple(0)) where [well_formed((rigid &(shared) ^lt0_1 ^ty0_0)), well_formed((rigid tuple(0))), is_implemented(Debug(^ty0_0))] })", source: Error { context: "check_fn_in_impl", source: "could not prove `sub((rigid &(mut) !ltU(2)_1 !tyU(2)_0), (rigid &(shared) !ltU(2)_1 !tyU(2)_0))` given `[\n well_formed((rigid &(shared) !ltU(2)_1 !tyU(2)_0)),\n well_formed((rigid tuple(0))),\n is_implemented(Debug(!tyU(2)_0)),\n]`", @@ -70,16 +70,16 @@ fn different_self_type_mut_vs_sh() { fn different_arg_type_u32_vs_i32() { const PROGRAM: &str = "[ crate core { - trait Debug<> where [] {} - trait Display<> where [] {} + trait Debug {} + trait Display {} - trait Get<> where [] { - fn get(&mut l T, u32) -> () where [T: Debug<>]; + trait Get { + fn get(&mut l T, u32) -> () where [T: Debug]; // --- } - impl<> Get<> for () where [] { - fn get(&mut l T, i32) -> () where [T: Debug<>] { + impl Get for () { + fn get(&mut l T, i32) -> () where T: Debug { // --- trusted } @@ -90,7 +90,7 @@ fn different_arg_type_u32_vs_i32() { expect_test::expect![[r#" Err( Error { - context: "check_trait_impl(impl <> Get((rigid tuple(0))) where [] { fn get [(rigid &(mut) ^lt0_1 ^ty0_0), (rigid (scalar i32))] -> (rigid tuple(0)) where [well_formed((rigid &(mut) ^lt0_1 ^ty0_0)), well_formed((rigid (scalar i32))), well_formed((rigid tuple(0))), is_implemented(Debug(^ty0_0))] })", + context: "check_trait_impl(impl <> Get((rigid tuple(0))) { fn get [(rigid &(mut) ^lt0_1 ^ty0_0), (rigid (scalar i32))] -> (rigid tuple(0)) where [well_formed((rigid &(mut) ^lt0_1 ^ty0_0)), well_formed((rigid (scalar i32))), well_formed((rigid tuple(0))), is_implemented(Debug(^ty0_0))] })", source: Error { context: "check_fn_in_impl", source: "could not prove `sub((rigid (scalar u32)), (rigid (scalar i32)))` given `[\n well_formed((rigid &(mut) !ltU(2)_1 !tyU(2)_0)),\n well_formed((rigid (scalar i32))),\n well_formed((rigid tuple(0))),\n is_implemented(Debug(!tyU(2)_0)),\n]`", diff --git a/fixme_tests/wf-impl--supertrait-required.rs b/fixme_tests/wf-impl--supertrait-required.rs index d4db92fc..48d70a7a 100644 --- a/fixme_tests/wf-impl--supertrait-required.rs +++ b/fixme_tests/wf-impl--supertrait-required.rs @@ -6,16 +6,16 @@ fn test_one_impl() { const PROGRAM: &str = "[ crate core { - trait Eq<> where [Self: PartialEq<>] { } - trait PartialEq<> where [] { } - impl<> Eq<> for u32 where [] { } + trait Eq where Self: PartialEq { } + trait PartialEq { } + impl Eq for u32 { } } ]"; expect_test::expect![[r#" Err( Error { - context: "check_trait_impl(impl <> Eq((rigid (scalar u32))) where [] { })", + context: "check_trait_impl(impl <> Eq((rigid (scalar u32))) { })", source: "could not prove `is_implemented(Eq((rigid (scalar u32))))` given `[]`", }, ) @@ -28,10 +28,10 @@ fn test_one_impl() { fn test_both_impls() { const PROGRAM: &str = "[ crate core { - trait Eq<> where [Self: PartialEq<>] { } - trait PartialEq<> where [] { } - impl<> Eq<> for u32 where [] { } - impl<> PartialEq<> for u32 where [] { } + trait Eq where Self: PartialEq { } + trait PartialEq { } + impl Eq for u32 { } + impl PartialEq for u32 { } } ]"; diff --git a/tests/associated_type_normalization.rs b/tests/associated_type_normalization.rs index 95c95721..e7a18c2f 100644 --- a/tests/associated_type_normalization.rs +++ b/tests/associated_type_normalization.rs @@ -3,12 +3,12 @@ use formality_core::test; const MIRROR: &str = "[ crate core { - trait Mirror<> where [] { - type Assoc<> : [] where []; + trait Mirror { + type Assoc : []; } - impl Mirror<> for T where [] { - type Assoc<> = T where []; + impl Mirror for T { + type Assoc = T; } } ]"; @@ -47,6 +47,6 @@ fn test_mirror_normalizes_u32_to_u32() { "#]] .assert_debug_eq(&test_where_clause( MIRROR, - "exists {} => {::Assoc<> = T}", + "exists {} => {::Assoc = T}", )); } diff --git a/tests/coherence_overlap.rs b/tests/coherence_overlap.rs index aeb8f1bf..720ef2f0 100644 --- a/tests/coherence_overlap.rs +++ b/tests/coherence_overlap.rs @@ -11,24 +11,24 @@ fn test_overlap_normalize_alias_to_LocalType() { let gen_program = |addl: &str| { const BASE_PROGRAM: &str = "[ crate core { - trait Iterator<> where [] { + trait Iterator { } - trait Mirror<> where [] { - type T<> : [] where []; + trait Mirror { + type T : []; } - impl Mirror<> for A where [] { - type T<> = A where []; + impl Mirror for A { + type T = A; } - struct LocalType<> where [] {} + struct LocalType {} - trait LocalTrait<> where [] { } + trait LocalTrait { } - impl LocalTrait<> for T where [T: Iterator<>] { } + impl LocalTrait for T where T: Iterator { } - impl<> LocalTrait<> for ::T where [] { } + impl LocalTrait for ::T { } ADDITIONAL } @@ -53,11 +53,11 @@ fn test_overlap_normalize_alias_to_LocalType() { expect_test::expect![[r#" Err( - "impls may overlap:\nimpl LocalTrait < > for ^ty0_0 where [^ty0_0 : Iterator < >] { }\nimpl <> LocalTrait < > for ::T where [] { }", + "impls may overlap:\nimpl LocalTrait for ^ty0_0 where ^ty0_0 : Iterator { }\nimpl LocalTrait for ::T { }", ) "#]] .assert_debug_eq(&test_program_ok(&gen_program( - "impl<> Iterator<> for LocalType<> where [] {}", + "impl Iterator for LocalType {}", ))); } @@ -69,24 +69,24 @@ fn test_overlap_alias_not_normalizable() { let gen_program = |addl: &str| { const BASE_PROGRAM: &str = "[ crate core { - trait Iterator<> where [] { + trait Iterator { } - trait Mirror<> where [] { - type T<> : [] where []; + trait Mirror { + type T : []; } - impl Mirror<> for A where [] { - type T<> = A where []; + impl Mirror for A { + type T = A; } - struct LocalType<> where [] {} + struct LocalType {} - trait LocalTrait<> where [] { } + trait LocalTrait { } - impl LocalTrait<> for T where [T: Iterator<>] { } + impl LocalTrait for T where T: Iterator { } - impl LocalTrait<> for ::T where [T: Mirror<>] { } + impl LocalTrait for ::T where T: Mirror { } ADDITIONAL } @@ -114,10 +114,10 @@ fn test_overlap_alias_not_normalizable() { expect_test::expect![[r#" Err( - "impls may overlap:\nimpl LocalTrait < > for ^ty0_0 where [^ty0_0 : Iterator < >] { }\nimpl LocalTrait < > for <^ty0_0 as Mirror>::T where [^ty0_0 : Mirror < >] { }", + "impls may overlap:\nimpl LocalTrait for ^ty0_0 where ^ty0_0 : Iterator { }\nimpl LocalTrait for <^ty0_0 as Mirror>::T where ^ty0_0 : Mirror { }", ) "#]] // FIXME .assert_debug_eq(&test_program_ok(&gen_program( - "impl<> Iterator<> for u32 where[] {}", + "impl Iterator for u32 {}", ))); } diff --git a/tests/parser-torture-tests/ambiguity.rs b/tests/parser-torture-tests/ambiguity.rs new file mode 100644 index 00000000..3275fb61 --- /dev/null +++ b/tests/parser-torture-tests/ambiguity.rs @@ -0,0 +1,67 @@ +use formality_core::{term, test}; +use std::sync::Arc; + +#[test] +fn reduce_reduce_ok() { + #[term] + pub enum Root { + #[cast] + ClassTy(ClassTy), + #[cast] + Perm(Perm), + } + + #[term($perm $class_id)] + pub struct ClassTy { + perm: Perm, + class_id: Id, + } + + #[term] + pub enum Perm { + My, + Our, + } + + formality_core::id!(Id); + + let term: Root = crate::ptt::term("my String"); + expect_test::expect![[r#" + ClassTy( + ClassTy { + perm: My, + class_id: String, + }, + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +#[should_panic(expected = "ambiguous parse")] +fn reduce_reduce_ambig() { + #[term] + pub enum Root { + #[grammar($v0)] + OneId(Id), + #[grammar($v0 $v1)] + TwoId(Id, Id), + #[grammar($v0 $v1)] + TwoRr(Arc, Arc), + } + + formality_core::id!(Id); + + // This will panic. It could be parsed in multiple ways + // (using a variant of Reverse Polish Notation) and none is obviously + // better than the other: + // + // Root = ((Id Root::OneId) (Id Id Root::TwoId) Root::TwoRr) + // Root = (Id Id Root::TwoId) (Id Root::OneId) Root::TwoRr) + // Root = ((Id Root::OneId) (Id Root::OneId) (Id Root::OneId) Root::TwoRr) + let term: Root = crate::ptt::term("a b c"); + expect_test::expect![[r#" + a b c + "#]] + .assert_debug_eq(&term); +} diff --git a/tests/parser-torture-tests/grammar.rs b/tests/parser-torture-tests/grammar.rs new file mode 100644 index 00000000..a03a8558 --- /dev/null +++ b/tests/parser-torture-tests/grammar.rs @@ -0,0 +1,42 @@ +use crate::ptt::{ + grammar::{BoundVar, ExistentialVar, UniversalVar, Variable}, + FormalityLang, +}; +use formality_core::{language::HasKind, term}; + +// Create a dummy kind/parameter -- we're not using these for the torture +// tests, but we need them. + +#[term] +#[derive(Copy)] +pub enum DummyKind { + Ty, +} + +#[term] +pub enum DummyParameter { + #[cast] + Ty(DummyTy), +} + +#[term] +pub enum DummyTy { + #[variable] + Variable(Variable), +} + +formality_core::cast_impl!((BoundVar) <: (Variable) <: (DummyTy)); +formality_core::cast_impl!((ExistentialVar) <: (Variable) <: (DummyTy)); +formality_core::cast_impl!((UniversalVar) <: (Variable) <: (DummyTy)); +formality_core::cast_impl!((Variable) <: (DummyTy) <: (DummyParameter)); +formality_core::cast_impl!((BoundVar) <: (DummyTy) <: (DummyParameter)); +formality_core::cast_impl!((ExistentialVar) <: (DummyTy) <: (DummyParameter)); +formality_core::cast_impl!((UniversalVar) <: (DummyTy) <: (DummyParameter)); + +impl HasKind for DummyParameter { + fn kind(&self) -> formality_core::language::CoreKind { + match self { + DummyParameter::Ty(_) => DummyKind::Ty, + } + } +} diff --git a/tests/parser-torture-tests/left_associative.rs b/tests/parser-torture-tests/left_associative.rs new file mode 100644 index 00000000..61896962 --- /dev/null +++ b/tests/parser-torture-tests/left_associative.rs @@ -0,0 +1,161 @@ +use formality_core::{term, test}; +use std::sync::Arc; + +// ANCHOR: Expr +#[term] +pub enum Expr { + #[cast] + Id(Id), + + #[grammar($v0 + $v1)] + #[precedence(1)] + Add(Arc, Arc), + + #[grammar($v0 * $v1)] + #[precedence(2)] + Mul(Arc, Arc), +} + +formality_core::id!(Id); +// ANCHOR_END: Expr + +#[test] +fn add_mul() { + let term: Expr = crate::ptt::term("a + b * c"); + expect_test::expect![[r#" + Add( + Id( + a, + ), + Mul( + Id( + b, + ), + Id( + c, + ), + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_add() { + let term: Expr = crate::ptt::term("a * b + c"); + expect_test::expect![[r#" + Add( + Mul( + Id( + a, + ), + Id( + b, + ), + ), + Id( + c, + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn add_add() { + let term: Expr = crate::ptt::term("a + b + c"); + expect_test::expect![[r#" + Add( + Add( + Id( + a, + ), + Id( + b, + ), + ), + Id( + c, + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_mul() { + let term: Expr = crate::ptt::term("a * b * c"); + expect_test::expect![[r#" + Mul( + Mul( + Id( + a, + ), + Id( + b, + ), + ), + Id( + c, + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_mul_mul() { + let term: Expr = crate::ptt::term("a * b * c * d"); + expect_test::expect![[r#" + Mul( + Mul( + Mul( + Id( + a, + ), + Id( + b, + ), + ), + Id( + c, + ), + ), + Id( + d, + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn add_add_mul_add() { + let term: Expr = crate::ptt::term("a + b + c * d + e"); + expect_test::expect![[r#" + Add( + Add( + Add( + Id( + a, + ), + Id( + b, + ), + ), + Mul( + Id( + c, + ), + Id( + d, + ), + ), + ), + Id( + e, + ), + ) + "#]] + .assert_debug_eq(&term); +} diff --git a/tests/parser-torture-tests/main.rs b/tests/parser-torture-tests/main.rs new file mode 100644 index 00000000..c903ef45 --- /dev/null +++ b/tests/parser-torture-tests/main.rs @@ -0,0 +1,49 @@ +mod ambiguity; +mod grammar; +mod left_associative; +mod none_associative; +mod path; +mod right_associative; + +formality_core::declare_language! { + mod ptt { + const NAME = "PTT"; + type Kind = crate::grammar::DummyKind; + type Parameter = crate::grammar::DummyParameter; + const BINDING_OPEN = '<'; + const BINDING_CLOSE = '>'; + const KEYWORDS = [ + "struct", + "fn", + "let", + "in", + "integer", + ]; + } +} + +/// Used to parse `text` when we expect some remainder +fn expect_remainder(text: &str) -> (T, &str) +where + T: CoreParse, +{ + match T::parse(&Default::default(), text) { + Ok(parse) => { + let (value, remainder) = parse.finish(); + assert!( + !remainder.is_empty(), + "expected to have remainder text, but parsed entire term `{text:?}`" + ); + (value, remainder) + } + Err(errs) => panic!("encountered unexpected parse error: {errs:#?}"), + } +} + +// Default language for our crate +use formality_core::{parse::CoreParse, Fallible}; +use ptt::FormalityLang; + +fn main() -> Fallible<()> { + Ok(()) +} diff --git a/tests/parser-torture-tests/none_associative.rs b/tests/parser-torture-tests/none_associative.rs new file mode 100644 index 00000000..3b751e4e --- /dev/null +++ b/tests/parser-torture-tests/none_associative.rs @@ -0,0 +1,138 @@ +use formality_core::{term, test}; +use std::sync::Arc; + +use crate::expect_remainder; + +#[term] +pub enum Expr { + #[cast] + Id(Id), + + #[grammar($v0 + $v1)] + #[precedence(1, none)] + Add(Arc, Arc), + + #[grammar($v0 * $v1)] + #[precedence(2, none)] + Mul(Arc, Arc), +} + +formality_core::id!(Id); + +#[test] +fn add_mul() { + let term: Expr = crate::ptt::term("a + b * c"); + expect_test::expect![[r#" + Add( + Id( + a, + ), + Mul( + Id( + b, + ), + Id( + c, + ), + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_add() { + let term: Expr = crate::ptt::term("a * b + c"); + expect_test::expect![[r#" + Add( + Mul( + Id( + a, + ), + Id( + b, + ), + ), + Id( + c, + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn add_add() { + let term = expect_remainder::("a + b + c"); + expect_test::expect![[r#" + ( + Add( + Id( + a, + ), + Id( + b, + ), + ), + " + c", + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_mul() { + let term = expect_remainder::("a * b * c"); + expect_test::expect![[r#" + ( + Mul( + Id( + a, + ), + Id( + b, + ), + ), + " * c", + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_mul_mul() { + let term = expect_remainder::("a * b * c * d"); + expect_test::expect![[r#" + ( + Mul( + Id( + a, + ), + Id( + b, + ), + ), + " * c * d", + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn add_add_mul_add() { + let term = expect_remainder::("a + b + c * d + e"); + expect_test::expect![[r#" + ( + Add( + Id( + a, + ), + Id( + b, + ), + ), + " + c * d + e", + ) + "#]] + .assert_debug_eq(&term); +} diff --git a/tests/parser-torture-tests/path.rs b/tests/parser-torture-tests/path.rs new file mode 100644 index 00000000..1862f288 --- /dev/null +++ b/tests/parser-torture-tests/path.rs @@ -0,0 +1,43 @@ +use formality_core::{term, test}; +use std::sync::Arc; + +// ANCHOR: path +#[term] +pub enum Path { + #[cast] + Id(Id), + + #[grammar($v0 . $v1)] + Field(Arc, Id), + + #[grammar($v0 [ $v1 ])] + Index(Arc, Arc), +} + +formality_core::id!(Id); +// ANCHOR_END: path + +#[test] +fn path() { + let term: Path = crate::ptt::term("a.b[c.d].e"); + expect_test::expect![[r#" + Field( + Index( + Field( + Id( + a, + ), + b, + ), + Field( + Id( + c, + ), + d, + ), + ), + e, + ) + "#]] + .assert_debug_eq(&term); +} diff --git a/tests/parser-torture-tests/right_associative.rs b/tests/parser-torture-tests/right_associative.rs new file mode 100644 index 00000000..966f3370 --- /dev/null +++ b/tests/parser-torture-tests/right_associative.rs @@ -0,0 +1,159 @@ +use formality_core::{term, test}; +use std::sync::Arc; + +#[term] +pub enum Expr { + #[cast] + Id(Id), + + #[grammar($v0 + $v1)] + #[precedence(1, right)] + Add(Arc, Arc), + + #[grammar($v0 * $v1)] + #[precedence(2, right)] + Mul(Arc, Arc), +} + +formality_core::id!(Id); + +#[test] +fn add_mul() { + let term: Expr = crate::ptt::term("a + b * c"); + expect_test::expect![[r#" + Add( + Id( + a, + ), + Mul( + Id( + b, + ), + Id( + c, + ), + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_add() { + let term: Expr = crate::ptt::term("a * b + c"); + expect_test::expect![[r#" + Add( + Mul( + Id( + a, + ), + Id( + b, + ), + ), + Id( + c, + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn add_add() { + let term: Expr = crate::ptt::term("a + b + c"); + expect_test::expect![[r#" + Add( + Id( + a, + ), + Add( + Id( + b, + ), + Id( + c, + ), + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_mul() { + let term: Expr = crate::ptt::term("a * b * c"); + expect_test::expect![[r#" + Mul( + Id( + a, + ), + Mul( + Id( + b, + ), + Id( + c, + ), + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn mul_mul_mul() { + let term: Expr = crate::ptt::term("a * b * c * d"); + expect_test::expect![[r#" + Mul( + Id( + a, + ), + Mul( + Id( + b, + ), + Mul( + Id( + c, + ), + Id( + d, + ), + ), + ), + ) + "#]] + .assert_debug_eq(&term); +} + +#[test] +fn add_add_mul_add() { + let term: Expr = crate::ptt::term("a + b + c * d + e"); + expect_test::expect![[r#" + Add( + Id( + a, + ), + Add( + Id( + b, + ), + Add( + Mul( + Id( + c, + ), + Id( + d, + ), + ), + Id( + e, + ), + ), + ), + ) + "#]] + .assert_debug_eq(&term); +} diff --git a/tests/projection.rs b/tests/projection.rs index ea3cd005..bf1d6c6e 100644 --- a/tests/projection.rs +++ b/tests/projection.rs @@ -2,16 +2,16 @@ use a_mir_formality::test_where_clause; const NORMALIZE_BASIC: &str = "[ crate test { - trait Iterator<> where [] { - type Item<> : [] where []; + trait Iterator { + type Item : []; } - struct Vec where [] {} + struct Vec {} - struct Foo<> where [] {} + struct Foo {} - impl Iterator<> for Vec where [] { - type Item<> = T where []; + impl Iterator for Vec { + type Item = T; } } ]"; @@ -52,7 +52,7 @@ fn normalize_basic() { "#]] .assert_debug_eq(&test_where_clause( NORMALIZE_BASIC, - "forall exists {} => { as Iterator>::Item<> = U }", + "forall exists {} => { as Iterator>::Item = U }", )); expect_test::expect![[r#" @@ -73,7 +73,7 @@ fn normalize_basic() { "#]] .assert_debug_eq(&test_where_clause( NORMALIZE_BASIC, - "forall {} => { Iterator(Vec), as Iterator<>>::Item<> = T }", + "forall {} => { Iterator(Vec), as Iterator>::Item = T }", )); expect_test::expect![[r#" @@ -94,7 +94,7 @@ fn normalize_basic() { "#]] .assert_debug_eq(&test_where_clause( NORMALIZE_BASIC, - "forall { Iterator(T), >::Item<> = Foo } => { >::Item<> = Foo }", + "forall { Iterator(T), ::Item = Foo } => { ::Item = Foo }", )); expect_test::expect![[r#" @@ -118,7 +118,7 @@ fn normalize_basic() { "#]] .assert_debug_eq(&test_where_clause( NORMALIZE_BASIC, - "forall exists { Iterator(T) } => { >::Item<> = U }", + "forall exists { Iterator(T) } => { ::Item = U }", )); expect_test::expect![[r#" @@ -139,7 +139,7 @@ fn normalize_basic() { "#]] .assert_debug_eq(&test_where_clause( NORMALIZE_BASIC, - "forall { Iterator(T) } => { >::Item<> = >::Item<> }", + "forall { Iterator(T) } => { ::Item = ::Item }", )); expect_test::expect![[r#" @@ -178,30 +178,30 @@ fn normalize_basic() { "#]] .assert_debug_eq(&test_where_clause( NORMALIZE_BASIC, - "forall exists { Iterator(T) } => { >::Item<> = >::Item<> }", + "forall exists { Iterator(T) } => { ::Item = ::Item }", )); } const NORMALIZE_INTO_ITERATOR: &str = "[ crate test { - trait IntoIterator<> where [] { - type Item<> : [] where []; + trait IntoIterator { + type Item : []; } - trait Iterator<> where [] { - type Item<> : [] where []; + trait Iterator { + type Item : []; } - struct Vec where [] {} + struct Vec {} - struct Foo<> where [] {} + struct Foo {} - impl IntoIterator<> for Vec where [] { - type Item<> = T where []; + impl IntoIterator for Vec { + type Item = T; } - impl IntoIterator<> for T where [ T: Iterator<> ] { - type Item<> = ::Item<> where []; + impl IntoIterator for T where T: Iterator { + type Item = ::Item; } } ]"; @@ -242,20 +242,20 @@ fn normalize_into_iterator() { "#]] .assert_debug_eq(&test_where_clause( NORMALIZE_INTO_ITERATOR, - "forall exists {} => { as IntoIterator>::Item<> = U }", + "forall exists {} => { as IntoIterator>::Item = U }", )); } const PROJECTION_EQUALITY: &str = "[ crate test { - trait Trait1<> where [] { - type Type<> : [] where []; + trait Trait1<> { + type Type : []; } - trait Trait2 where [] {} - impl Trait2 for U where [ U: Trait1<>, ::Type => T ] {} - struct S<> where [] {} - impl<> Trait1<> for S<> where [] { - type Type<> = u32 where []; + trait Trait2 {} + impl Trait2 for U where U: Trait1<>, ::Type => T {} + struct S {} + impl Trait1<> for S { + type Type = u32; } } ]"; @@ -294,7 +294,7 @@ fn projection_equality() { "#]] .assert_debug_eq(&test_where_clause( PROJECTION_EQUALITY, - "exists {} => { Trait1(S), >::Type<> = U }", + "exists {} => { Trait1(S), >::Type = U }", )); expect_test::expect![[r#" diff --git a/tests/ui/basic_where_clauses_fail.stderr b/tests/ui/basic_where_clauses_fail.stderr index da29e03a..a55bc4da 100644 --- a/tests/ui/basic_where_clauses_fail.stderr +++ b/tests/ui/basic_where_clauses_fail.stderr @@ -1,6 +1,6 @@ Error: check_trait(WellFormed) Caused by: - 0: prove_where_clause_well_formed(for u32 : A < ^ty0_0 >) - 1: prove_where_clause_well_formed(u32 : A < !ty_2 >) + 0: prove_where_clause_well_formed(for u32 : A <^ty0_0>) + 1: prove_where_clause_well_formed(u32 : A ) 2: failed to prove {@ WellFormedTraitRef(A(u32, !ty_2))} given {for A(u32, ^ty0_0)}, got {} diff --git "a/tests/ui/basic_where_clauses_fail.\360\237\224\254" "b/tests/ui/basic_where_clauses_fail.\360\237\224\254" index 57d080d6..93c779fb 100644 --- "a/tests/ui/basic_where_clauses_fail.\360\237\224\254" +++ "b/tests/ui/basic_where_clauses_fail.\360\237\224\254" @@ -1,9 +1,9 @@ [ crate core { - trait A where [T: B<>] { } + trait A where T: B { } - trait B<> where [] { } + trait B { } - trait WellFormed<> where [for u32: A] { } + trait WellFormed where for u32: A { } } ] diff --git "a/tests/ui/basic_where_clauses_pass.\360\237\224\254" "b/tests/ui/basic_where_clauses_pass.\360\237\224\254" index cc9eb6f9..b30eadcd 100644 --- "a/tests/ui/basic_where_clauses_pass.\360\237\224\254" +++ "b/tests/ui/basic_where_clauses_pass.\360\237\224\254" @@ -1,12 +1,12 @@ //@check-pass [ crate core { - trait A where [T: B<>] { } + trait A where T: B { } - trait B<> where [] { } + trait B { } - trait WellFormed<> where [for u32: A] { } + trait WellFormed where for u32: A { } - impl B<> for T where [] {} + impl B for T {} } ] diff --git a/tests/ui/coherence_orphan/CoreTrait_for_CoreStruct_in_Foo.stderr b/tests/ui/coherence_orphan/CoreTrait_for_CoreStruct_in_Foo.stderr index 3ef3c848..cbb2768a 100644 --- a/tests/ui/coherence_orphan/CoreTrait_for_CoreStruct_in_Foo.stderr +++ b/tests/ui/coherence_orphan/CoreTrait_for_CoreStruct_in_Foo.stderr @@ -1,4 +1,4 @@ -Error: orphan_check(impl <> CoreTrait < > for CoreStruct where [] { }) +Error: orphan_check(impl CoreTrait for CoreStruct { }) Caused by: failed to prove {@ IsLocal(CoreTrait(CoreStruct))} given {}, got {} diff --git "a/tests/ui/coherence_orphan/CoreTrait_for_CoreStruct_in_Foo.\360\237\224\254" "b/tests/ui/coherence_orphan/CoreTrait_for_CoreStruct_in_Foo.\360\237\224\254" index 9f6ce0ff..7700102d 100644 --- "a/tests/ui/coherence_orphan/CoreTrait_for_CoreStruct_in_Foo.\360\237\224\254" +++ "b/tests/ui/coherence_orphan/CoreTrait_for_CoreStruct_in_Foo.\360\237\224\254" @@ -1,9 +1,9 @@ [ crate core { - trait CoreTrait<> where [] {} - struct CoreStruct<> where [] {} + trait CoreTrait {} + struct CoreStruct {} }, crate foo { - impl<> CoreTrait<> for CoreStruct<> where [] {} + impl CoreTrait for CoreStruct {} } ] diff --git a/tests/ui/coherence_orphan/alias_to_unit.stderr b/tests/ui/coherence_orphan/alias_to_unit.stderr index cbb363b8..17fcdc72 100644 --- a/tests/ui/coherence_orphan/alias_to_unit.stderr +++ b/tests/ui/coherence_orphan/alias_to_unit.stderr @@ -1,4 +1,4 @@ -Error: orphan_check(impl <> CoreTrait < > for ::Assoc where [] { }) +Error: orphan_check(impl CoreTrait for ::Assoc { }) Caused by: failed to prove {@ IsLocal(CoreTrait(::Assoc))} given {}, got {} diff --git "a/tests/ui/coherence_orphan/alias_to_unit.\360\237\224\254" "b/tests/ui/coherence_orphan/alias_to_unit.\360\237\224\254" index b08deb0d..31bb5744 100644 --- "a/tests/ui/coherence_orphan/alias_to_unit.\360\237\224\254" +++ "b/tests/ui/coherence_orphan/alias_to_unit.\360\237\224\254" @@ -1,17 +1,17 @@ [ crate core { - trait CoreTrait<> where [] {} + trait CoreTrait {} - trait Unit<> where [] { - type Assoc<> : [] where []; + trait Unit { + type Assoc : []; } - impl Unit<> for T where [] { - type Assoc<> = () where []; + impl Unit for T { + type Assoc = (); } }, crate foo { - struct FooStruct<> where [] {} - impl<> CoreTrait<> for as Unit<>>::Assoc<> where [] {} + struct FooStruct {} + impl CoreTrait for ::Assoc {} } ] diff --git "a/tests/ui/coherence_orphan/covered_VecT.\360\237\224\254" "b/tests/ui/coherence_orphan/covered_VecT.\360\237\224\254" index c30433ac..b054d6d7 100644 --- "a/tests/ui/coherence_orphan/covered_VecT.\360\237\224\254" +++ "b/tests/ui/coherence_orphan/covered_VecT.\360\237\224\254" @@ -1,11 +1,11 @@ //@check-pass [ crate core { - trait CoreTrait where [] {} - struct Vec where [] {} + trait CoreTrait {} + struct Vec {} }, crate foo { - struct FooStruct<> where [] {} - impl CoreTrait> for Vec where [] {} + struct FooStruct {} + impl CoreTrait for Vec {} } ] diff --git a/tests/ui/coherence_orphan/mirror_CoreStruct.stderr b/tests/ui/coherence_orphan/mirror_CoreStruct.stderr index 0450e2d9..1c5f6b4e 100644 --- a/tests/ui/coherence_orphan/mirror_CoreStruct.stderr +++ b/tests/ui/coherence_orphan/mirror_CoreStruct.stderr @@ -1,4 +1,4 @@ -Error: orphan_check(impl <> CoreTrait < > for ::Assoc where [] { }) +Error: orphan_check(impl CoreTrait for ::Assoc { }) Caused by: failed to prove {@ IsLocal(CoreTrait(::Assoc))} given {}, got {} diff --git "a/tests/ui/coherence_orphan/mirror_CoreStruct.\360\237\224\254" "b/tests/ui/coherence_orphan/mirror_CoreStruct.\360\237\224\254" index ed764190..b6ebb5e8 100644 --- "a/tests/ui/coherence_orphan/mirror_CoreStruct.\360\237\224\254" +++ "b/tests/ui/coherence_orphan/mirror_CoreStruct.\360\237\224\254" @@ -1,17 +1,17 @@ [ crate core { - trait CoreTrait<> where [] {} - struct CoreStruct<> where [] {} + trait CoreTrait {} + struct CoreStruct {} - trait Mirror<> where [] { - type Assoc<> : [] where []; + trait Mirror { + type Assoc : []; } - impl Mirror<> for T where [] { - type Assoc<> = T where []; + impl Mirror for T { + type Assoc = T; } }, crate foo { - impl<> CoreTrait<> for as Mirror<>>::Assoc<> where [] {} + impl CoreTrait for ::Assoc {} } ] diff --git "a/tests/ui/coherence_orphan/mirror_FooStruct.\360\237\224\254" "b/tests/ui/coherence_orphan/mirror_FooStruct.\360\237\224\254" index 28a81c00..d92ab188 100644 --- "a/tests/ui/coherence_orphan/mirror_FooStruct.\360\237\224\254" +++ "b/tests/ui/coherence_orphan/mirror_FooStruct.\360\237\224\254" @@ -1,18 +1,18 @@ //@check-pass [ crate core { - trait CoreTrait<> where [] {} + trait CoreTrait {} - trait Mirror<> where [] { - type Assoc<> : [] where []; + trait Mirror { + type Assoc : []; } - impl Mirror<> for T where [] { - type Assoc<> = T where []; + impl Mirror for T { + type Assoc = T; } }, crate foo { - struct FooStruct<> where [] {} - impl<> CoreTrait<> for as Mirror<>>::Assoc<> where [] {} + struct FooStruct {} + impl CoreTrait for ::Assoc {} } ] diff --git a/tests/ui/coherence_orphan/neg_CoreTrait_for_CoreStruct_in_Foo.stderr b/tests/ui/coherence_orphan/neg_CoreTrait_for_CoreStruct_in_Foo.stderr index 081e8613..e5415d69 100644 --- a/tests/ui/coherence_orphan/neg_CoreTrait_for_CoreStruct_in_Foo.stderr +++ b/tests/ui/coherence_orphan/neg_CoreTrait_for_CoreStruct_in_Foo.stderr @@ -1,4 +1,4 @@ -Error: orphan_check_neg(impl <> ! CoreTrait < > for CoreStruct where [] {}) +Error: orphan_check_neg(impl ! CoreTrait for CoreStruct {}) Caused by: failed to prove {@ IsLocal(CoreTrait(CoreStruct))} given {}, got {} diff --git "a/tests/ui/coherence_orphan/neg_CoreTrait_for_CoreStruct_in_Foo.\360\237\224\254" "b/tests/ui/coherence_orphan/neg_CoreTrait_for_CoreStruct_in_Foo.\360\237\224\254" index bfc1e54e..208512f7 100644 --- "a/tests/ui/coherence_orphan/neg_CoreTrait_for_CoreStruct_in_Foo.\360\237\224\254" +++ "b/tests/ui/coherence_orphan/neg_CoreTrait_for_CoreStruct_in_Foo.\360\237\224\254" @@ -1,9 +1,9 @@ [ crate core { - trait CoreTrait<> where [] {} - struct CoreStruct<> where [] {} + trait CoreTrait {} + struct CoreStruct {} }, crate foo { - impl<> !CoreTrait<> for CoreStruct<> where [] {} + impl !CoreTrait for CoreStruct {} } ] diff --git a/tests/ui/coherence_orphan/uncovered_T.stderr b/tests/ui/coherence_orphan/uncovered_T.stderr index 35cfc0fc..faeeb406 100644 --- a/tests/ui/coherence_orphan/uncovered_T.stderr +++ b/tests/ui/coherence_orphan/uncovered_T.stderr @@ -1,4 +1,4 @@ -Error: orphan_check(impl CoreTrait < FooStruct > for ^ty0_0 where [] { }) +Error: orphan_check(impl CoreTrait for ^ty0_0 { }) Caused by: failed to prove {@ IsLocal(CoreTrait(!ty_1, FooStruct))} given {}, got {} diff --git "a/tests/ui/coherence_orphan/uncovered_T.\360\237\224\254" "b/tests/ui/coherence_orphan/uncovered_T.\360\237\224\254" index fa78976b..6082f558 100644 --- "a/tests/ui/coherence_orphan/uncovered_T.\360\237\224\254" +++ "b/tests/ui/coherence_orphan/uncovered_T.\360\237\224\254" @@ -1,9 +1,9 @@ [ crate core { - trait CoreTrait where [] {} + trait CoreTrait {} }, crate foo { - struct FooStruct<> where [] {} - impl CoreTrait> for T where [] {} + struct FooStruct {} + impl CoreTrait for T {} } ] diff --git a/tests/ui/coherence_overlap/T_where_Foo_not_u32_impls.stderr b/tests/ui/coherence_overlap/T_where_Foo_not_u32_impls.stderr index ecbbe8ee..39bd5e7b 100644 --- a/tests/ui/coherence_overlap/T_where_Foo_not_u32_impls.stderr +++ b/tests/ui/coherence_overlap/T_where_Foo_not_u32_impls.stderr @@ -1,4 +1,4 @@ -Error: check_trait_impl(impl Foo < > for ^ty0_0 where [^ty0_0 : Foo < >] { }) +Error: check_trait_impl(impl Foo for ^ty0_0 where ^ty0_0 : Foo { }) Caused by: failed to disprove diff --git "a/tests/ui/coherence_overlap/T_where_Foo_not_u32_impls.\360\237\224\254" "b/tests/ui/coherence_overlap/T_where_Foo_not_u32_impls.\360\237\224\254" index d6d1b295..9e3c2db0 100644 --- "a/tests/ui/coherence_overlap/T_where_Foo_not_u32_impls.\360\237\224\254" +++ "b/tests/ui/coherence_overlap/T_where_Foo_not_u32_impls.\360\237\224\254" @@ -5,8 +5,8 @@ // was erroneously accepted by an earlier variant of negative impls. [ crate core { - trait Foo<> where [] {} - impl Foo<> for T where [T: Foo<>] {} - impl<> !Foo<> for u32 where [] {} + trait Foo {} + impl Foo for T where T: Foo {} + impl !Foo for u32 {} } ] diff --git a/tests/ui/coherence_overlap/foo_crate_cannot_assume_CoreStruct_does_not_impl_CoreTrait.stderr b/tests/ui/coherence_overlap/foo_crate_cannot_assume_CoreStruct_does_not_impl_CoreTrait.stderr index 280dfd6b..34f60b86 100644 --- a/tests/ui/coherence_overlap/foo_crate_cannot_assume_CoreStruct_does_not_impl_CoreTrait.stderr +++ b/tests/ui/coherence_overlap/foo_crate_cannot_assume_CoreStruct_does_not_impl_CoreTrait.stderr @@ -1,3 +1,3 @@ Error: impls may overlap: -impl FooTrait < > for ^ty0_0 where [^ty0_0 : CoreTrait < >] { } -impl <> FooTrait < > for CoreStruct where [] { } +impl FooTrait for ^ty0_0 where ^ty0_0 : CoreTrait { } +impl FooTrait for CoreStruct { } diff --git "a/tests/ui/coherence_overlap/foo_crate_cannot_assume_CoreStruct_does_not_impl_CoreTrait.\360\237\224\254" "b/tests/ui/coherence_overlap/foo_crate_cannot_assume_CoreStruct_does_not_impl_CoreTrait.\360\237\224\254" index b70143ea..7331d51f 100644 --- "a/tests/ui/coherence_overlap/foo_crate_cannot_assume_CoreStruct_does_not_impl_CoreTrait.\360\237\224\254" +++ "b/tests/ui/coherence_overlap/foo_crate_cannot_assume_CoreStruct_does_not_impl_CoreTrait.\360\237\224\254" @@ -1,11 +1,11 @@ [ crate core { - trait CoreTrait<> where [] {} - struct CoreStruct<> where [] {} + trait CoreTrait {} + struct CoreStruct {} }, crate foo { - trait FooTrait<> where [] {} - impl FooTrait<> for T where [T: CoreTrait<>] {} - impl<> FooTrait<> for CoreStruct<> where [] {} + trait FooTrait {} + impl FooTrait for T where T: CoreTrait {} + impl FooTrait for CoreStruct {} } ] diff --git "a/tests/ui/coherence_overlap/neg_CoreTrait_for_CoreStruct_implies_no_overlap.\360\237\224\254" "b/tests/ui/coherence_overlap/neg_CoreTrait_for_CoreStruct_implies_no_overlap.\360\237\224\254" index ccfc44dd..632420b4 100644 --- "a/tests/ui/coherence_overlap/neg_CoreTrait_for_CoreStruct_implies_no_overlap.\360\237\224\254" +++ "b/tests/ui/coherence_overlap/neg_CoreTrait_for_CoreStruct_implies_no_overlap.\360\237\224\254" @@ -3,13 +3,13 @@ // where there is a negative impl, so it is accepted. [ crate core { - trait CoreTrait<> where [] {} - struct CoreStruct<> where [] {} - impl<> !CoreTrait<> for CoreStruct<> where [] {} + trait CoreTrait {} + struct CoreStruct {} + impl !CoreTrait for CoreStruct {} }, crate foo { - trait FooTrait<> where [] {} - impl FooTrait<> for T where [T: CoreTrait<>] {} - impl<> FooTrait<> for CoreStruct<> where [] {} + trait FooTrait {} + impl FooTrait for T where T: CoreTrait {} + impl FooTrait for CoreStruct {} } ] diff --git a/tests/ui/coherence_overlap/u32_T_impls.stderr b/tests/ui/coherence_overlap/u32_T_impls.stderr index 83ac7c7c..2ec470b1 100644 --- a/tests/ui/coherence_overlap/u32_T_impls.stderr +++ b/tests/ui/coherence_overlap/u32_T_impls.stderr @@ -1,3 +1,3 @@ Error: impls may overlap: -impl <> Foo < > for u32 where [] { } -impl Foo < > for ^ty0_0 where [] { } +impl Foo for u32 { } +impl Foo for ^ty0_0 { } diff --git "a/tests/ui/coherence_overlap/u32_T_impls.\360\237\224\254" "b/tests/ui/coherence_overlap/u32_T_impls.\360\237\224\254" index 9aaf9f06..ebd0cf4d 100644 --- "a/tests/ui/coherence_overlap/u32_T_impls.\360\237\224\254" +++ "b/tests/ui/coherence_overlap/u32_T_impls.\360\237\224\254" @@ -1,7 +1,7 @@ [ crate core { - trait Foo<> where [] {} - impl<> Foo<> for u32 where [] {} - impl Foo<> for T where [] {} + trait Foo {} + impl Foo for u32 {} + impl Foo for T {} } ] diff --git a/tests/ui/coherence_overlap/u32_T_where_T_Is_impls.stderr b/tests/ui/coherence_overlap/u32_T_where_T_Is_impls.stderr index b4bbfe86..aa6c5ed2 100644 --- a/tests/ui/coherence_overlap/u32_T_where_T_Is_impls.stderr +++ b/tests/ui/coherence_overlap/u32_T_where_T_Is_impls.stderr @@ -1,3 +1,3 @@ Error: impls may overlap: -impl <> Foo < > for u32 where [] { } -impl Foo < > for ^ty0_0 where [^ty0_0 : Is < >] { } +impl Foo for u32 { } +impl Foo for ^ty0_0 where ^ty0_0 : Is { } diff --git "a/tests/ui/coherence_overlap/u32_T_where_T_Is_impls.\360\237\224\254" "b/tests/ui/coherence_overlap/u32_T_where_T_Is_impls.\360\237\224\254" index 149d9d05..c0abce62 100644 --- "a/tests/ui/coherence_overlap/u32_T_where_T_Is_impls.\360\237\224\254" +++ "b/tests/ui/coherence_overlap/u32_T_where_T_Is_impls.\360\237\224\254" @@ -2,11 +2,11 @@ // and also all `T: Is`, and `u32: Is`. [ crate core { - trait Foo<> where [] {} - impl<> Foo<> for u32 where [] {} - impl Foo<> for T where [T: Is<>] {} + trait Foo {} + impl Foo for u32 {} + impl Foo for T where T: Is {} - trait Is<> where [] {} - impl<> Is<> for u32 where [] {} + trait Is {} + impl Is for u32 {} } ] diff --git "a/tests/ui/coherence_overlap/u32_T_where_T_Not_impls.\360\237\224\254" "b/tests/ui/coherence_overlap/u32_T_where_T_Not_impls.\360\237\224\254" index 4e44d346..ce525162 100644 --- "a/tests/ui/coherence_overlap/u32_T_where_T_Not_impls.\360\237\224\254" +++ "b/tests/ui/coherence_overlap/u32_T_where_T_Not_impls.\360\237\224\254" @@ -6,10 +6,10 @@ // See also test_foo_crate_cannot_assume_CoreStruct_does_not_impl_CoreTrait [ crate core { - trait Foo<> where [] {} - impl<> Foo<> for u32 where [] {} - impl Foo<> for T where [T: Not<>] {} + trait Foo {} + impl Foo for u32 {} + impl Foo for T where T: Not {} - trait Not<> where [] {} + trait Not {} } ] diff --git "a/tests/ui/coherence_overlap/u32_i32_impls.\360\237\224\254" "b/tests/ui/coherence_overlap/u32_i32_impls.\360\237\224\254" index b85c62ff..e2fbc191 100644 --- "a/tests/ui/coherence_overlap/u32_i32_impls.\360\237\224\254" +++ "b/tests/ui/coherence_overlap/u32_i32_impls.\360\237\224\254" @@ -1,8 +1,8 @@ //@check-pass [ crate core { - trait Foo<> where [] {} - impl<> Foo<> for u32 where [] {} - impl<> Foo<> for i32 where [] {} + trait Foo {} + impl Foo for u32 {} + impl Foo for i32 {} } ] diff --git a/tests/ui/coherence_overlap/u32_not_u32_impls.stderr b/tests/ui/coherence_overlap/u32_not_u32_impls.stderr index 65398442..b7710f57 100644 --- a/tests/ui/coherence_overlap/u32_not_u32_impls.stderr +++ b/tests/ui/coherence_overlap/u32_not_u32_impls.stderr @@ -1,4 +1,4 @@ -Error: check_trait_impl(impl <> Foo < > for u32 where [] { }) +Error: check_trait_impl(impl Foo for u32 { }) Caused by: failed to disprove diff --git "a/tests/ui/coherence_overlap/u32_not_u32_impls.\360\237\224\254" "b/tests/ui/coherence_overlap/u32_not_u32_impls.\360\237\224\254" index 8140731b..941b8550 100644 --- "a/tests/ui/coherence_overlap/u32_not_u32_impls.\360\237\224\254" +++ "b/tests/ui/coherence_overlap/u32_not_u32_impls.\360\237\224\254" @@ -1,8 +1,8 @@ // Test that a positive and negative impl for the same type (`u32`, here) is rejected. [ crate core { - trait Foo<> where [] {} - impl<> Foo<> for u32 where [] {} - impl<> !Foo<> for u32 where [] {} + trait Foo {} + impl Foo for u32 {} + impl !Foo for u32 {} } ] diff --git a/tests/ui/coherence_overlap/u32_u32_impls.stderr b/tests/ui/coherence_overlap/u32_u32_impls.stderr index fb0aa5a5..1e241644 100644 --- a/tests/ui/coherence_overlap/u32_u32_impls.stderr +++ b/tests/ui/coherence_overlap/u32_u32_impls.stderr @@ -1 +1 @@ -Error: duplicate impl in current crate: impl <> Foo < > for u32 where [] { } +Error: duplicate impl in current crate: impl Foo for u32 { } diff --git "a/tests/ui/coherence_overlap/u32_u32_impls.\360\237\224\254" "b/tests/ui/coherence_overlap/u32_u32_impls.\360\237\224\254" index c007b201..59069e29 100644 --- "a/tests/ui/coherence_overlap/u32_u32_impls.\360\237\224\254" +++ "b/tests/ui/coherence_overlap/u32_u32_impls.\360\237\224\254" @@ -1,7 +1,7 @@ [ crate core { - trait Foo<> where [] {} - impl<> Foo<> for u32 where [] {} - impl<> Foo<> for u32 where [] {} + trait Foo {} + impl Foo for u32 {} + impl Foo for u32 {} } ] diff --git a/tests/ui/consts/generic_mismatch.stderr b/tests/ui/consts/generic_mismatch.stderr index 1b15792b..7732b05b 100644 --- a/tests/ui/consts/generic_mismatch.stderr +++ b/tests/ui/consts/generic_mismatch.stderr @@ -1,4 +1,4 @@ -Error: check_trait_impl(impl Foo < const ^const0_0 > for u32 where [type_of_const ^const0_0 is u32] { }) +Error: check_trait_impl(impl Foo for u32 where type_of_const ^const0_0 is u32 { }) Caused by: failed to prove {Foo(u32, const !const_1)} given {@ ConstHasType(!const_1 , u32)}, got {} diff --git "a/tests/ui/consts/generic_mismatch.\360\237\224\254" "b/tests/ui/consts/generic_mismatch.\360\237\224\254" index 00855203..217d22b9 100644 --- "a/tests/ui/consts/generic_mismatch.\360\237\224\254" +++ "b/tests/ui/consts/generic_mismatch.\360\237\224\254" @@ -1,7 +1,7 @@ [ crate Foo { - trait Foo where [type_of_const C is bool] {} + trait Foo where type_of_const C is bool {} - impl Foo for u32 where [type_of_const C is u32] {} + impl Foo for u32 where type_of_const C is u32 {} } ] diff --git "a/tests/ui/consts/holds.\360\237\224\254" "b/tests/ui/consts/holds.\360\237\224\254" index a124a028..c1ac5ca2 100644 --- "a/tests/ui/consts/holds.\360\237\224\254" +++ "b/tests/ui/consts/holds.\360\237\224\254" @@ -1,8 +1,8 @@ //@check-pass [ crate Foo { - trait Foo where [type_of_const C is bool] {} + trait Foo where type_of_const C is bool {} - impl<> Foo for u32 where [] {} + impl Foo for u32 {} } ] diff --git a/tests/ui/consts/mismatch.stderr b/tests/ui/consts/mismatch.stderr index f1bf5189..7b5542e0 100644 --- a/tests/ui/consts/mismatch.stderr +++ b/tests/ui/consts/mismatch.stderr @@ -1,4 +1,4 @@ -Error: check_trait_impl(impl <> Foo < const value(42, u32) > for u32 where [] { }) +Error: check_trait_impl(impl Foo for u32 { }) Caused by: failed to prove {Foo(u32, const value(42, u32))} given {}, got {} diff --git "a/tests/ui/consts/mismatch.\360\237\224\254" "b/tests/ui/consts/mismatch.\360\237\224\254" index ba7598be..d3aad171 100644 --- "a/tests/ui/consts/mismatch.\360\237\224\254" +++ "b/tests/ui/consts/mismatch.\360\237\224\254" @@ -1,7 +1,7 @@ [ crate Foo { - trait Foo where [type_of_const C is bool] {} + trait Foo where type_of_const C is bool {} - impl<> Foo for u32 where [] {} + impl Foo for u32 {} } ] diff --git "a/tests/ui/consts/multiple_type_of_const.\360\237\224\254" "b/tests/ui/consts/multiple_type_of_const.\360\237\224\254" index 9a416ed9..b643b71c 100644 --- "a/tests/ui/consts/multiple_type_of_const.\360\237\224\254" +++ "b/tests/ui/consts/multiple_type_of_const.\360\237\224\254" @@ -5,6 +5,6 @@ //@check-pass [ crate Foo { - trait Foo where [type_of_const C is bool, type_of_const C is u32] {} + trait Foo where type_of_const C is bool, type_of_const C is u32 {} } ] diff --git "a/tests/ui/consts/nonsense_rigid_const_bound.\360\237\224\254" "b/tests/ui/consts/nonsense_rigid_const_bound.\360\237\224\254" index cf6cc59f..d3d0f2bb 100644 --- "a/tests/ui/consts/nonsense_rigid_const_bound.\360\237\224\254" +++ "b/tests/ui/consts/nonsense_rigid_const_bound.\360\237\224\254" @@ -2,6 +2,6 @@ /// substituting and directly going to a wrong constant. [ crate Foo { - trait Foo<> where [type_of_const true is u32] {} + trait Foo where type_of_const true is u32 {} } ] diff --git "a/tests/ui/consts/ok.\360\237\224\254" "b/tests/ui/consts/ok.\360\237\224\254" index e565909d..e5311ebe 100644 --- "a/tests/ui/consts/ok.\360\237\224\254" +++ "b/tests/ui/consts/ok.\360\237\224\254" @@ -1,9 +1,9 @@ //@check-pass [ crate Foo { - trait Foo where [type_of_const C is bool] {} - trait Bar where [type_of_const C is u32] {} + trait Foo where type_of_const C is bool {} + trait Bar where type_of_const C is u32 {} - impl Foo for u32 where [type_of_const C is bool] {} + impl Foo for u32 where type_of_const C is bool {} } ] diff --git "a/tests/ui/consts/rigid_const_bound.\360\237\224\254" "b/tests/ui/consts/rigid_const_bound.\360\237\224\254" index 179a7030..4a0856ff 100644 --- "a/tests/ui/consts/rigid_const_bound.\360\237\224\254" +++ "b/tests/ui/consts/rigid_const_bound.\360\237\224\254" @@ -3,6 +3,6 @@ //@check-pass [ crate Foo { - trait Foo<> where [type_of_const true is bool] {} + trait Foo where type_of_const true is bool {} } ] diff --git "a/tests/ui/fn/lifetime.\360\237\224\254" "b/tests/ui/fn/lifetime.\360\237\224\254" index e1f33550..59716040 100644 --- "a/tests/ui/fn/lifetime.\360\237\224\254" +++ "b/tests/ui/fn/lifetime.\360\237\224\254" @@ -2,6 +2,6 @@ [ crate Foo { // fn one_lt_arg<'a, T>(_: &'a T) -> () {} - fn one_lt_arg(&a T) -> () where [] { trusted } + fn one_lt_arg(&a T) -> () { trusted } } ] diff --git "a/tests/ui/fn/ok.\360\237\224\254" "b/tests/ui/fn/ok.\360\237\224\254" index cfa61a7e..8925cf45 100644 --- "a/tests/ui/fn/ok.\360\237\224\254" +++ "b/tests/ui/fn/ok.\360\237\224\254" @@ -3,18 +3,18 @@ [ crate Foo { // fn simple_fn() {} - fn simple_fn<>() -> () where [] { trusted } + fn simple_fn() -> () { trusted } // fn one_arg(_: T) {} - fn one_arg(T) -> () where [] { trusted } + fn one_arg(T) -> () { trusted } // fn one_ret(_: T) {} - fn one_ret() -> T where [] { trusted } + fn one_ret() -> T { trusted } // fn arg_ret(_: T) -> U {} - fn arg_ret(T) -> U where [] { trusted } + fn arg_ret(T) -> U { trusted } // fn multi_arg_ret(_: T, _: Y) -> (U, I) {} - fn multi_arg_ret(T, Y) -> (U, I) where [] { trusted } + fn multi_arg_ret(T, Y) -> (U, I) { trusted } } ] diff --git "a/tests/ui/hello_world.\360\237\224\254" "b/tests/ui/hello_world.\360\237\224\254" index 3a0bb8e4..6b035c1e 100644 --- "a/tests/ui/hello_world.\360\237\224\254" +++ "b/tests/ui/hello_world.\360\237\224\254" @@ -1,15 +1,15 @@ //@check-pass [ crate Foo { - trait Foo where [T: Bar, Self: Baz<>] {} + trait Foo where T: Bar, Self: Baz {} - trait Bar where [T: Baz<>] {} + trait Bar where T: Baz {} - trait Baz<> where [] {} + trait Baz {} - impl<> Baz<> for u32 where [] {} + impl Baz for u32 {} - impl<> Bar for u32 where [] {} - impl Bar for () where [T: Baz<>] {} + impl Bar for u32 {} + impl Bar for () where T: Baz {} } ] diff --git a/tests/ui/hello_world_fail.stderr b/tests/ui/hello_world_fail.stderr index 93081ae3..1d9f5654 100644 --- a/tests/ui/hello_world_fail.stderr +++ b/tests/ui/hello_world_fail.stderr @@ -1,5 +1,5 @@ Error: check_trait(Foo) Caused by: - 0: prove_where_clause_well_formed(!ty_2 : Bar < !ty_1 >) + 0: prove_where_clause_well_formed(!ty_2 : Bar ) 1: failed to prove {@ WellFormedTraitRef(Bar(!ty_2, !ty_1))} given {Bar(!ty_2, !ty_1)}, got {} diff --git "a/tests/ui/hello_world_fail.\360\237\224\254" "b/tests/ui/hello_world_fail.\360\237\224\254" index 4620deca..69ca1485 100644 --- "a/tests/ui/hello_world_fail.\360\237\224\254" +++ "b/tests/ui/hello_world_fail.\360\237\224\254" @@ -1,9 +1,9 @@ [ crate Foo { - trait Foo where [T: Bar] {} + trait Foo where T: Bar {} - trait Bar where [T: Baz<>] {} + trait Bar where T: Baz {} - trait Baz<> where [] {} + trait Baz {} } ] diff --git a/tests/ui/parser.stderr b/tests/ui/parser.stderr index 61a957c7..5b943336 100644 --- a/tests/ui/parser.stderr +++ b/tests/ui/parser.stderr @@ -1,13 +1,13 @@ Error: expected `:` Caused by: - 0: ] {} + 0: {} } ] 1: failed to parse [ crate Foo { - trait Baz<> where [ cake ] {} + trait Baz where cake {} } ] diff --git "a/tests/ui/parser.\360\237\224\254" "b/tests/ui/parser.\360\237\224\254" index 45f21b25..d2e35fb2 100644 --- "a/tests/ui/parser.\360\237\224\254" +++ "b/tests/ui/parser.\360\237\224\254" @@ -1,5 +1,5 @@ [ crate Foo { - trait Baz<> where [ cake ] {} + trait Baz where cake {} } ]