Skip to content

Commit 606a240

Browse files
committed
automatically reject variable names
When one variant is tagged as `#[variable]`, we now automatically reject variable names from other variants. This means we can use `#[precedence]` just to encode true *precedence* (i.e., of operators). This was important since there was a conflict where variables wanted to be the same *precedence* as other identifiers in a logical sense.
1 parent 901d2e1 commit 606a240

File tree

6 files changed

+56
-20
lines changed

6 files changed

+56
-20
lines changed

crates/formality-core/src/language.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::cast::UpcastFrom;
22
use crate::term::CoreTerm;
33
use crate::variable::{CoreBoundVar, CoreExistentialVar, CoreUniversalVar, CoreVariable};
4+
use crate::DowncastTo;
45
use std::fmt::Debug;
56
use std::hash::Hash;
67

@@ -20,7 +21,8 @@ pub trait Language: 'static + Copy + Ord + Hash + Debug + Default {
2021
+ UpcastFrom<CoreVariable<Self>>
2122
+ UpcastFrom<CoreUniversalVar<Self>>
2223
+ UpcastFrom<CoreExistentialVar<Self>>
23-
+ UpcastFrom<CoreBoundVar<Self>>;
24+
+ UpcastFrom<CoreBoundVar<Self>>
25+
+ DowncastTo<CoreVariable<Self>>;
2426

2527
/// The token (typically `<`) used to open binders.
2628
const BINDING_OPEN: char;

crates/formality-core/src/parse/parser.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use std::str::FromStr;
33

44
use crate::{
55
language::{CoreParameter, HasKind, Language},
6-
set, Downcast, DowncastFrom, Set, Upcast,
6+
set,
7+
variable::CoreVariable,
8+
Downcast, DowncastFrom, Set, Upcast,
79
};
810

911
use super::{CoreParse, ParseError, ParseResult, Scope, SuccessfulParse, TokenResult};
@@ -471,6 +473,21 @@ where
471473
result
472474
}
473475

476+
/// Returns an error if an in-scope variable name is found.
477+
/// The derive automatically inserts calls to this for all other variants
478+
/// if any variant is declared `#[variable]`.
479+
pub fn reject_variable(&self) -> Result<(), Set<ParseError<'t>>> {
480+
self.reject::<CoreVariable<L>>(
481+
|p| p.variable(),
482+
|var| {
483+
ParseError::at(
484+
self.text,
485+
format!("found unexpected in-scope variable {:?}", var),
486+
)
487+
},
488+
)
489+
}
490+
474491
/// Parses the next identifier as a variable in scope
475492
/// with the kind appropriate for the return type `R`.
476493
///

crates/formality-macros/src/parse.rs

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,17 @@ pub(crate) fn derive_parse_with_spec(
3737
}
3838
}
3939

40+
// Determine whether *any* variant is marked as `#[variable]`.
41+
// If so, all *other* variants will automatically reject in-scope variable names.
42+
let any_variable_variant = s
43+
.variants()
44+
.iter()
45+
.any(|v| has_variable_attr(v.ast().attrs));
46+
4047
let mut parse_variants = TokenStream::new();
4148
for variant in s.variants() {
4249
let variant_name = Literal::string(&format!("{}::{}", s.ast().ident, variant.ast().ident));
43-
let v = parse_variant(variant, external_spec)?;
50+
let v = parse_variant(variant, external_spec, any_variable_variant)?;
4451
let precedence = precedence(&variant.ast().attrs)?.literal();
4552
parse_variants.extend(quote_spanned!(
4653
variant.ast().ident.span() =>
@@ -66,17 +73,27 @@ pub(crate) fn derive_parse_with_spec(
6673
fn parse_variant(
6774
variant: &synstructure::VariantInfo,
6875
external_spec: Option<&FormalitySpec>,
76+
any_variable_variant: bool,
6977
) -> syn::Result<TokenStream> {
7078
let ast = variant.ast();
79+
let has_variable_attr = has_variable_attr(ast.attrs);
80+
let mut stream = TokenStream::default();
81+
82+
// If there are variable variants -- but this is not one -- then we always begin by rejecting
83+
// variable names. This avoids ambiguity in a case like `for<ty X> { X : Debug }`, where we
84+
// want to parse `X` as a type variable.
85+
if any_variable_variant && !has_variable_attr {
86+
stream.extend(quote_spanned!(ast.ident.span() => __p.reject_variable()?;));
87+
}
7188

7289
// When invoked like `#[term(foo)]`, use the spec from `foo`
7390
if let Some(spec) = external_spec {
74-
return parse_variant_with_attr(variant, spec);
91+
return parse_variant_with_attr(variant, spec, stream);
7592
}
7693

7794
// Else, look for a `#[grammar]` attribute on the variant
7895
if let Some(attr) = get_grammar_attr(ast.attrs) {
79-
return parse_variant_with_attr(variant, &attr?);
96+
return parse_variant_with_attr(variant, &attr?, stream);
8097
}
8198

8299
// If no `#[grammar(...)]` attribute is provided, then we provide default behavior.
@@ -85,44 +102,46 @@ fn parse_variant(
85102
// No bindings (e.g., `Foo`) -- just parse a keyword `foo`
86103
let literal = Literal::string(&to_parse_ident(ast.ident));
87104
let construct = variant.construct(|_, _| quote! {});
88-
Ok(quote_spanned! {
105+
stream.extend(quote_spanned! {
89106
ast.ident.span() =>
90107
__p.expect_keyword(#literal)?;
91108
Ok(#construct)
92-
})
93-
} else if has_variable_attr(variant.ast().attrs) {
109+
});
110+
} else if has_variable_attr {
94111
// Has the `#[variable]` attribute -- parse an identifier and then check to see if it is present
95112
// in the scope. If so, downcast it and check that it has the correct kind.
96-
Ok(quote_spanned! {
113+
stream.extend(quote_spanned! {
97114
ast.ident.span() =>
98115
let v = __p.variable()?;
99116
Ok(v)
100-
})
117+
});
101118
} else if has_cast_attr(variant.ast().attrs) {
102119
// Has the `#[cast]` attribute -- just parse the bindings (comma separated, if needed)
103120
let build: Vec<TokenStream> = parse_bindings(variant.bindings());
104121
let construct = variant.construct(field_ident);
105-
Ok(quote_spanned! {
122+
stream.extend(quote_spanned! {
106123
ast.ident.span() =>
107124
__p.mark_as_cast_variant();
108125
#(#build)*
109126
Ok(#construct)
110-
})
127+
});
111128
} else {
112129
// Otherwise -- parse `variant(binding0, ..., bindingN)`
113130
let literal = Literal::string(&to_parse_ident(ast.ident));
114131
let build: Vec<TokenStream> = parse_bindings(variant.bindings());
115132
let construct = variant.construct(field_ident);
116-
Ok(quote_spanned! {
133+
stream.extend(quote_spanned! {
117134
ast.ident.span() =>
118135
__p.expect_keyword(#literal)?;
119136
__p.expect_char('(')?;
120137
#(#build)*
121138
__p.skip_trailing_comma();
122139
__p.expect_char(')')?;
123140
Ok(#construct)
124-
})
141+
});
125142
}
143+
144+
Ok(stream)
126145
}
127146

128147
/// When a type is given a formality attribute, we use that to guide parsing:
@@ -144,9 +163,8 @@ fn parse_variant(
144163
fn parse_variant_with_attr(
145164
variant: &synstructure::VariantInfo,
146165
spec: &FormalitySpec,
166+
mut stream: TokenStream,
147167
) -> syn::Result<TokenStream> {
148-
let mut stream = TokenStream::new();
149-
150168
for symbol in &spec.symbols {
151169
stream.extend(match symbol {
152170
spec::FormalitySpecSymbol::Field { name, mode } => {

crates/formality-types/src/grammar/consts.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ pub enum ConstData {
4646
Value(ValTree, Ty),
4747

4848
#[variable]
49-
#[precedence(1)]
5049
Variable(Variable),
5150
}
5251

crates/formality-types/src/grammar/ty.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ pub enum TyData {
107107
#[cast]
108108
PredicateTy(PredicateTy),
109109
#[variable]
110-
#[precedence(1)]
111110
Variable(Variable),
112111
}
113112

crates/formality-types/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ formality_core::declare_language! {
1616
"enum",
1717
"union",
1818
"const",
19-
"ty",
20-
"lt",
19+
"true",
20+
"false",
21+
"static",
2122
];
2223
}
2324
}

0 commit comments

Comments
 (0)