diff --git a/dora-frontend/src/error/msg.rs b/dora-frontend/src/error/msg.rs index b412b380d..6c87458c6 100644 --- a/dora-frontend/src/error/msg.rs +++ b/dora-frontend/src/error/msg.rs @@ -193,6 +193,8 @@ pub enum ErrorMessage { TraitNotObjectSafe, UnexpectedTypeBinding, WrongOrderOfGenericsAndBindings, + UnknownTypeBinding, + TypeBindingDefinedAgain, } impl ErrorMessage { @@ -662,9 +664,13 @@ impl ErrorMessage { } ErrorMessage::TraitNotObjectSafe => format!("Trait not object safe"), ErrorMessage::UnexpectedTypeBinding => format!("Type binding not allowed here."), + ErrorMessage::UnknownTypeBinding => format!("Type binding not allowed here."), ErrorMessage::WrongOrderOfGenericsAndBindings => { format!("Generic arguments should be ordered before type bindings.") } + ErrorMessage::TypeBindingDefinedAgain => { + format!("Type binding for this name already exists.") + } } } } diff --git a/dora-frontend/src/generator/expr.rs b/dora-frontend/src/generator/expr.rs index f46f7c717..f908b9a72 100644 --- a/dora-frontend/src/generator/expr.rs +++ b/dora-frontend/src/generator/expr.rs @@ -176,7 +176,7 @@ fn is_comparable_method(sa: &Sema, fct: &FctDefinition) -> bool { match fct.parent { FctParent::Impl(impl_id) => { let impl_ = &sa.impl_(impl_id); - impl_.trait_id() == sa.known.traits.comparable() + impl_.trait_id().expect("trait expected") == sa.known.traits.comparable() } FctParent::Trait(trait_id) => trait_id == sa.known.traits.comparable(), diff --git a/dora-frontend/src/impldefck.rs b/dora-frontend/src/impldefck.rs index 5e61a704a..1331e412c 100644 --- a/dora-frontend/src/impldefck.rs +++ b/dora-frontend/src/impldefck.rs @@ -5,7 +5,7 @@ use crate::sema::{ implements_trait, new_identity_type_params, AliasDefinitionId, FctDefinition, FctDefinitionId, ImplDefinition, Sema, TraitDefinition, }; -use crate::{package_for_type, ErrorMessage, SourceType, SourceTypeArray}; +use crate::{package_for_type, ErrorMessage, SourceType, SourceTypeArray, TraitType}; pub fn check_definition(sa: &Sema) { for (_id, impl_) in sa.impls.iter() { @@ -14,10 +14,6 @@ pub fn check_definition(sa: &Sema) { } fn check_impl_definition(sa: &Sema, impl_: &ImplDefinition) { - if !impl_.trait_ty().is_trait() && !impl_.trait_ty().is_error() { - sa.report(impl_.file_id, impl_.ast.span, ErrorMessage::ExpectedTrait); - } - match impl_.extended_ty() { SourceType::TypeAlias(..) => unimplemented!(), SourceType::Any | SourceType::Ptr | SourceType::This => { @@ -49,8 +45,9 @@ fn check_impl_definition(sa: &Sema, impl_: &ImplDefinition) { impl_.ast.span, ); - if impl_.trait_ty().is_trait() && !impl_.extended_ty().is_error() { - let is_trait_foreign = package_for_type(sa, impl_.trait_ty()) != Some(impl_.package_id); + if impl_.trait_ty().is_some() && !impl_.extended_ty().is_error() { + let trait_id = impl_.trait_id().expect("expected trait"); + let is_trait_foreign = sa.trait_(trait_id).package_id != impl_.package_id; let is_extended_ty_foreign = package_for_type(sa, impl_.extended_ty()) != Some(impl_.package_id); @@ -66,16 +63,19 @@ fn check_impl_definition(sa: &Sema, impl_: &ImplDefinition) { pub fn check_definition_against_trait(sa: &Sema) { for (_id, impl_) in sa.impls.iter() { - let trait_ty = impl_.trait_ty(); - - if let Some(trait_id) = trait_ty.trait_id() { - let trait_ = sa.trait_(trait_id); - check_impl_methods(sa, impl_, trait_); + if let Some(trait_ty) = impl_.trait_ty() { + let trait_ = sa.trait_(trait_ty.trait_id); + check_impl_methods(sa, impl_, &trait_ty, trait_); } } } -fn check_impl_methods(sa: &Sema, impl_: &ImplDefinition, trait_: &TraitDefinition) { +fn check_impl_methods( + sa: &Sema, + impl_: &ImplDefinition, + trait_ty: &TraitType, + trait_: &TraitDefinition, +) { let mut remaining_trait_methods: HashSet = trait_.methods().iter().cloned().collect(); let mut trait_method_map = HashMap::new(); @@ -108,7 +108,7 @@ fn check_impl_methods(sa: &Sema, impl_: &ImplDefinition, trait_: &TraitDefinitio if !method_definitions_compatible( sa, trait_method, - impl_.trait_ty().type_params(), + trait_ty.type_params.clone(), &trait_alias_map, impl_method, impl_.extended_ty().clone(), @@ -365,10 +365,8 @@ fn report_missing_methods( pub fn check_type_aliases(sa: &Sema) { for (_id, impl_) in sa.impls.iter() { - let trait_ty = impl_.trait_ty(); - - if let Some(trait_id) = trait_ty.trait_id() { - let trait_ = sa.trait_(trait_id); + if let Some(trait_ty) = impl_.trait_ty() { + let trait_ = sa.trait_(trait_ty.trait_id); check_impl_types(sa, impl_, trait_); } } @@ -397,20 +395,22 @@ fn check_impl_types(sa: &Sema, impl_: &ImplDefinition, trait_: &TraitDefinition) let trait_alias = sa.alias(trait_alias_id); for bound in trait_alias.bounds() { - if !implements_trait( - sa, - impl_alias.ty(), - impl_.type_param_definition(), - bound.ty(), - ) { - let name = impl_alias - .ty() - .name_with_type_params(sa, impl_.type_param_definition()); - let trait_name = bound - .ty() - .name_with_type_params(sa, trait_.type_param_definition()); - let msg = ErrorMessage::TypeNotImplementingTrait(name, trait_name); - sa.report(impl_.file_id, impl_alias.node.span, msg); + if let Some(trait_ty) = bound.ty() { + if !implements_trait( + sa, + impl_alias.ty(), + impl_.type_param_definition(), + trait_ty.ty(), + ) { + let name = impl_alias + .ty() + .name_with_type_params(sa, impl_.type_param_definition()); + let trait_name = trait_ty + .ty() + .name_with_type_params(sa, trait_.type_param_definition()); + let msg = ErrorMessage::TypeNotImplementingTrait(name, trait_name); + sa.report(impl_.file_id, impl_alias.node.span, msg); + } } } diff --git a/dora-frontend/src/lib.rs b/dora-frontend/src/lib.rs index 8b1cb98da..b9f1944f5 100644 --- a/dora-frontend/src/lib.rs +++ b/dora-frontend/src/lib.rs @@ -2,13 +2,13 @@ pub use crate::error::msg::ErrorMessage; use crate::interner::Name; use crate::sema::{Sema, SourceFileId}; use crate::sym::{ModuleSymTable, SymTable, Symbol, SymbolKind}; -use crate::ty::{contains_self, SourceType, SourceTypeArray}; +use crate::ty::{contains_self, SourceType, SourceTypeArray, TraitType}; use dora_bytecode::{dump_stdout, Program}; use dora_parser::ast; use dora_parser::Span; pub use crate::extensiondefck::package_for_type; -pub use parsety::{ParsedType, ParsedTypeAst}; +pub use parsety::{ParsedTraitType, ParsedType, ParsedTypeAst}; pub use program_emitter::emit_program; pub use readty::check_type_params; pub use specialize::{replace_type, specialize_type}; diff --git a/dora-frontend/src/parsety.rs b/dora-frontend/src/parsety.rs index 363fb0dbd..376de7a0a 100644 --- a/dora-frontend/src/parsety.rs +++ b/dora-frontend/src/parsety.rs @@ -1,12 +1,15 @@ use dora_parser::ast; -use crate::access::sym_accessible_from; +use crate::access::{sym_accessible_from, trait_accessible_from}; use crate::readty::read_type_path; use crate::sema::{ - is_object_safe, ModuleDefinitionId, SourceFileId, TraitDefinitionId, TypeParamDefinition, + is_object_safe, AliasDefinitionId, ModuleDefinitionId, SourceFileId, TraitDefinitionId, + TypeParamDefinition, }; use crate::sym::{ModuleSymTable, SymbolKind}; -use crate::{check_type_params, ErrorMessage, Name, Sema, SourceType, SourceTypeArray, Span}; +use crate::{ + check_type_params, ErrorMessage, Name, Sema, SourceType, SourceTypeArray, Span, TraitType, +}; use std::cell::{OnceCell, RefCell}; use std::collections::HashSet; @@ -55,6 +58,35 @@ impl ParsedType { } } +#[derive(Clone, Debug)] +pub struct ParsedTraitType { + ast: Option, + parsed_ast: OnceCell>, + ty: RefCell>, +} + +impl ParsedTraitType { + pub fn new_ast(ast: ast::Type) -> ParsedTraitType { + ParsedTraitType { + ast: Some(ast), + parsed_ast: OnceCell::new(), + ty: RefCell::new(None), + } + } + + pub fn ty(&self) -> Option { + self.ty.borrow().as_ref().cloned() + } + + pub fn set_ty(&self, ty: Option) { + *self.ty.borrow_mut() = ty; + } + + fn parsed_ast(&self) -> Option<&ParsedTypeAst> { + self.parsed_ast.get().map(|ast| &**ast) + } +} + #[derive(Clone, Debug)] pub struct ParsedTypeAst { #[allow(unused)] @@ -106,6 +138,36 @@ pub fn parse_type( } } +pub fn parse_trait_type( + sa: &Sema, + table: &ModuleSymTable, + file_id: SourceFileId, + parsed_ty: &ParsedTraitType, +) { + let node = parsed_ty.ast.as_ref().expect("missing ast node"); + let parsed_ast = parse_type_inner(sa, table, file_id, node); + assert!(parsed_ty.parsed_ast.set(parsed_ast).is_ok()); + + let parsed_ast = parsed_ty.parsed_ast().expect("missing ast"); + + match &parsed_ast.kind { + ParsedTypeKind::Regular { + symbol: SymbolKind::Trait(trait_id), + type_arguments, + } => { + let trait_ty = convert_trait_type(sa, file_id, &parsed_ast, *trait_id, &type_arguments); + parsed_ty.set_ty(trait_ty); + } + + ParsedTypeKind::Error => {} + + _ => { + let msg = ErrorMessage::BoundExpected; + sa.report(file_id, parsed_ast.span, msg); + } + } +} + fn parse_type_inner( sa: &Sema, table: &ModuleSymTable, @@ -344,7 +406,9 @@ fn convert_type_regular(sa: &Sema, file_id: SourceFileId, parsed_ty: &ParsedType } SymbolKind::Trait(trait_id) => { - convert_type_trait(sa, file_id, parsed_ty, trait_id, type_params) + convert_trait_type(sa, file_id, parsed_ty, trait_id, type_params) + .map(|t| t.ty()) + .unwrap_or(SourceType::Error) } SymbolKind::Class(..) | SymbolKind::Enum(..) | SymbolKind::Struct(..) => { @@ -364,17 +428,17 @@ fn convert_type_regular(sa: &Sema, file_id: SourceFileId, parsed_ty: &ParsedType } } -fn convert_type_trait( +fn convert_trait_type( sa: &Sema, file_id: SourceFileId, _parsed_ty: &ParsedTypeAst, trait_id: TraitDefinitionId, type_params: &Vec, -) -> SourceType { +) -> Option { let trait_ = sa.trait_(trait_id); let mut idx = 0; let mut generics = Vec::new(); - let mut bindings = Vec::new(); + let mut bindings: Vec<(AliasDefinitionId, SourceType)> = Vec::new(); while idx < type_params.len() { let type_param = &type_params[idx]; @@ -394,23 +458,30 @@ fn convert_type_trait( let type_param = &type_params[idx]; let name = type_param.name.expect("name expected"); - if let Some(alias_id) = trait_.alias_names().get(&name) { + if let Some(&alias_id) = trait_.alias_names().get(&name) { if used_aliases.insert(alias_id) { let ty = convert_type_inner(sa, file_id, &type_param.ty); bindings.push((alias_id, ty)); } else { - unimplemented!() + let msg = ErrorMessage::TypeBindingDefinedAgain; + sa.report(file_id, type_param.span, msg); + return None; } } else { - let msg = ErrorMessage::UnexpectedTypeBinding; + let msg = ErrorMessage::UnknownTypeBinding; sa.report(file_id, type_param.span, msg); + return None; } idx += 1; } let type_params = SourceTypeArray::with(generics); - SourceType::Trait(trait_id, type_params) + Some(TraitType { + trait_id, + type_params, + bindings, + }) } fn sym_type_param_definition(sa: &Sema, sym: SymbolKind) -> &TypeParamDefinition { @@ -651,45 +722,69 @@ fn check_type_record( } } -pub fn check_trait_type(sa: &Sema, ctxt: &TypeContext, parsed_ty: &ParsedType) -> SourceType { +pub fn check_trait_type(sa: &Sema, ctxt: &TypeContext, parsed_ty: &ParsedTraitType) { let parsed_ty_ast = parsed_ty.parsed_ast().expect("missing ast node"); - let new_ty = check_trait_type_inner(sa, ctxt, parsed_ty.ty(), parsed_ty_ast); - parsed_ty.set_ty(new_ty.clone()); - new_ty + + if let Some(trait_ty) = parsed_ty.ty() { + let new_ty = check_trait_type_inner(sa, ctxt, trait_ty, parsed_ty_ast); + parsed_ty.set_ty(new_ty); + } } fn check_trait_type_inner( sa: &Sema, ctxt: &TypeContext, - ty: SourceType, + trait_ty: TraitType, parsed_ty: &ParsedTypeAst, -) -> SourceType { - match ty.clone() { - SourceType::Any | SourceType::Ptr => { - unreachable!() - } - SourceType::Error => SourceType::Error, - SourceType::This - | SourceType::TypeAlias(..) - | SourceType::Unit - | SourceType::TypeParam(..) - | SourceType::Bool - | SourceType::UInt8 - | SourceType::Char - | SourceType::Float32 - | SourceType::Float64 - | SourceType::Int32 - | SourceType::Int64 - | SourceType::Lambda(..) - | SourceType::Tuple(..) - | SourceType::Class(..) - | SourceType::Struct(..) - | SourceType::Enum(..) => { - let msg = ErrorMessage::BoundExpected; - sa.report(ctxt.file_id, parsed_ty.span, msg); - SourceType::Error - } - SourceType::Trait(_, type_params) => check_type_record(sa, ctxt, parsed_ty, type_params), +) -> Option { + let (symbol, parsed_type_params) = match parsed_ty.kind { + ParsedTypeKind::Regular { + ref symbol, + type_arguments: ref type_params, + .. + } => (symbol.clone(), type_params), + _ => unreachable!(), + }; + + if !trait_accessible_from(sa, trait_ty.trait_id, ctxt.module_id) { + let msg = ErrorMessage::NotAccessible; + sa.report(ctxt.file_id, parsed_ty.span, msg); + } + + assert_eq!(trait_ty.type_params.len(), parsed_type_params.len()); + assert!(trait_ty.bindings.is_empty()); + let mut new_type_params = Vec::with_capacity(parsed_type_params.len()); + + for idx in 0..trait_ty.type_params.len() { + let parsed_type_arg = &parsed_type_params[idx]; + assert!(parsed_type_arg.name.is_none()); + let ty = check_type_inner( + sa, + ctxt, + trait_ty.type_params[idx].clone(), + &parsed_type_arg.ty, + ); + new_type_params.push(ty); + } + + let new_type_params = SourceTypeArray::with(new_type_params); + let type_param_defs = sym_type_param_definition(sa, symbol.clone()); + + if check_type_params( + sa, + type_param_defs, + new_type_params.types(), + ctxt.file_id, + parsed_ty.span, + ctxt.type_param_definition, + ) { + Some(TraitType { + trait_id: trait_ty.trait_id, + type_params: new_type_params, + bindings: trait_ty.bindings, + }) + } else { + None } } @@ -703,6 +798,23 @@ pub fn expand_type( new_ty } +pub fn expand_trait_type(sa: &Sema, parsed_ty: &ParsedTraitType, replace_self: Option) { + if let Some(trait_ty) = parsed_ty.ty() { + let new_type_params = expand_sta(sa, trait_ty.type_params, replace_self.clone()); + let new_bindings = trait_ty + .bindings + .into_iter() + .map(|(id, ty)| (id, expand_st(sa, ty, replace_self.clone()))) + .collect::>(); + let new_trait_ty = TraitType { + trait_id: trait_ty.trait_id, + type_params: new_type_params, + bindings: new_bindings, + }; + parsed_ty.set_ty(Some(new_trait_ty)); + } +} + fn expand_st(sa: &Sema, ty: SourceType, replace_self: Option) -> SourceType { match ty { SourceType::Class(cls_id, type_params) => { @@ -798,15 +910,28 @@ mod tests { } #[test] - #[ignore] fn trait_type_with_unknown_named_arg() { err( " trait Foo[T] {} fn f(x: Foo[Float32, T = Int64]) {} ", - (3, 36), - ErrorMessage::Unimplemented, + (3, 34), + ErrorMessage::UnknownTypeBinding, + ) + } + + #[test] + fn trait_type_with_same_binding() { + err( + " + trait Foo[T] { + type T; + } + fn f(x: Foo[Float32, T = Int64, T = Int64]) {} + ", + (5, 45), + ErrorMessage::TypeBindingDefinedAgain, ) } } diff --git a/dora-frontend/src/program_emitter.rs b/dora-frontend/src/program_emitter.rs index 955bab1f5..6fa1b98a6 100644 --- a/dora-frontend/src/program_emitter.rs +++ b/dora-frontend/src/program_emitter.rs @@ -115,8 +115,8 @@ fn create_impls(sa: &Sema) -> Vec { for (_id, impl_) in sa.impls.iter() { let mut methods = Vec::new(); - let trait_id = impl_.trait_id(); - let trait_ = sa.trait_(trait_id); + let trait_ty = impl_.trait_ty().expect("trait expected"); + let trait_ = sa.trait_(trait_ty.trait_id); // The methods array for impl should have the exact same order as for the trait. for method_id in trait_.methods() { @@ -140,7 +140,7 @@ fn create_impls(sa: &Sema) -> Vec { result.push(ImplData { module_id: convert_module_id(impl_.module_id), type_params: create_type_params(sa, impl_.type_param_definition()), - trait_ty: bty_from_ty(impl_.trait_ty()), + trait_ty: bty_from_ty(trait_ty.ty()), extended_ty: bty_from_ty(impl_.extended_ty()), methods, trait_method_map, @@ -324,7 +324,7 @@ fn create_type_params(sa: &Sema, type_params: &TypeParamDefinition) -> TypeParam .bounds() .map(|b| TypeParamBound { ty: bty_from_ty(b.ty()), - trait_ty: bty_from_ty(b.trait_ty()), + trait_ty: bty_from_ty(b.trait_ty().expect("missing trait type").ty()), }) .collect(); diff --git a/dora-frontend/src/readty.rs b/dora-frontend/src/readty.rs index 47ce85ba3..bb0969c18 100644 --- a/dora-frontend/src/readty.rs +++ b/dora-frontend/src/readty.rs @@ -79,15 +79,17 @@ pub fn check_type_params( for bound in tp_definitions.bounds() { let tp_ty = bound.ty(); - let trait_ty = bound.trait_ty(); - let tp_ty = specialize_type(sa, tp_ty, &type_params_sta); - - if !implements_trait(sa, tp_ty.clone(), type_param_defs, trait_ty.clone()) { - let name = tp_ty.name_with_type_params(sa, type_param_defs); - let trait_name = trait_ty.name_with_type_params(sa, type_param_defs); - let msg = ErrorMessage::TypeNotImplementingTrait(name, trait_name); - sa.report(file_id, span, msg); - success = false; + + if let Some(trait_ty) = bound.trait_ty() { + let tp_ty = specialize_type(sa, tp_ty, &type_params_sta); + + if !implements_trait(sa, tp_ty.clone(), type_param_defs, trait_ty.ty().clone()) { + let name = tp_ty.name_with_type_params(sa, type_param_defs); + let trait_name = trait_ty.ty().name_with_type_params(sa, type_param_defs); + let msg = ErrorMessage::TypeNotImplementingTrait(name, trait_name); + sa.report(file_id, span, msg); + success = false; + } } } diff --git a/dora-frontend/src/sema/aliases.rs b/dora-frontend/src/sema/aliases.rs index b8d7eebdb..e5855f775 100644 --- a/dora-frontend/src/sema/aliases.rs +++ b/dora-frontend/src/sema/aliases.rs @@ -1,4 +1,9 @@ -use crate::{interner::Name, program_parser::ParsedModifierList, ty::SourceType, ParsedType}; +use crate::{ + interner::Name, + program_parser::ParsedModifierList, + ty::{SourceType, TraitType}, + ParsedTraitType, ParsedType, +}; use std::cell::OnceCell; use std::sync::Arc; @@ -83,22 +88,22 @@ impl AliasDefinition { pub struct AliasBound { pub ty_ast: ast::Type, - pub parsed_ty: ParsedType, + pub parsed_ty: ParsedTraitType, } impl AliasBound { pub fn new(ast: ast::Type) -> AliasBound { AliasBound { ty_ast: ast.clone(), - parsed_ty: ParsedType::new_ast(ast), + parsed_ty: ParsedTraitType::new_ast(ast), } } - pub fn ty(&self) -> SourceType { + pub fn ty(&self) -> Option { self.parsed_ty().ty() } - pub fn parsed_ty(&self) -> &ParsedType { + pub fn parsed_ty(&self) -> &ParsedTraitType { &self.parsed_ty } } diff --git a/dora-frontend/src/sema/extensions.rs b/dora-frontend/src/sema/extensions.rs index c9e03a3a1..a5e980863 100644 --- a/dora-frontend/src/sema/extensions.rs +++ b/dora-frontend/src/sema/extensions.rs @@ -200,7 +200,7 @@ pub mod matching { let check_tp_id = check_ty.type_param_id().expect("expected type param"); for trait_ty in ext_type_param_defs.bounds_for_type_param(ext_tp_id) { - if !check_type_param_defs.implements_trait(check_tp_id, trait_ty) { + if !check_type_param_defs.implements_trait(check_tp_id, trait_ty.ty()) { return false; } } @@ -216,7 +216,7 @@ pub mod matching { ext_type_param_defs: &TypeParamDefinition, ) -> bool { for trait_ty in ext_type_param_defs.bounds_for_type_param(ext_tp_id) { - if !implements_trait(sa, check_ty.clone(), check_type_param_defs, trait_ty) { + if !implements_trait(sa, check_ty.clone(), check_type_param_defs, trait_ty.ty()) { return false; } } diff --git a/dora-frontend/src/sema/impls.rs b/dora-frontend/src/sema/impls.rs index 7ea4804be..8ba139642 100644 --- a/dora-frontend/src/sema/impls.rs +++ b/dora-frontend/src/sema/impls.rs @@ -11,7 +11,7 @@ use crate::sema::{ PackageDefinitionId, Sema, SourceFileId, TraitDefinitionId, TypeParamDefinition, }; use crate::ty::{SourceType, SourceTypeArray}; -use crate::ParsedType; +use crate::{ParsedTraitType, ParsedType, TraitType}; use id_arena::Id; pub type ImplDefinitionId = Id; @@ -26,7 +26,7 @@ pub struct ImplDefinition { pub declaration_span: Span, pub span: Span, pub type_param_definition: Rc, - pub parsed_trait_ty: ParsedType, + pub parsed_trait_ty: ParsedTraitType, pub parsed_extended_ty: ParsedType, pub methods: OnceCell>, pub aliases: OnceCell>, @@ -51,7 +51,7 @@ impl ImplDefinition { type_param_definition, declaration_span: node.declaration_span, span: node.span, - parsed_trait_ty: ParsedType::new_ast( + parsed_trait_ty: ParsedTraitType::new_ast( node.trait_type .as_ref() .expect("missing trait type") @@ -73,15 +73,15 @@ impl ImplDefinition { &self.type_param_definition } - pub fn trait_id(&self) -> TraitDefinitionId { - self.trait_ty().trait_id().expect("trait expected") + pub fn trait_id(&self) -> Option { + self.trait_ty().map(|t| t.trait_id) } - pub fn trait_ty(&self) -> SourceType { + pub fn trait_ty(&self) -> Option { self.parsed_trait_ty().ty() } - pub fn parsed_trait_ty(&self) -> &ParsedType { + pub fn parsed_trait_ty(&self) -> &ParsedTraitType { &self.parsed_trait_ty } @@ -201,16 +201,19 @@ pub fn find_impl( trait_ty: SourceType, ) -> Option { for (_id, impl_) in sa.impls.iter() { - if impl_.trait_ty() != trait_ty { - continue; - } - - if let Some(binding) = impl_matches(sa, check_ty.clone(), check_type_param_defs, impl_.id()) - { - return Some(ImplMatch { - id: impl_.id(), - binding, - }); + if let Some(impl_trait_ty) = impl_.trait_ty() { + if impl_trait_ty.ty() != trait_ty { + continue; + } + + if let Some(binding) = + impl_matches(sa, check_ty.clone(), check_type_param_defs, impl_.id()) + { + return Some(ImplMatch { + id: impl_.id(), + binding, + }); + } } } diff --git a/dora-frontend/src/sema/type_params.rs b/dora-frontend/src/sema/type_params.rs index e020b0b6b..ef6bc180a 100644 --- a/dora-frontend/src/sema/type_params.rs +++ b/dora-frontend/src/sema/type_params.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use dora_parser::ast; -use crate::{Name, ParsedType, SourceType, SourceTypeArray}; +use crate::{Name, ParsedTraitType, ParsedType, SourceType, SourceTypeArray, TraitType}; #[derive(Clone, Debug)] pub struct TypeParamDefinition { @@ -85,7 +85,7 @@ impl TypeParamDefinition { pub fn add_bound(&mut self, id: TypeParamId, ast_trait_ty: ast::Type) { let bound = Bound::new( ParsedType::new_ty(SourceType::TypeParam(id)), - ParsedType::new_ast(ast_trait_ty.clone()), + ParsedTraitType::new_ast(ast_trait_ty.clone()), ); self.bounds.push(bound); @@ -94,7 +94,7 @@ impl TypeParamDefinition { pub fn add_where_bound(&mut self, ast_ty: ast::Type, ast_trait_ty: ast::Type) { let bound = Bound::new( ParsedType::new_ast(ast_ty.clone()), - ParsedType::new_ast(ast_trait_ty.clone()), + ParsedTraitType::new_ast(ast_trait_ty.clone()), ); self.bounds.push(bound); @@ -102,8 +102,10 @@ impl TypeParamDefinition { pub fn implements_trait(&self, id: TypeParamId, trait_ty: SourceType) -> bool { for bound in self.bounds() { - if bound.ty() == SourceType::TypeParam(id) && bound.trait_ty() == trait_ty { - return true; + if let Some(bound_trait_ty) = bound.trait_ty() { + if bound.ty() == SourceType::TypeParam(id) && bound_trait_ty.ty() == trait_ty { + return true; + } } } false @@ -128,10 +130,10 @@ impl TypeParamDefinition { pub fn bounds_for_type_param<'a>( &'a self, id: TypeParamId, - ) -> impl Iterator + 'a { + ) -> impl Iterator + 'a { self.bounds() - .filter(move |b| b.ty() == SourceType::TypeParam(id)) - .map(|b| b.trait_ty()) + .filter(move |b| b.ty() == SourceType::TypeParam(id) && b.trait_ty().is_some()) + .map(|b| b.trait_ty().expect("trait type expected")) } pub fn type_param_count(&self) -> usize { @@ -154,11 +156,11 @@ impl TypeParamDefinition { #[derive(Clone, Debug)] pub struct Bound { parsed_ty: ParsedType, - parsed_trait_ty: ParsedType, + parsed_trait_ty: ParsedTraitType, } impl Bound { - pub fn new(parsed_ty: ParsedType, parsed_trait_ty: ParsedType) -> Bound { + pub fn new(parsed_ty: ParsedType, parsed_trait_ty: ParsedTraitType) -> Bound { Bound { parsed_ty, parsed_trait_ty, @@ -173,11 +175,11 @@ impl Bound { &self.parsed_ty } - pub fn trait_ty(&self) -> SourceType { + pub fn trait_ty(&self) -> Option { self.parsed_trait_ty().ty() } - pub fn parsed_trait_ty(&self) -> &ParsedType { + pub fn parsed_trait_ty(&self) -> &ParsedTraitType { &self.parsed_trait_ty } } diff --git a/dora-frontend/src/stdlib_lookup.rs b/dora-frontend/src/stdlib_lookup.rs index bf222cae2..a192fa36a 100644 --- a/dora-frontend/src/stdlib_lookup.rs +++ b/dora-frontend/src/stdlib_lookup.rs @@ -967,13 +967,14 @@ fn lookup_impl_for_item( extended_ty: SymbolKind, ) -> Option { for (id, impl_) in sa.impls.iter() { - match impl_.trait_ty() { - SourceType::Trait(impl_trait_id, ..) if impl_trait_id == trait_id => {} - _ => continue, - } - - if ty_matches_symbol(sa, impl_.extended_ty(), extended_ty.clone()) { - return Some(id); + if let Some(trait_ty) = impl_.trait_ty() { + if trait_ty.trait_id != trait_id { + continue; + } + + if ty_matches_symbol(sa, impl_.extended_ty(), extended_ty.clone()) { + return Some(id); + } } } diff --git a/dora-frontend/src/ty.rs b/dora-frontend/src/ty.rs index 7c24b27da..12709a0a1 100644 --- a/dora-frontend/src/ty.rs +++ b/dora-frontend/src/ty.rs @@ -831,6 +831,31 @@ impl<'a> SourceTypePrinter<'a> { } } +#[derive(Clone, Debug)] +pub struct TraitType { + pub trait_id: TraitDefinitionId, + pub type_params: SourceTypeArray, + pub bindings: Vec<(AliasDefinitionId, SourceType)>, +} + +impl TraitType { + pub fn new_ty(ty: SourceType) -> TraitType { + match ty { + SourceType::Trait(trait_id, type_params) => TraitType { + trait_id, + type_params, + bindings: Vec::new(), + }, + + _ => unreachable!(), + } + } + + pub fn ty(&self) -> SourceType { + SourceType::Trait(self.trait_id, self.type_params.clone()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/dora-frontend/src/typeck/call.rs b/dora-frontend/src/typeck/call.rs index e8396c081..0c4344112 100644 --- a/dora-frontend/src/typeck/call.rs +++ b/dora-frontend/src/typeck/call.rs @@ -109,8 +109,7 @@ fn check_expr_call_generic_static_method( let interned_name = ck.sa.interner.intern(&name); for trait_ty in ck.type_param_definition.bounds_for_type_param(tp_id) { - let trait_id = trait_ty.trait_id().expect("trait expected"); - let trait_ = ck.sa.trait_(trait_id); + let trait_ = ck.sa.trait_(trait_ty.trait_id); if let Some(trait_method_id) = trait_.get_method(interned_name, true) { matched_methods.push((trait_method_id, trait_ty)); @@ -144,7 +143,7 @@ fn check_expr_call_generic_static_method( ck.sa, trait_method, args, - &trait_ty.type_params(), + &trait_ty.type_params, Some(tp.clone()), ) { let fct_params = trait_method @@ -159,16 +158,16 @@ fn check_expr_call_generic_static_method( let call_type = CallType::GenericStaticMethod( tp_id, - trait_ty.trait_id().expect("trait expected"), + trait_ty.trait_id, trait_method_id, - trait_ty.type_params(), + trait_ty.type_params.clone(), ); ck.analysis.map_calls.insert(e.id, Arc::new(call_type)); let return_type = replace_type( ck.sa, trait_method.return_type(), - Some(&trait_ty.type_params()), + Some(&trait_ty.type_params), Some(tp), ); @@ -648,12 +647,10 @@ fn check_expr_call_generic_type_param( let interned_name = ck.sa.interner.intern(&name); for trait_ty in ck.type_param_definition.bounds_for_type_param(id) { - if let Some(trait_id) = trait_ty.trait_id() { - let trait_ = ck.sa.trait_(trait_id); + let trait_ = ck.sa.trait_(trait_ty.trait_id); - if let Some(trait_method_id) = trait_.get_method(interned_name, false) { - matched_methods.push((trait_method_id, trait_ty)); - } + if let Some(trait_method_id) = trait_.get_method(interned_name, false) { + matched_methods.push((trait_method_id, trait_ty)); } } @@ -666,13 +663,13 @@ fn check_expr_call_generic_type_param( let return_type = replace_type( ck.sa, return_type, - Some(&trait_ty.type_params()), + Some(&trait_ty.type_params), Some(object_type.clone()), ); ck.analysis.set_ty(e.id, return_type.clone()); - let trait_type_params = trait_ty.type_params(); + let trait_type_params = trait_ty.type_params; let call_type = CallType::GenericMethod( id, diff --git a/dora-frontend/src/typeck/lookup.rs b/dora-frontend/src/typeck/lookup.rs index 7124aa2f1..ae54a7083 100644 --- a/dora-frontend/src/typeck/lookup.rs +++ b/dora-frontend/src/typeck/lookup.rs @@ -372,8 +372,8 @@ pub fn find_method_call_candidates( for (_id, impl_) in sa.impls.iter() { if let Some(bindings) = impl_matches(sa, object_type.clone(), type_param_defs, impl_.id()) { - if let Some(trait_id) = impl_.trait_ty().trait_id() { - let trait_ = &sa.trait_(trait_id); + if let Some(trait_ty) = impl_.trait_ty() { + let trait_ = &sa.trait_(trait_ty.trait_id); if let Some(trait_method_id) = trait_.get_method(name, is_static) { candidates.push(Candidate { diff --git a/dora-frontend/src/typedefck.rs b/dora-frontend/src/typedefck.rs index cf28994cd..2d08d4234 100644 --- a/dora-frontend/src/typedefck.rs +++ b/dora-frontend/src/typedefck.rs @@ -41,7 +41,7 @@ fn parse_alias_types(sa: &Sema) { } for bound in &alias.bounds { - parsety::parse_type(sa, &table, alias.file_id, bound.parsed_ty()); + parsety::parse_trait_type(sa, &table, alias.file_id, bound.parsed_ty()); } } } @@ -98,7 +98,7 @@ fn parse_impl_types(sa: &Sema) { impl_.file_id, ); - parsety::parse_type(sa, &symtable, impl_.file_id, impl_.parsed_trait_ty()); + parsety::parse_trait_type(sa, &symtable, impl_.file_id, impl_.parsed_trait_ty()); parsety::parse_type( sa, @@ -257,7 +257,7 @@ fn parse_type_param_definition( for bound in type_param_definition.own_bounds() { parsety::parse_type(sa, &symtable, file_id, bound.parsed_ty()); - parsety::parse_type(sa, &symtable, file_id, bound.parsed_trait_ty()); + parsety::parse_trait_type(sa, &symtable, file_id, bound.parsed_trait_ty()); } } @@ -476,7 +476,7 @@ pub fn expand_types(sa: &Sema) { fn expand_impl_types(sa: &Sema) { for (_id, impl_) in sa.impls.iter() { parsety::expand_type(sa, impl_.parsed_extended_ty(), None); - parsety::expand_type(sa, impl_.parsed_trait_ty(), Some(impl_.extended_ty())); + parsety::expand_trait_type(sa, impl_.parsed_trait_ty(), Some(impl_.extended_ty())); expand_type_param_definition(sa, impl_.type_param_definition(), None); } @@ -583,7 +583,7 @@ fn expand_type_param_definition( ) { for bound in type_param_definition.own_bounds() { parsety::expand_type(sa, bound.parsed_ty(), replace_self.clone()); - parsety::expand_type(sa, bound.parsed_trait_ty(), replace_self.clone()); + parsety::expand_trait_type(sa, bound.parsed_trait_ty(), replace_self.clone()); } } diff --git a/dora-frontend/src/typeparamck.rs b/dora-frontend/src/typeparamck.rs index 11d8f0728..bf1e4bcf7 100644 --- a/dora-frontend/src/typeparamck.rs +++ b/dora-frontend/src/typeparamck.rs @@ -111,20 +111,20 @@ impl<'a> TypeParamCheck<'a> { for bound in self.callee_type_param_defs.bounds() { let tp_ty = bound.ty(); - let trait_ty = bound.trait_ty(); - - let tp_ty = specialize_type(self.sa, tp_ty, tps); - - if !implements_trait( - self.sa, - tp_ty.clone(), - self.caller_type_param_defs, - trait_ty.clone(), - ) { - if let ErrorReporting::Yes(file_id, span) = self.error { - self.fail_trait_bound(file_id, span, trait_ty, tp_ty.clone()); + if let Some(trait_ty) = bound.trait_ty() { + let tp_ty = specialize_type(self.sa, tp_ty, tps); + + if !implements_trait( + self.sa, + tp_ty.clone(), + self.caller_type_param_defs, + trait_ty.ty(), + ) { + if let ErrorReporting::Yes(file_id, span) = self.error { + self.fail_trait_bound(file_id, span, trait_ty.ty(), tp_ty.clone()); + } + succeeded = false; } - succeeded = false; } }