diff --git a/dora-boots/assembler.dora b/dora-boots/assembler.dora index 378af4913..ea0261d9c 100644 --- a/dora-boots/assembler.dora +++ b/dora-boots/assembler.dora @@ -129,11 +129,10 @@ fn testAssemblerBufferEmitInt() { assert(buffer.size() == 4i64); } -pub trait RegisterType { +pub trait RegisterType: Equals { static fn fromInt32(value: Int32): Self; fn toInt32(): Int32; fn toLocation(): Location; - fn equals(other: Self): Bool; } pub struct Register(pub value: UInt8) @@ -167,10 +166,6 @@ impl RegisterType for Register { fn toLocation(): Location { Location::Reg(self) } - - fn equals(other: Register): Bool { - self.value == other.value - } } impl std::Stringable for Register { @@ -212,10 +207,6 @@ impl RegisterType for FloatRegister { fn toLocation(): Location { Location::FloatReg(self) } - - fn equals(other: FloatRegister): Bool { - self.value == other.value - } } diff --git a/dora-boots/regalloc.dora b/dora-boots/regalloc.dora index 4286bd048..fae2c2c31 100644 --- a/dora-boots/regalloc.dora +++ b/dora-boots/regalloc.dora @@ -668,7 +668,7 @@ impl SimpleRegisterAllocator { fn allocateFixedRegisterGeneric[T: RegisterType](dest: T, registers: RegisterTracker[T], inst: Inst) { let current = registers.getRegister(inst); - if current is Some(current) && current.equals(dest) { + if current is Some(current) && current == dest { registers.protect(dest); return; } @@ -763,7 +763,7 @@ impl[T: RegisterType] RegisterTracker[T] { let old_reg = self.locations.insert(value, reg); if old_reg is Some(old_reg) { if !self.protected.contains(old_reg) { - assert(!old_reg.equals(reg)); + assert(old_reg != reg); self.free.add(old_reg); self.data.free(old_reg); } @@ -864,7 +864,7 @@ impl[T: RegisterType] RegisterTracker[T] { if self.data.get(reg) is Some(inst) { let current_reg = self.locations.get(inst); - if current_reg.isNone() || (current_reg is Some(current_reg) && !reg.equals(current_reg)) { + if current_reg.isNone() || (current_reg is Some(current_reg) && reg != current_reg) { self.free.add(reg); self.data.free(reg); } diff --git a/dora-frontend/src/aliasck.rs b/dora-frontend/src/aliasck.rs index 4f63b148a..3f43be5c2 100644 --- a/dora-frontend/src/aliasck.rs +++ b/dora-frontend/src/aliasck.rs @@ -458,8 +458,8 @@ mod tests { } ", &[ - ((5, 47), ErrorMessage::UnexpectedTypeAliasAssignment), ((5, 29), ErrorMessage::UnexpectedWhere), + ((5, 47), ErrorMessage::UnexpectedTypeAliasAssignment), ], ); } diff --git a/dora-frontend/src/impldefck.rs b/dora-frontend/src/impldefck.rs index e5bbb7142..c8d61d9a4 100644 --- a/dora-frontend/src/impldefck.rs +++ b/dora-frontend/src/impldefck.rs @@ -537,6 +537,40 @@ fn check_type_aliases_bounds_inner(sa: &Sema, impl_: &ImplDefinition, trait_: &T } } +pub fn check_super_traits(sa: &Sema) { + for (_id, impl_) in sa.impls.iter() { + if let Some(trait_ty) = impl_.trait_ty() { + check_super_traits_for_bound(sa, impl_, trait_ty); + } + } +} + +fn check_super_traits_for_bound(sa: &Sema, impl_: &ImplDefinition, trait_ty: TraitType) { + let trait_ = sa.trait_(trait_ty.trait_id); + let type_param_definition = trait_.type_param_definition(); + + for bound in type_param_definition.bounds_for_self() { + if implements_trait( + sa, + impl_.extended_ty(), + impl_.type_param_definition(), + bound.clone(), + ) { + check_super_traits_for_bound(sa, impl_, bound); + } else { + let name = impl_ + .extended_ty() + .name_with_type_params(sa, impl_.type_param_definition()); + + let bound_name = bound.name_with_type_params(sa, trait_.type_param_definition()); + let msg = ErrorMessage::TypeNotImplementingTrait(name, bound_name); + sa.report(impl_.file_id, impl_.parsed_trait_ty().span(), msg); + + impl_.parsed_trait_ty().set_ty(None); + } + } +} + #[cfg(test)] mod tests { use crate::error::msg::ErrorMessage; @@ -1134,6 +1168,22 @@ mod tests { "); } + #[test] + fn impl_generic_extended_ty_with_trait_bound() { + err( + " + trait Foo[T] { fn get(): T; } + trait Bar {} + impl[T] Foo[T] for T where T: Bar { + fn get(): T { self } + } + fn f(x: Int64): Foo[Int64] { x as Foo[Int64] } + ", + (7, 42), + ErrorMessage::TypeNotImplementingTrait("Int64".into(), "Foo[Int64]".into()), + ); + } + #[test] fn impl_reuse_trait_implementation() { ok(" diff --git a/dora-frontend/src/lib.rs b/dora-frontend/src/lib.rs index 46cd407ba..5a49e68ea 100644 --- a/dora-frontend/src/lib.rs +++ b/dora-frontend/src/lib.rs @@ -13,7 +13,8 @@ pub use parsety::{ParsedTraitType, ParsedType, ParsedTypeAst}; pub use path::{parse_path, PathKind}; pub use program_emitter::emit_program; pub use specialize::{ - replace_type, specialize_for_element, specialize_type, specialize_type_array, + replace_type, specialize_for_element, specialize_trait_type, specialize_type, + specialize_type_array, }; pub(crate) mod access; @@ -82,6 +83,7 @@ pub fn check_program(sa: &mut Sema) -> bool { impldefck::check_definition(sa); impldefck::check_definition_against_trait(sa); impldefck::check_type_aliases_bounds(sa); + impldefck::check_super_traits(sa); enumck::check(sa); globaldefck::check(sa); extensiondefck::check(sa); @@ -296,16 +298,39 @@ pub mod tests { } pub fn errors(code: &'static str, vec: &[((u32, u32), ErrorMessage)]) { - test::check(code, |vm| { - let diag = vm.diag.borrow(); - let errors = diag.errors(); + test::check(code, |sa| { + let diag = sa.diag.borrow(); + let mut errors = diag.errors().to_vec(); + errors.sort_by_key(|e| e.span); + + println!("expected errors:"); + for error in vec { + println!("{}:{}: {}", error.0 .0, error.0 .1, error.1.message()); + } + println!(""); - println!("errors = {:?}", errors); - assert_eq!(vec.len(), errors.len()); + println!("actual errors:"); + for error in &errors { + println!("{}", error.message(sa)); + } + println!("\n"); + + assert_eq!( + vec.len(), + errors.len(), + "test expects {} errors but actually got {} errors.", + vec.len(), + errors.len() + ); for (ind, error) in errors.iter().enumerate() { + println!("compare error {}", ind); assert_eq!(Some(vec[ind].0), compute_pos(code, error)); - assert_eq!(vec[ind].1, error.msg); + assert_eq!( + vec[ind].1, error.msg, + "\nexpected: {:?}\n but got: {:?}", + vec[ind].1, error.msg + ); } }); } diff --git a/dora-frontend/src/parsety.rs b/dora-frontend/src/parsety.rs index 35c50b465..e49d87efd 100644 --- a/dora-frontend/src/parsety.rs +++ b/dora-frontend/src/parsety.rs @@ -83,6 +83,10 @@ impl ParsedTraitType { *self.ty.borrow_mut() = ty; } + pub fn span(&self) -> Span { + self.parsed_ast().expect("missing ast node").span + } + fn parsed_ast(&self) -> Option<&ParsedTypeAst> { self.parsed_ast.get().map(|ast| &**ast) } @@ -1009,7 +1013,7 @@ fn check_type_params( fn check_trait_type_param_definition( sa: &Sema, - element: &dyn Element, + _element: &dyn Element, trait_: &TraitDefinition, generic_arguments: &[SourceType], type_bindings: &[(AliasDefinitionId, SourceType)], @@ -1023,15 +1027,10 @@ fn check_trait_type_param_definition( let mut success = true; for bound in type_param_definition.bounds() { - let mut tp_ty = bound.ty(); + let tp_ty = bound.ty(); if tp_ty.is_self() { - if element.is_impl() { - let impl_ = element.to_impl().expect("impl expected"); - tp_ty = impl_.extended_ty(); - } else { - continue; - } + continue; } if let Some(trait_ty) = bound.trait_ty() { diff --git a/dora-frontend/src/sema.rs b/dora-frontend/src/sema.rs index 3d62c5980..905f22622 100644 --- a/dora-frontend/src/sema.rs +++ b/dora-frontend/src/sema.rs @@ -287,6 +287,12 @@ impl Sema { Location::new(line, column) } + pub fn debug_loc(&self, element: &dyn Element) -> String { + let file = self.file(element.file_id()); + let loc = self.compute_loc(element.file_id(), element.span()); + format!("{}:{}", file.path.display(), loc) + } + pub fn report(&self, file: SourceFileId, span: Span, msg: ErrorMessage) { self.diag.borrow_mut().report(file, span, msg); } diff --git a/dora-frontend/src/sema/aliases.rs b/dora-frontend/src/sema/aliases.rs index 927f9bdee..93f414d3f 100644 --- a/dora-frontend/src/sema/aliases.rs +++ b/dora-frontend/src/sema/aliases.rs @@ -14,6 +14,7 @@ use crate::sema::{ Element, ElementId, ImplDefinitionId, ModuleDefinitionId, PackageDefinitionId, Sema, SourceFileId, TraitDefinitionId, TypeParamDefinition, Visibility, }; +use crate::Span; use dora_parser::ast; pub type AliasDefinitionId = Id; @@ -128,6 +129,10 @@ impl Element for AliasDefinition { self.file_id } + fn span(&self) -> Span { + self.node.span + } + fn module_id(&self) -> ModuleDefinitionId { self.module_id } diff --git a/dora-frontend/src/sema/classes.rs b/dora-frontend/src/sema/classes.rs index 60a72b13a..8b3116df2 100644 --- a/dora-frontend/src/sema/classes.rs +++ b/dora-frontend/src/sema/classes.rs @@ -8,13 +8,12 @@ use id_arena::Id; use crate::interner::Name; use crate::program_parser::ParsedModifierList; use dora_parser::ast; -use dora_parser::Span; use crate::sema::{ module_path, Element, ElementAccess, ElementId, ExtensionDefinitionId, FctDefinitionId, ModuleDefinitionId, PackageDefinitionId, Sema, SourceFileId, TypeParamDefinition, }; -use crate::{specialize_for_element, ParsedType, SourceType, SourceTypeArray}; +use crate::{specialize_for_element, ParsedType, SourceType, SourceTypeArray, Span}; pub type ClassDefinitionId = Id; @@ -191,6 +190,10 @@ impl Element for ClassDefinition { self.file_id.expect("missing file_id") } + fn span(&self) -> Span { + self.span() + } + fn module_id(&self) -> ModuleDefinitionId { self.module_id } diff --git a/dora-frontend/src/sema/consts.rs b/dora-frontend/src/sema/consts.rs index 4e8277306..deb3ebcee 100644 --- a/dora-frontend/src/sema/consts.rs +++ b/dora-frontend/src/sema/consts.rs @@ -86,6 +86,10 @@ impl Element for ConstDefinition { self.file_id } + fn span(&self) -> Span { + self.span + } + fn module_id(&self) -> ModuleDefinitionId { self.module_id } diff --git a/dora-frontend/src/sema/elements.rs b/dora-frontend/src/sema/elements.rs index f0d8e27f4..9ce19b007 100644 --- a/dora-frontend/src/sema/elements.rs +++ b/dora-frontend/src/sema/elements.rs @@ -1,15 +1,13 @@ use std::rc::Rc; -use crate::{ - sema::{ - AliasDefinition, AliasDefinitionId, AliasParent, ClassDefinitionId, ConstDefinitionId, - EnumDefinitionId, ExtensionDefinitionId, FctDefinition, FctDefinitionId, FctParent, - GlobalDefinitionId, ImplDefinition, ImplDefinitionId, ModuleDefinitionId, - PackageDefinitionId, Sema, SourceFileId, StructDefinitionId, TraitDefinition, - TraitDefinitionId, TypeParamDefinition, UseDefinitionId, - }, - ty::SourceType, +use crate::sema::{ + AliasDefinition, AliasDefinitionId, AliasParent, ClassDefinitionId, ConstDefinitionId, + EnumDefinitionId, ExtensionDefinitionId, FctDefinition, FctDefinitionId, FctParent, + GlobalDefinitionId, ImplDefinition, ImplDefinitionId, ModuleDefinitionId, PackageDefinitionId, + Sema, SourceFileId, StructDefinitionId, TraitDefinition, TraitDefinitionId, + TypeParamDefinition, UseDefinitionId, }; +use crate::{SourceType, Span}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ElementId { @@ -30,6 +28,7 @@ pub trait Element { fn element_id(&self) -> ElementId; fn file_id(&self) -> SourceFileId; + fn span(&self) -> Span; fn module_id(&self) -> ModuleDefinitionId; fn package_id(&self) -> PackageDefinitionId; fn type_param_definition(&self) -> Option<&Rc>; diff --git a/dora-frontend/src/sema/enums.rs b/dora-frontend/src/sema/enums.rs index 60874f022..4ea9649dd 100644 --- a/dora-frontend/src/sema/enums.rs +++ b/dora-frontend/src/sema/enums.rs @@ -120,6 +120,10 @@ impl Element for EnumDefinition { self.file_id } + fn span(&self) -> Span { + self.span + } + fn module_id(&self) -> ModuleDefinitionId { self.module_id } diff --git a/dora-frontend/src/sema/extensions.rs b/dora-frontend/src/sema/extensions.rs index 35eabd5bb..f0c60d63f 100644 --- a/dora-frontend/src/sema/extensions.rs +++ b/dora-frontend/src/sema/extensions.rs @@ -85,6 +85,10 @@ impl Element for ExtensionDefinition { self.file_id } + fn span(&self) -> Span { + self.span + } + fn module_id(&self) -> ModuleDefinitionId { self.module_id } diff --git a/dora-frontend/src/sema/functions.rs b/dora-frontend/src/sema/functions.rs index 3c494c09b..3545d9363 100644 --- a/dora-frontend/src/sema/functions.rs +++ b/dora-frontend/src/sema/functions.rs @@ -252,6 +252,10 @@ impl Element for FctDefinition { self.file_id } + fn span(&self) -> Span { + self.span + } + fn module_id(&self) -> ModuleDefinitionId { self.module_id } diff --git a/dora-frontend/src/sema/globals.rs b/dora-frontend/src/sema/globals.rs index c54c43bbf..785176a6b 100644 --- a/dora-frontend/src/sema/globals.rs +++ b/dora-frontend/src/sema/globals.rs @@ -102,6 +102,10 @@ impl Element for GlobalDefinition { self.file_id } + fn span(&self) -> Span { + self.span + } + fn module_id(&self) -> ModuleDefinitionId { self.module_id } diff --git a/dora-frontend/src/sema/impls.rs b/dora-frontend/src/sema/impls.rs index 0036f85e2..6fd698e3d 100644 --- a/dora-frontend/src/sema/impls.rs +++ b/dora-frontend/src/sema/impls.rs @@ -128,6 +128,10 @@ impl Element for ImplDefinition { self.file_id } + fn span(&self) -> Span { + self.span + } + fn module_id(&self) -> ModuleDefinitionId { self.module_id } diff --git a/dora-frontend/src/sema/structs.rs b/dora-frontend/src/sema/structs.rs index a3b9370ec..238e9296b 100644 --- a/dora-frontend/src/sema/structs.rs +++ b/dora-frontend/src/sema/structs.rs @@ -144,6 +144,10 @@ impl Element for StructDefinition { self.file_id } + fn span(&self) -> Span { + self.span + } + fn module_id(&self) -> ModuleDefinitionId { self.module_id } diff --git a/dora-frontend/src/sema/traits.rs b/dora-frontend/src/sema/traits.rs index e3d14e8a6..d76cdc1ef 100644 --- a/dora-frontend/src/sema/traits.rs +++ b/dora-frontend/src/sema/traits.rs @@ -133,6 +133,10 @@ impl Element for TraitDefinition { self.file_id } + fn span(&self) -> Span { + self.span + } + fn module_id(&self) -> ModuleDefinitionId { self.module_id } @@ -198,6 +202,13 @@ pub fn is_object_safe(sa: &Sema, trait_id: TraitDefinitionId) -> bool { } } + let type_param_definition = trait_.type_param_definition(); + for bound in type_param_definition.bounds_for_self() { + if !is_object_safe(sa, bound.trait_id) { + return false; + } + } + true } diff --git a/dora-frontend/src/specialize.rs b/dora-frontend/src/specialize.rs index fc3d82ddb..f3552998b 100644 --- a/dora-frontend/src/specialize.rs +++ b/dora-frontend/src/specialize.rs @@ -1,5 +1,17 @@ use crate::sema::{Element, Sema}; -use crate::{SourceType, SourceTypeArray}; +use crate::{SourceType, SourceTypeArray, TraitType}; + +pub fn specialize_trait_type(sa: &Sema, ty: TraitType, type_params: &SourceTypeArray) -> TraitType { + TraitType { + trait_id: ty.trait_id, + type_params: replace_sta(sa, ty.type_params, Some(type_params), None), + bindings: ty + .bindings + .into_iter() + .map(|(id, ty)| (id, replace_type(sa, ty, Some(type_params), None))) + .collect(), + } +} pub fn specialize_type(sa: &Sema, ty: SourceType, type_params: &SourceTypeArray) -> SourceType { replace_type(sa, ty, Some(type_params), None) diff --git a/dora-frontend/src/traitdefck.rs b/dora-frontend/src/traitdefck.rs index 52621d295..ed2955663 100644 --- a/dora-frontend/src/traitdefck.rs +++ b/dora-frontend/src/traitdefck.rs @@ -31,13 +31,13 @@ mod tests { } #[test] - #[ignore] fn trait_method_using_another_trait_method_generic() { ok(" - trait Foo[T] { - fn foo(): Int64; - fn bar(): Int64 { self.foo() } - }"); + trait Foo[T] { + fn foo(): Int64; + fn bar(): Int64 { self.foo() } + } + "); } #[test] @@ -476,13 +476,51 @@ mod tests { } #[test] - #[ignore] fn check_super_trait_on_generic_impl() { + err( + " + trait A {} + trait B: A {} + class Foo[T](value: T) + impl[T] B for Foo[T] {} + ", + (5, 21), + ErrorMessage::TypeNotImplementingTrait("Foo[T]".into(), "A".into()), + ); + ok(" trait A {} trait B: A {} class Foo[T](value: T) impl[T] B for Foo[T] {} - ") + impl[T] A for Foo[T] {} + "); + } + + #[test] + fn super_trait_object_safe() { + ok(" + trait A { + fn f(); + } + trait B: A { + fn g(); + } + fn f(b: B) {} + "); + + err( + " + trait A { + static fn f(); + } + trait B: A { + fn g(); + } + fn f(b: B) {} + ", + (8, 21), + ErrorMessage::TraitNotObjectSafe, + ); } } diff --git a/dora-frontend/src/ty.rs b/dora-frontend/src/ty.rs index 28ef15838..8128914f8 100644 --- a/dora-frontend/src/ty.rs +++ b/dora-frontend/src/ty.rs @@ -1099,7 +1099,7 @@ impl TraitType { } pub fn implements_trait(&self, sa: &Sema, check_trait_ty: &TraitType) -> bool { - if check_trait_ty == self { + if self == check_trait_ty { return true; } diff --git a/dora-frontend/src/typeck/tests.rs b/dora-frontend/src/typeck/tests.rs index cd34e5998..b4a5b4fd8 100644 --- a/dora-frontend/src/typeck/tests.rs +++ b/dora-frontend/src/typeck/tests.rs @@ -4662,8 +4662,8 @@ fn impl_method_lookup_on_missing_trait_method() { } ", &[ - ((15, 13), ErrorMessage::ElementNotInTrait), ((14, 9), ErrorMessage::ElementNotInImpl("h".into())), + ((15, 13), ErrorMessage::ElementNotInTrait), ( (21, 13), ErrorMessage::UnknownMethod("Int64".into(), "h".into(), Vec::new()), diff --git a/dora-parser/src/span.rs b/dora-parser/src/span.rs index 58da0b6ac..3506222a2 100644 --- a/dora-parser/src/span.rs +++ b/dora-parser/src/span.rs @@ -1,7 +1,7 @@ use std::fmt::{Display, Error, Formatter}; use std::result::Result; -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Span { start: u32, len: u32,