Skip to content

Commit

Permalink
fix: Return and error on duplicate fields defined in another module
Browse files Browse the repository at this point in the history
Since those symbols would be allocated in another symbol table their pointers would differ and so we must instead compare them by string instead of pointer
  • Loading branch information
Marwes committed Oct 12, 2017
1 parent 3eacf94 commit 0973b26
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 45 deletions.
11 changes: 6 additions & 5 deletions check/src/typecheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub enum TypeError<I> {
/// Multiple types were declared with the same name in the same expression
DuplicateTypeDefinition(I),
/// A field was defined more than once in a record constructor or pattern match
DuplicateField(I),
DuplicateField(String),
/// Type is not a type which has any fields
InvalidProjection(ArcType<I>),
/// Expected to find a record with the following fields
Expand Down Expand Up @@ -624,6 +624,7 @@ impl<'a> Typecheck<'a> {
&mut self,
expr: &mut SpannedExpr<Symbol>,
) -> Result<TailCall, TypeError<Symbol>> {
let expr_span = expr.span;
match expr.value {
Expr::Ident(ref mut id) => {
if let Some(new) = self.original_symbols.get(&id.name) {
Expand Down Expand Up @@ -871,7 +872,7 @@ impl<'a> Typecheck<'a> {
.filter(|field| {
self.error_on_duplicated_field(
&mut duplicated_fields,
pos::spanned(base.span, field.name.clone()),
pos::spanned(expr_span, field.name.clone()),
)
})
.cloned(),
Expand All @@ -882,7 +883,7 @@ impl<'a> Typecheck<'a> {
.filter(|field| {
self.error_on_duplicated_field(
&mut duplicated_fields,
pos::spanned(base.span, field.name.clone()),
pos::spanned(expr_span, field.name.clone()),
)
})
.cloned(),
Expand Down Expand Up @@ -1691,12 +1692,12 @@ impl<'a> Typecheck<'a> {

fn error_on_duplicated_field(
&mut self,
duplicated_fields: &mut FnvSet<Symbol>,
duplicated_fields: &mut FnvSet<String>,
new_name: Spanned<Symbol, BytePos>,
) -> bool {
let span = new_name.span;
duplicated_fields
.replace(new_name.value)
.replace(new_name.value.declared_name().to_string())
.map_or(true, |name| {
self.errors.push(Spanned {
span: span,
Expand Down
10 changes: 10 additions & 0 deletions check/tests/fail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -561,4 +561,14 @@ fn record_base_not_record() {
"#;
let result = support::typecheck(text);
assert_unify_err!(result, TypeMismatch(..));
}

#[test]
fn duplicate_fields_from_record_base() {
let _ = ::env_logger::init();
let text = r#"
{ x = 1 .. { y = "", x = "" } }
"#;
let result = support::typecheck(text);
assert_err!(result, DuplicateField(..));
}
21 changes: 20 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
//! on how to write gluon programs as well as how to run them using this library.
#![doc(html_root_url = "https://docs.rs/gluon/0.6.1")] // # GLUON

#[cfg(test)]
extern crate env_logger;

extern crate futures;
#[macro_use]
extern crate log;
Expand Down Expand Up @@ -548,7 +551,7 @@ let __implicit_string = import! "std/string.glu"
and { eq = { (==) } } = __implicit_string
and { (<), (<=), (>=), (>) } = __implicit_prelude.make_Ord __implicit_string.ord
in 0
in ()
"#;

pub fn filename_to_module(filename: &str) -> StdString {
Expand Down Expand Up @@ -591,3 +594,19 @@ fn load_regex(vm: &Thread) {
}
#[cfg(not(feature = "regex"))]
fn load_regex(_: &Thread) {}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn implicit_prelude() {
let _ = ::env_logger::init();

let thread = new_vm();
Compiler::new()
.implicit_prelude(false)
.run_expr::<()>(&thread, "prelude", PRELUDE)
.unwrap_or_else(|err| panic!("{}", err));
}
}
4 changes: 2 additions & 2 deletions std/string.glu
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ let semigroup : Semigroup String = { append = string_prim.append }

let monoid : Monoid String = { semigroup, empty = "" }

let eq : Eq String = { (==) = string_prim.eq }
let eq : Eq String = { (==) = string_eq }

let ord : Ord String = { eq, compare = string_prim.compare }
let ord : Ord String = { eq, compare = string_compare }

let show : Show String = { show = prelude.id }

Expand Down
2 changes: 1 addition & 1 deletion tests/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ Value::Tag(0)

test_expr!{ any marshalled_ordering_is_int,
r#"
string_prim.compare "a" "b"
string_compare "a" "b"
"#,
Value::Tag(0)
}
Expand Down
81 changes: 45 additions & 36 deletions vm/src/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,34 +168,32 @@ mod string {
let mut context = thread.context();
let value = StackFrame::current(&mut context.stack)[0];
match value {
Value::Array(array) => {
match GcStr::from_utf8(array) {
Ok(string) => {
let value = Value::String(string);
let result = context.alloc_with(
thread,
Def {
tag: 1,
elems: &[value],
},
);
match result {
Ok(data) => {
context.stack.push(Value::Data(data));
Status::Ok
}
Err(err) => {
let result: RuntimeResult<(), _> = RuntimeResult::Panic(err);
result.status_push(thread, &mut context)
}
Value::Array(array) => match GcStr::from_utf8(array) {
Ok(string) => {
let value = Value::String(string);
let result = context.alloc_with(
thread,
Def {
tag: 1,
elems: &[value],
},
);
match result {
Ok(data) => {
context.stack.push(Value::Data(data));
Status::Ok
}
Err(err) => {
let result: RuntimeResult<(), _> = RuntimeResult::Panic(err);
result.status_push(thread, &mut context)
}
}
Err(()) => {
}
Err(()) => {
let err: StdResult<&str, ()> = Err(());
err.status_push(thread, &mut context)
}
}
}
},
_ => unreachable!(),
}
}
Expand Down Expand Up @@ -346,6 +344,14 @@ pub fn load(vm: &Thread) -> Result<()> {

use self::string;
type string_prim = str;
vm.define_global(
"string_compare",
named_primitive!(2, "string_compare", string_prim::cmp),
)?;
vm.define_global(
"string_eq",
named_primitive!(2, "string_eq", <str as PartialEq>::eq),
)?;
vm.define_global(
"string_prim",
record! {
Expand All @@ -354,16 +360,25 @@ pub fn load(vm: &Thread) -> Result<()> {
split_at => primitive!(2 string_prim::split_at),
find => named_primitive!(2, "string_prim.find", string_prim::find::<&str>),
rfind => named_primitive!(2, "string_prim.rfind", string_prim::rfind::<&str>),
starts_with => named_primitive!(2, "string_prim.starts_with", string_prim::starts_with::<&str>),
ends_with => named_primitive!(2, "string_prim.ends_with", string_prim::ends_with::<&str>),
starts_with => named_primitive!(
2,
"string_prim.starts_with",
string_prim::starts_with::<&str>
),
ends_with => named_primitive!(
2,
"string_prim.ends_with",
string_prim::ends_with::<&str>
),
trim => primitive!(1 string_prim::trim),
trim_left => primitive!(1 string_prim::trim_left),
trim_right => primitive!(1 string_prim::trim_right),
compare => named_primitive!(2, "string_prim.compare", string_prim::cmp),
append => named_primitive!(2, "string_prim.append", string::append),
eq => named_primitive!(2, "string_prim.eq", <str as PartialEq>::eq),
slice => named_primitive!(3, "string_prim.slice", string::slice),
from_utf8 => primitive::<fn(Vec<u8>) -> StdResult<String, ()>>("string_prim.from_utf8", string::from_utf8),
from_utf8 => primitive::<fn(Vec<u8>) -> StdResult<String, ()>>(
"string_prim.from_utf8",
string::from_utf8
),
char_at => named_primitive!(2, "string_prim.char_at", string::char_at),
as_bytes => primitive!(1 string_prim::as_bytes)
},
Expand Down Expand Up @@ -399,18 +414,12 @@ pub fn load(vm: &Thread) -> Result<()> {

vm.define_global(
"#error",
primitive::<fn(StdString) -> Generic<A>>(
"#error",
prim::error,
),
primitive::<fn(StdString) -> Generic<A>>("#error", prim::error),
)?;

vm.define_global(
"error",
primitive::<fn(StdString) -> Generic<A>>(
"error",
prim::error,
),
primitive::<fn(StdString) -> Generic<A>>("error", prim::error),
)?;

::lazy::load(vm)?;
Expand Down

0 comments on commit 0973b26

Please sign in to comment.