From 4bad5ca918f559633776c40044ed0dfa452f0db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Inf=C3=BChr?= Date: Sun, 13 Oct 2024 16:41:03 +0200 Subject: [PATCH] frontend: Add specialize_for_element() --- dora-frontend/src/aliasck.rs | 11 ++-- dora-frontend/src/lib.rs | 4 +- dora-frontend/src/parsety.rs | 2 +- dora-frontend/src/path.rs | 93 +++++++++++++++++++--------- dora-frontend/src/sema/aliases.rs | 8 ++- dora-frontend/src/sema/classes.rs | 8 ++- dora-frontend/src/sema/consts.rs | 4 ++ dora-frontend/src/sema/elements.rs | 17 +++-- dora-frontend/src/sema/enums.rs | 10 ++- dora-frontend/src/sema/extensions.rs | 8 ++- dora-frontend/src/sema/functions.rs | 10 +++ dora-frontend/src/sema/globals.rs | 4 ++ dora-frontend/src/sema/impls.rs | 6 +- dora-frontend/src/sema/structs.rs | 4 ++ dora-frontend/src/sema/traits.rs | 4 ++ dora-frontend/src/specialize.rs | 86 ++++++++++++++++++++++++- dora-frontend/src/ty.rs | 7 ++- dora-frontend/src/typeck/lookup.rs | 4 +- 18 files changed, 236 insertions(+), 54 deletions(-) diff --git a/dora-frontend/src/aliasck.rs b/dora-frontend/src/aliasck.rs index 10c0d4ecd..521e01912 100644 --- a/dora-frontend/src/aliasck.rs +++ b/dora-frontend/src/aliasck.rs @@ -498,16 +498,19 @@ mod tests { #[test] #[ignore] - fn impl_alias_generic() { + fn impl_alias_as_return_type() { ok(" struct Foo[T] trait TraitA { - type X[T]; + type X; } impl TraitA for String { - type X[T] = T; + type X = Int64; + } + fn x() { + let x: Int64 = f[String](\"foo\"); } - fn f[T: TraitA](t: T): T::X[Int64] { + fn f[T: TraitA](t: T): T::X { 0 } "); diff --git a/dora-frontend/src/lib.rs b/dora-frontend/src/lib.rs index 98350c8ad..46cd407ba 100644 --- a/dora-frontend/src/lib.rs +++ b/dora-frontend/src/lib.rs @@ -12,7 +12,9 @@ pub use crate::extensiondefck::package_for_type; pub use parsety::{ParsedTraitType, ParsedType, ParsedTypeAst}; pub use path::{parse_path, PathKind}; pub use program_emitter::emit_program; -pub use specialize::{replace_type, specialize_type, specialize_type_array}; +pub use specialize::{ + replace_type, specialize_for_element, specialize_type, specialize_type_array, +}; pub(crate) mod access; mod aliasck; diff --git a/dora-frontend/src/parsety.rs b/dora-frontend/src/parsety.rs index 7a8ea4e99..f5e917b4b 100644 --- a/dora-frontend/src/parsety.rs +++ b/dora-frontend/src/parsety.rs @@ -1148,7 +1148,7 @@ fn expand_st( assert_eq!(trait_id, trait_.id()); ty } else { - unreachable!() + ty } } diff --git a/dora-frontend/src/path.rs b/dora-frontend/src/path.rs index 50f262057..cfc42379e 100644 --- a/dora-frontend/src/path.rs +++ b/dora-frontend/src/path.rs @@ -1,9 +1,11 @@ use std::collections::HashMap; -use dora_parser::ast; +use dora_parser::{ast, Span}; use crate::access::sym_accessible_from; -use crate::sema::{parent_element_or_self, AliasDefinitionId, Element, Sema, SourceFileId}; +use crate::sema::{ + parent_element_or_self, AliasDefinitionId, Element, Sema, SourceFileId, TypeParamId, +}; use crate::{ErrorMessage, ModuleSymTable, Name, SymbolKind}; pub enum PathKind { @@ -27,7 +29,7 @@ pub fn parse_path( parse_path_self(sa, file_id, element, allow_self, regular) } - ast::PathSegmentData::Ident(..) => parse_path_ident(sa, table, file_id, regular), + ast::PathSegmentData::Ident(..) => parse_path_ident(sa, file_id, table, element, regular), ast::PathSegmentData::Error { .. } => Err(()), } @@ -76,8 +78,9 @@ fn parse_path_self( fn parse_path_ident( sa: &Sema, - table: &ModuleSymTable, file_id: SourceFileId, + table: &ModuleSymTable, + element: &dyn Element, regular: &ast::TypeRegularType, ) -> Result { let segments = ®ular.path.segments; @@ -95,37 +98,50 @@ fn parse_path_ident( let mut previous_sym = sym.expect("missing symbol"); for (idx, segment) in segments.iter().enumerate().skip(1) { - if !previous_sym.is_module() { - let msg = ErrorMessage::ExpectedPath; - sa.report(file_id, segments[idx - 1].span(), msg); - return Err(()); - } + if previous_sym.is_module() { + let name = expect_ident(sa, file_id, segment)?; - let name = expect_ident(sa, file_id, segment)?; - - let module_id = previous_sym.to_module().expect("expected module"); - let module = sa.module(module_id); - let current_sym = module.table().get(name); + let module_id = previous_sym.to_module().expect("expected module"); + let module = sa.module(module_id); + let current_sym = module.table().get(name); - if let Some(current_sym) = current_sym { - if sym_accessible_from(sa, current_sym.clone(), module_id) { - previous_sym = current_sym; + if let Some(current_sym) = current_sym { + if sym_accessible_from(sa, current_sym.clone(), module_id) { + previous_sym = current_sym; + } else { + let module = sa.module(module_id); + let name = node.name.name_as_string.clone(); + let msg = ErrorMessage::NotAccessibleInModule(module.name(sa), name); + sa.report(file_id, node.span, msg); + return Err(()); + } } else { let module = sa.module(module_id); - let name = node.name.name_as_string.clone(); - let msg = ErrorMessage::NotAccessibleInModule(module.name(sa), name); - sa.report(file_id, node.span, msg); + let name = sa.interner.str(name).to_string(); + let module_name = module.name(sa); + sa.report( + file_id, + segment.span(), + ErrorMessage::UnknownIdentifierInModule(module_name, name), + ); return Err(()); } + } else if let SymbolKind::TypeParam(id) = previous_sym { + let name = expect_ident(sa, file_id, segment)?; + + let avaiable = lookup_alias_on_type_param(sa, element, id, name).unwrap_or(Vec::new()); + + if avaiable.len() == 1 { + previous_sym = SymbolKind::Alias(avaiable[0]); + } else { + unimplemented!() + } } else { - let module = sa.module(module_id); - let name = sa.interner.str(name).to_string(); - let module_name = module.name(sa); - sa.report( - file_id, - segment.span(), - ErrorMessage::UnknownIdentifierInModule(module_name, name), - ); + let msg = ErrorMessage::ExpectedPath; + let start = segments[0].span().start(); + let end = segments[idx - 1].span().end(); + let span = Span::new(start, end); + sa.report(file_id, span, msg); return Err(()); } } @@ -153,6 +169,27 @@ fn available_aliases<'a>( } } +fn lookup_alias_on_type_param<'a>( + sa: &'a Sema, + element: &'a dyn Element, + id: TypeParamId, + name: Name, +) -> Option> { + let type_param_definition = element.type_param_definition().expect("no type params"); + let mut results = Vec::with_capacity(2); + + for bound in type_param_definition.bounds_for_type_param(id) { + let trait_id = bound.trait_id; + let trait_ = sa.trait_(trait_id); + + if let Some(id) = trait_.alias_names().get(&name) { + results.push(*id); + } + } + + Some(results) +} + fn expect_ident(sa: &Sema, file_id: SourceFileId, segment: &ast::PathSegment) -> Result { match segment.as_ref() { ast::PathSegmentData::Self_(ref node) => { diff --git a/dora-frontend/src/sema/aliases.rs b/dora-frontend/src/sema/aliases.rs index 23429ad6b..b865fd5ff 100644 --- a/dora-frontend/src/sema/aliases.rs +++ b/dora-frontend/src/sema/aliases.rs @@ -11,8 +11,8 @@ use std::sync::Arc; use id_arena::Id; use crate::sema::{ - Element, ElementId, ImplDefinitionId, ModuleDefinitionId, PackageDefinitionId, SourceFileId, - TraitDefinitionId, TypeParamDefinition, Visibility, + Element, ElementId, ImplDefinitionId, ModuleDefinitionId, PackageDefinitionId, Sema, + SourceFileId, TraitDefinitionId, TypeParamDefinition, Visibility, }; use dora_parser::ast; @@ -129,6 +129,10 @@ impl Element for AliasDefinition { fn to_alias(&self) -> Option<&AliasDefinition> { Some(self) } + + fn self_ty(&self, _sa: &Sema) -> Option { + None + } } pub struct AliasBound { diff --git a/dora-frontend/src/sema/classes.rs b/dora-frontend/src/sema/classes.rs index 216b0b5a2..60a72b13a 100644 --- a/dora-frontend/src/sema/classes.rs +++ b/dora-frontend/src/sema/classes.rs @@ -14,7 +14,7 @@ use crate::sema::{ module_path, Element, ElementAccess, ElementId, ExtensionDefinitionId, FctDefinitionId, ModuleDefinitionId, PackageDefinitionId, Sema, SourceFileId, TypeParamDefinition, }; -use crate::{replace_type, ParsedType, SourceType, SourceTypeArray}; +use crate::{specialize_for_element, ParsedType, SourceType, SourceTypeArray}; pub type ClassDefinitionId = Id; @@ -202,6 +202,10 @@ impl Element for ClassDefinition { fn type_param_definition(&self) -> Option<&Rc> { Some(&self.type_param_definition) } + + fn self_ty(&self, _sa: &Sema) -> Option { + Some(self.ty()) + } } impl ElementAccess for ClassDefinition { @@ -276,7 +280,7 @@ pub fn find_field_in_class( if field.name == name { return Some(( field.id, - replace_type(sa, field.ty(), Some(&type_params), None), + specialize_for_element(sa, field.ty(), cls, &type_params), )); } } diff --git a/dora-frontend/src/sema/consts.rs b/dora-frontend/src/sema/consts.rs index 8955d2dc9..4e8277306 100644 --- a/dora-frontend/src/sema/consts.rs +++ b/dora-frontend/src/sema/consts.rs @@ -97,6 +97,10 @@ impl Element for ConstDefinition { fn type_param_definition(&self) -> Option<&Rc> { None } + + fn self_ty(&self, _sa: &Sema) -> Option { + None + } } #[derive(Clone, Debug, PartialEq)] diff --git a/dora-frontend/src/sema/elements.rs b/dora-frontend/src/sema/elements.rs index abe3d0df8..f0d8e27f4 100644 --- a/dora-frontend/src/sema/elements.rs +++ b/dora-frontend/src/sema/elements.rs @@ -1,11 +1,14 @@ 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, +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, }; #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -58,6 +61,8 @@ pub trait Element { fn to_alias(&self) -> Option<&AliasDefinition> { None } + + fn self_ty(&self, sa: &Sema) -> Option; } pub trait ElementAccess { diff --git a/dora-frontend/src/sema/enums.rs b/dora-frontend/src/sema/enums.rs index ca208e539..60874f022 100644 --- a/dora-frontend/src/sema/enums.rs +++ b/dora-frontend/src/sema/enums.rs @@ -15,7 +15,9 @@ use crate::sema::{ module_path, Element, ElementAccess, ElementId, ExtensionDefinitionId, ModuleDefinitionId, PackageDefinitionId, Sema, SourceFileId, TypeParamDefinition, Visibility, }; -use crate::ty::SourceTypeArray; +use crate::{SourceType, SourceTypeArray}; + +use super::new_identity_type_params; pub type EnumDefinitionId = Id; @@ -129,6 +131,12 @@ impl Element for EnumDefinition { fn type_param_definition(&self) -> Option<&Rc> { Some(&self.type_param_definition) } + + fn self_ty(&self, _sa: &Sema) -> Option { + let type_params = self.type_param_definition().type_param_count(); + let type_params = new_identity_type_params(type_params); + Some(SourceType::Enum(self.id(), type_params)) + } } impl ElementAccess for EnumDefinition { diff --git a/dora-frontend/src/sema/extensions.rs b/dora-frontend/src/sema/extensions.rs index c0f08502b..35eabd5bb 100644 --- a/dora-frontend/src/sema/extensions.rs +++ b/dora-frontend/src/sema/extensions.rs @@ -5,8 +5,8 @@ use std::sync::Arc; use crate::interner::Name; use crate::sema::{ - Element, ElementId, FctDefinitionId, ModuleDefinitionId, PackageDefinitionId, SourceFileId, - TypeParamDefinition, + Element, ElementId, FctDefinitionId, ModuleDefinitionId, PackageDefinitionId, Sema, + SourceFileId, TypeParamDefinition, }; use crate::ty::SourceType; use crate::ParsedType; @@ -96,4 +96,8 @@ impl Element for ExtensionDefinition { fn type_param_definition(&self) -> Option<&Rc> { Some(&self.type_param_definition) } + + fn self_ty(&self, _sa: &Sema) -> Option { + Some(self.ty()) + } } diff --git a/dora-frontend/src/sema/functions.rs b/dora-frontend/src/sema/functions.rs index 624c7a9ea..3c494c09b 100644 --- a/dora-frontend/src/sema/functions.rs +++ b/dora-frontend/src/sema/functions.rs @@ -267,6 +267,16 @@ impl Element for FctDefinition { fn to_fct(&self) -> Option<&FctDefinition> { Some(self) } + + fn self_ty(&self, sa: &Sema) -> Option { + match self.parent { + FctParent::Extension(id) => Some(sa.extension(id).ty()), + FctParent::Impl(id) => Some(sa.impl_(id).extended_ty()), + FctParent::Function => unreachable!(), + FctParent::Trait(..) => unimplemented!(), + FctParent::None => None, + } + } } fn path_for_type(sa: &Sema, ty: SourceType) -> String { diff --git a/dora-frontend/src/sema/globals.rs b/dora-frontend/src/sema/globals.rs index 623085ffb..c54c43bbf 100644 --- a/dora-frontend/src/sema/globals.rs +++ b/dora-frontend/src/sema/globals.rs @@ -113,4 +113,8 @@ impl Element for GlobalDefinition { fn type_param_definition(&self) -> Option<&Rc> { None } + + fn self_ty(&self, _sa: &Sema) -> Option { + None + } } diff --git a/dora-frontend/src/sema/impls.rs b/dora-frontend/src/sema/impls.rs index 2b71a6e43..0036f85e2 100644 --- a/dora-frontend/src/sema/impls.rs +++ b/dora-frontend/src/sema/impls.rs @@ -8,7 +8,7 @@ use dora_parser::Span; use crate::sema::{ AliasDefinitionId, Element, ElementId, FctDefinitionId, ModuleDefinitionId, - PackageDefinitionId, SourceFileId, TraitDefinitionId, TypeParamDefinition, + PackageDefinitionId, Sema, SourceFileId, TraitDefinitionId, TypeParamDefinition, }; use crate::ty::SourceType; use crate::{ParsedTraitType, ParsedType, TraitType}; @@ -143,4 +143,8 @@ impl Element for ImplDefinition { fn to_impl(&self) -> Option<&ImplDefinition> { Some(self) } + + fn self_ty(&self, _sa: &Sema) -> Option { + Some(self.extended_ty()) + } } diff --git a/dora-frontend/src/sema/structs.rs b/dora-frontend/src/sema/structs.rs index 57f7ca3b8..a3b9370ec 100644 --- a/dora-frontend/src/sema/structs.rs +++ b/dora-frontend/src/sema/structs.rs @@ -155,6 +155,10 @@ impl Element for StructDefinition { fn type_param_definition(&self) -> Option<&Rc> { Some(&self.type_param_definition) } + + fn self_ty(&self, _sa: &Sema) -> Option { + Some(self.ty()) + } } impl ElementAccess for StructDefinition { diff --git a/dora-frontend/src/sema/traits.rs b/dora-frontend/src/sema/traits.rs index cb26dfb81..e3d14e8a6 100644 --- a/dora-frontend/src/sema/traits.rs +++ b/dora-frontend/src/sema/traits.rs @@ -148,6 +148,10 @@ impl Element for TraitDefinition { fn to_trait(&self) -> Option<&TraitDefinition> { Some(self) } + + fn self_ty(&self, _sa: &Sema) -> Option { + Some(SourceType::This) + } } impl ElementAccess for TraitDefinition { diff --git a/dora-frontend/src/specialize.rs b/dora-frontend/src/specialize.rs index c21421df3..e46664cb0 100644 --- a/dora-frontend/src/specialize.rs +++ b/dora-frontend/src/specialize.rs @@ -1,4 +1,4 @@ -use crate::sema::Sema; +use crate::sema::{Element, Sema}; use crate::{SourceType, SourceTypeArray}; pub fn specialize_type(sa: &Sema, ty: SourceType, type_params: &SourceTypeArray) -> SourceType { @@ -103,6 +103,90 @@ fn replace_sta( SourceTypeArray::with(new_array) } +pub fn specialize_for_element( + sa: &Sema, + ty: SourceType, + element: &dyn Element, + type_params_for_element: &SourceTypeArray, +) -> SourceType { + match ty { + SourceType::Class(cls_id, cls_type_params) => SourceType::Class( + cls_id, + specialize_for_element_array(sa, cls_type_params, element, type_params_for_element), + ), + + SourceType::TraitObject(trait_id, trait_type_params, bindings) => SourceType::TraitObject( + trait_id, + specialize_for_element_array(sa, trait_type_params, element, type_params_for_element), + specialize_for_element_array(sa, bindings, element, type_params_for_element), + ), + + SourceType::Struct(struct_id, struct_type_params) => SourceType::Struct( + struct_id, + specialize_for_element_array(sa, struct_type_params, element, type_params_for_element), + ), + + SourceType::Enum(enum_id, enum_type_params) => SourceType::Enum( + enum_id, + specialize_for_element_array(sa, enum_type_params, element, type_params_for_element), + ), + + SourceType::Alias(alias_id, alias_type_params) => SourceType::Alias( + alias_id, + specialize_for_element_array(sa, alias_type_params, element, type_params_for_element), + ), + + SourceType::Lambda(params, return_type) => SourceType::Lambda( + specialize_for_element_array(sa, params, element, type_params_for_element), + Box::new(specialize_for_element( + sa, + *return_type, + element, + type_params_for_element, + )), + ), + + SourceType::Tuple(subtypes) => SourceType::Tuple(specialize_for_element_array( + sa, + subtypes, + element, + type_params_for_element, + )), + + SourceType::This => { + assert!(element.is_trait()); + SourceType::This + } + + SourceType::TypeParam(id) => type_params_for_element[id.index()].clone(), + + SourceType::Unit + | SourceType::UInt8 + | SourceType::Bool + | SourceType::Char + | SourceType::Int32 + | SourceType::Int64 + | SourceType::Float32 + | SourceType::Float64 + | SourceType::Error => ty, + + SourceType::Any | SourceType::Ptr => unreachable!(), + } +} + +fn specialize_for_element_array( + sa: &Sema, + array: SourceTypeArray, + element: &dyn Element, + type_params_for_element: &SourceTypeArray, +) -> SourceTypeArray { + let new_array = array + .iter() + .map(|ty| specialize_for_element(sa, ty, element, type_params_for_element)) + .collect::>(); + SourceTypeArray::with(new_array) +} + pub fn specialize_for_trait_object(sa: &Sema, ty: SourceType, trait_ty: SourceType) -> SourceType { match ty { SourceType::Class(cls_id, cls_type_params) => SourceType::Class( diff --git a/dora-frontend/src/ty.rs b/dora-frontend/src/ty.rs index 405a1a96a..fb5fcfc64 100644 --- a/dora-frontend/src/ty.rs +++ b/dora-frontend/src/ty.rs @@ -491,7 +491,8 @@ impl SourceType { *self == other } SourceType::Ptr => panic!("ptr does not allow any other types"), - SourceType::This | SourceType::Alias(..) => unreachable!(), + SourceType::This => unreachable!(), + SourceType::Alias(..) => *self == other, SourceType::Class(self_cls_id, self_list) => { if *self == other { return true; @@ -529,9 +530,9 @@ impl SourceType { _ => false, }, - SourceType::TypeParam(_) => *self == other, + SourceType::TypeParam(..) => *self == other, - SourceType::Lambda(_, _) => { + SourceType::Lambda(..) => { // for now expect the exact same params and return types // possible improvement: allow super classes for params, // sub class for return type diff --git a/dora-frontend/src/typeck/lookup.rs b/dora-frontend/src/typeck/lookup.rs index ab59bc86f..da22c12b6 100644 --- a/dora-frontend/src/typeck/lookup.rs +++ b/dora-frontend/src/typeck/lookup.rs @@ -6,7 +6,7 @@ use crate::sema::{ }; use crate::typeck::function::args_compatible_fct; use crate::typeparamck::{self, ErrorReporting}; -use crate::{replace_type, ty, SourceType, SourceTypeArray}; +use crate::{specialize_for_element, ty, SourceType, SourceTypeArray}; use dora_parser::Span; pub struct MethodLookupResult { @@ -260,7 +260,7 @@ impl<'a> MethodLookup<'a> { let cmp_type = { let type_list = container_tps.connect(&fct_tps); - replace_type(self.sa, fct.return_type(), Some(&type_list), None) + specialize_for_element(self.sa, fct.return_type(), fct, &type_list) }; if self.ret.is_none() || self.ret.clone().unwrap() == cmp_type {