Skip to content

Commit

Permalink
feat: Allow attributes to be specified on fields inside types
Browse files Browse the repository at this point in the history
  • Loading branch information
Marwes committed Oct 7, 2019
1 parent 24ca105 commit fb35db5
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 41 deletions.
34 changes: 21 additions & 13 deletions base/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl<'a, T: ?Sized + IdentEnv> IdentEnv for &'a mut T {

#[derive(Clone, Eq, PartialEq, Debug)]
struct InnerAstType<Id> {
comment: Option<Comment>,
metadata: Option<Metadata>,
typ: Spanned<Type<Id, AstType<Id>>, BytePos>,
}

Expand Down Expand Up @@ -101,7 +101,10 @@ impl<Id: AsRef<str>> fmt::Display for AstType<Id> {
impl<Id> From<Spanned<Type<Id, AstType<Id>>, BytePos>> for AstType<Id> {
fn from(typ: Spanned<Type<Id, AstType<Id>>, BytePos>) -> Self {
AstType {
_typ: Box::new(InnerAstType { comment: None, typ }),
_typ: Box::new(InnerAstType {
metadata: None,
typ,
}),
}
}
}
Expand Down Expand Up @@ -139,34 +142,39 @@ where
}
}

pub trait Commented {
fn comment(&self) -> Option<&Comment>;
}
pub trait HasMetadata {
fn metadata(&self) -> Option<&Metadata>;

impl<Id> Commented for AstType<Id> {
fn comment(&self) -> Option<&Comment> {
self._typ.comment.as_ref()
self.metadata()
.and_then(|metadata| metadata.comment.as_ref())
}
}

impl<Id> HasMetadata for AstType<Id> {
fn metadata(&self) -> Option<&Metadata> {
self._typ.metadata.as_ref()
}
}

impl<Id> AstType<Id> {
pub fn with_comment<T>(comment: T, typ: Spanned<Type<Id, AstType<Id>>, BytePos>) -> Self
pub fn with_metadata<T>(metadata: T, typ: Spanned<Type<Id, AstType<Id>>, BytePos>) -> Self
where
T: Into<Option<Comment>>,
T: Into<Option<Metadata>>,
{
AstType {
_typ: Box::new(InnerAstType {
comment: comment.into(),
metadata: metadata.into(),
typ,
}),
}
}

pub fn set_comment<T>(&mut self, comment: T)
pub fn set_metadata<T>(&mut self, metadata: T)
where
T: Into<Option<Comment>>,
T: Into<Option<Metadata>>,
{
self._typ.comment = comment.into();
self._typ.metadata = metadata.into();
}

pub fn into_inner(self) -> Type<Id, Self> {
Expand Down
14 changes: 7 additions & 7 deletions base/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ use smallvec::SmallVec;
use itertools::Itertools;

use crate::{
ast::{Commented, EmptyEnv, IdentEnv},
ast::{EmptyEnv, HasMetadata, IdentEnv},
fnv::FnvMap,
kind::{ArcKind, Kind, KindCache, KindEnv},
merge::{merge, merge_collect},
metadata::Comment,
metadata::Metadata,
pos::{BytePos, HasSpan, Span},
source::Source,
symbol::{Name, Symbol, SymbolRef},
Expand Down Expand Up @@ -1434,8 +1434,8 @@ impl<Id> HasSpan for ArcType<Id> {
}
}

impl<Id> Commented for ArcType<Id> {
fn comment(&self) -> Option<&Comment> {
impl<Id> HasMetadata for ArcType<Id> {
fn metadata(&self) -> Option<&Metadata> {
None
}
}
Expand Down Expand Up @@ -1598,7 +1598,7 @@ pub trait TypeExt: Deref<Target = Type<<Self as TypeExt>::Id, Self>> + Clone + S
where
Self::Id: AsRef<str> + 'a,
A: Clone,
Self: Commented + HasSpan,
Self: HasMetadata + HasSpan,
{
top(self).pretty(&Printer::new(arena, &()))
}
Expand Down Expand Up @@ -2312,7 +2312,7 @@ const INDENT: usize = 4;

impl<'a, I, T> DisplayType<'a, T>
where
T: Deref<Target = Type<I, T>> + HasSpan + Commented + 'a,
T: Deref<Target = Type<I, T>> + HasSpan + HasMetadata + 'a,
I: AsRef<str> + 'a,
{
pub fn pretty<A>(&self, printer: &Printer<'a, I, A>) -> DocBuilder<'a, Arena<'a, A>, A>
Expand Down Expand Up @@ -2705,7 +2705,7 @@ pub fn pretty_print<'a, I, T, A>(
) -> DocBuilder<'a, Arena<'a, A>, A>
where
I: AsRef<str> + 'a,
T: Deref<Target = Type<I, T>> + HasSpan + Commented,
T: Deref<Target = Type<I, T>> + HasSpan + HasMetadata,
A: Clone,
{
dt(Prec::Top, typ).pretty(printer)
Expand Down
6 changes: 3 additions & 3 deletions base/src/types/pretty_print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::ops::Deref;

use pretty::{Arena, Doc, DocAllocator, DocBuilder};

use crate::ast::{is_operator_char, Commented};
use crate::ast::{is_operator_char, HasMetadata};
use crate::metadata::{Comment, CommentType};
use crate::pos::{BytePos, HasSpan, Span};
use crate::source::Source;
Expand Down Expand Up @@ -129,7 +129,7 @@ impl<'a, I, T, A> TypeFormatter<'a, I, T, A> {

pub fn pretty(&self, arena: &'a Arena<'a, A>) -> DocBuilder<'a, Arena<'a, A>, A>
where
T: Deref<Target = Type<I, T>> + HasSpan + Commented + 'a,
T: Deref<Target = Type<I, T>> + HasSpan + HasMetadata + 'a,
I: AsRef<str>,
A: Clone,
{
Expand All @@ -156,7 +156,7 @@ impl<'a, I, T, A> TypeFormatter<'a, I, T, A> {

impl<'a, I, T> fmt::Display for TypeFormatter<'a, I, T, ()>
where
T: Deref<Target = Type<I, T>> + HasSpan + Commented + 'a,
T: Deref<Target = Type<I, T>> + HasSpan + HasMetadata + 'a,
I: AsRef<str>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Expand Down
12 changes: 6 additions & 6 deletions check/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{collections::BTreeMap, sync::Arc};

use crate::base::ast::Visitor;
use crate::base::ast::{
self, Argument, AstType, Commented, Expr, Pattern, SpannedExpr, SpannedPattern, ValueBinding,
self, Argument, AstType, Expr, HasMetadata, Pattern, SpannedExpr, SpannedPattern, ValueBinding,
};
use crate::base::fnv::FnvMap;
use crate::base::metadata::{Metadata, MetadataEnv};
Expand Down Expand Up @@ -199,7 +199,7 @@ pub fn metadata(
}
for field in types {
if let Some(m) = metadata.get_module(field.name.value.as_ref()) {
// FIXME Shouldn't need to insert this metadata twice
// TODO Shouldn't need to insert this metadata twice
if let Some(type_field) = typ
.type_field_iter()
.find(|type_field| type_field.name.name_eq(&field.name.value))
Expand Down Expand Up @@ -356,7 +356,7 @@ pub fn metadata(
);

if metadata.has_data() {
// FIXME Shouldn't need to insert this metadata twice
// TODO Shouldn't need to insert this metadata twice
self.stack_var(bind.alias.value.name.clone(), metadata.clone());
self.stack_var(bind.name.value.clone(), metadata);
}
Expand All @@ -383,10 +383,10 @@ pub fn metadata(
let module: BTreeMap<_, _> = row_iter(typ)
.filter_map(|field| {
let field_metadata = Self::metadata_of_type(&field.typ);
let field_metadata = match field.typ.comment() {
Some(comment) => {
let field_metadata = match field.typ.metadata() {
Some(other_metadata) => {
let mut metadata = field_metadata.unwrap_or_default();
metadata.comment = Some(comment.clone());
metadata.merge_with_ref(other_metadata);
Some(metadata)
}
None => field_metadata,
Expand Down
4 changes: 2 additions & 2 deletions check/src/typecheck/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ where
I: fmt::Display + AsRef<str> + Clone,
T: TypeExt<Id = I>
+ fmt::Display
+ ast::Commented
+ ast::HasMetadata
+ pos::HasSpan
+ for<'a> ToDoc<'a, Arena<'a, ()>, (), ()>,
{
Expand Down Expand Up @@ -217,7 +217,7 @@ where
I: fmt::Display + AsRef<str> + Clone,
T: TypeExt<Id = I>
+ fmt::Display
+ ast::Commented
+ ast::HasMetadata
+ pos::HasSpan
+ for<'a> ToDoc<'a, Arena<'a, ()>, (), ()>,
{
Expand Down
4 changes: 2 additions & 2 deletions check/src/unify_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ impl<T> From<ResolveError> for TypeError<Symbol, T> {
impl<I, T> fmt::Display for TypeError<I, T>
where
I: fmt::Display + AsRef<str>,
T: TypeExt<Id = I> + ast::Commented + pos::HasSpan,
T: TypeExt<Id = I> + ast::HasMetadata + pos::HasSpan,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let filter = self.make_filter();
Expand Down Expand Up @@ -176,7 +176,7 @@ where
impl<I, T> TypeError<I, T>
where
I: fmt::Display + AsRef<str>,
T: TypeExt<Id = I> + ast::Commented + pos::HasSpan,
T: TypeExt<Id = I> + ast::HasMetadata + pos::HasSpan,
{
pub fn make_filter<'a>(&'a self) -> Box<dyn Fn(&I) -> Filter + 'a> {
match *self {
Expand Down
36 changes: 35 additions & 1 deletion check/tests/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ let x ?test : [Test a] -> a = test.x
}

#[test]
fn propagate_metadata_through_implicits() {
fn propagate_metadata_through_implicits1() {
let _ = env_logger::try_init();

let text = r#"
Expand Down Expand Up @@ -369,3 +369,37 @@ let x ?test : [Test a] -> Test (Wrap a) = { x = Wrap test.x }
})
);
}

#[test]
fn propagate_metadata_through_implicits2() {
let _ = env_logger::try_init();

let text = r#"
type Test a = {
#[attribute]
x : a,
}
type Wrap a = | Wrap a
let x ?test : [Test a] -> a = test.x
{ x }
"#;
let (mut expr, result) = support::typecheck_expr(text);

assert!(result.is_ok(), "{}", result.unwrap_err());

let metadata = metadata(&MockEnv, &mut expr);
assert_eq!(
metadata.module.get("x").map(|m| &**m),
Some(&Metadata {
definition: metadata.module.get("x").and_then(|m| m.definition.clone()),
attributes: vec![Attribute {
name: "attribute".into(),
arguments: None,
}],
args: vec![Argument::implicit(intern("test@9_8"))],
..Metadata::default()
})
);
}
14 changes: 7 additions & 7 deletions parser/src/grammar.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ TypeParam: Generic<Id> = {
};

RecordField: Either<Field<Id, Alias<Id, AstType<Id>>>, Field<Id, AstType<Id>>> = {
<comment: DocComment?> <id: Sp<Ident>> <args: Ident*> <alias: ("=" <Sp<Type_>>)> => {
<metadata: Metadata?> <id: Sp<Ident>> <args: Ident*> <alias: ("=" <Sp<Type_>>)> => {
let span = id.span;
Either::Left(Field::new(
id.value.clone(),
Expand All @@ -227,22 +227,22 @@ RecordField: Either<Field<Id, Alias<Id, AstType<Id>>>, Field<Id, AstType<Id>>> =
args.into_iter()
.map(|id| Generic::new(id, type_cache.kind_cache.hole()))
.collect(),
AstType::with_comment(comment, alias),
AstType::with_metadata(metadata, alias),
)),
))
},
<comment: DocComment?> <id: Sp<Ident>> => {
<metadata: Metadata?> <id: Sp<Ident>> => {
let span = id.span;
Either::Left(Field::new(
id.value.clone(),
Alias::new(
id.value,
Vec::new(),
AstType::with_comment(comment, pos::spanned(span, Type::Hole)),
AstType::with_metadata(metadata, pos::spanned(span, Type::Hole)),
),
))
},
<comment: DocComment?> <id: Ident> ":" <typ: Sp<Type_>> => {
<metadata: Metadata?> <id: Ident> ":" <typ: Sp<Type_>> => {
if env.string(&id).starts_with(char::is_uppercase) {
errors.push(::lalrpop_util::ParseError::User {
error: pos::spanned(typ.span, format!("Defining a kind for a type in this location is not supported yet").into()),
Expand All @@ -252,13 +252,13 @@ RecordField: Either<Field<Id, Alias<Id, AstType<Id>>>, Field<Id, AstType<Id>>> =
Alias::new(
id,
Vec::new(),
AstType::with_comment(comment, typ),
AstType::with_metadata(metadata, typ),
),
))
} else {
Either::Right(Field::new(
id,
AstType::with_comment(comment, typ),
AstType::with_metadata(metadata, typ),
))
}
},
Expand Down

0 comments on commit fb35db5

Please sign in to comment.