diff --git a/Cargo.lock b/Cargo.lock index fc11dc0e6e..fdfe363ab3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1088,6 +1088,9 @@ dependencies = [ "fe-hir", "fe-macros", "itertools", + "num-bigint", + "once_cell", + "regex", "rustc-hash", "salsa-2022", "smallvec", @@ -1158,10 +1161,10 @@ dependencies = [ "derive_more", "dir-test", "fe-compiler-test-utils", - "fxhash", "lazy_static", "logos", "rowan", + "rustc-hash", "wasm-bindgen", "wasm-bindgen-test", ] @@ -1649,9 +1652,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" @@ -1751,9 +1754,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "oorandom" @@ -2087,13 +2090,25 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.0" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac6cf59af1067a3fb53fbe5c88c053764e930f932be1d71d3ffe032cbe147f59" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.0", + "regex-automata", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", ] [[package]] @@ -2104,9 +2119,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6868896879ba532248f33598de5181522d8b3d9d724dfd230911e1a7d4822f5" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "revm" diff --git a/crates/hir-analysis/Cargo.toml b/crates/hir-analysis/Cargo.toml index 31002b9d27..5ec9b929ae 100644 --- a/crates/hir-analysis/Cargo.toml +++ b/crates/hir-analysis/Cargo.toml @@ -20,6 +20,9 @@ hir = { path = "../hir", package = "fe-hir" } common = { path = "../common2", package = "fe-common2" } macros = { path = "../macros", package = "fe-macros" } fe-compiler-test-utils = { path = "../test-utils" } +num-bigint = "0.4" +once_cell = "1.18" +regex = "1.10" [dev-dependencies] codespan-reporting = "0.11" diff --git a/crates/hir-analysis/src/lib.rs b/crates/hir-analysis/src/lib.rs index 9c2a2eb591..051071d3f5 100644 --- a/crates/hir-analysis/src/lib.rs +++ b/crates/hir-analysis/src/lib.rs @@ -16,6 +16,9 @@ pub struct Jar( ty::ty_def::AdtDef, ty::ty_def::FuncDef, ty::ty_def::AdtRefId, + /// Const types. + ty::const_ty::ConstTyId, + ty::const_ty::evaluate_const_ty, /// Type lowering. ty::ty_lower::lower_hir_ty, ty::ty_lower::lower_adt, @@ -23,6 +26,8 @@ pub struct Jar( ty::ty_lower::lower_type_alias, ty::ty_lower::collect_generic_params, ty::ty_lower::GenericParamOwnerId, + ty::ty_lower::GenericParamTypeSet, + ty::ty_lower::evaluate_params_precursor, /// Trait lowering. ty::trait_lower::lower_trait, ty::trait_lower::lower_trait_ref, diff --git a/crates/hir-analysis/src/name_resolution/diagnostics.rs b/crates/hir-analysis/src/name_resolution/diagnostics.rs index 5ca03b10d7..34c0bd2748 100644 --- a/crates/hir-analysis/src/name_resolution/diagnostics.rs +++ b/crates/hir-analysis/src/name_resolution/diagnostics.rs @@ -8,9 +8,8 @@ use hir::{ HirDb, }; -use crate::HirAnalysisDb; - use super::NameRes; +use crate::HirAnalysisDb; #[salsa::accumulator] pub struct NameResolutionDiagAccumulator(pub(super) NameResDiag); diff --git a/crates/hir-analysis/src/name_resolution/import_resolver.rs b/crates/hir-analysis/src/name_resolution/import_resolver.rs index 343fde1206..51f634aec8 100644 --- a/crates/hir-analysis/src/name_resolution/import_resolver.rs +++ b/crates/hir-analysis/src/name_resolution/import_resolver.rs @@ -11,8 +11,6 @@ use hir::{ use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; -use crate::{name_resolution::visibility_checker::is_use_visible, HirAnalysisDb}; - use super::{ diagnostics::NameResDiag, name_resolver::{ @@ -20,6 +18,7 @@ use super::{ NameResolutionError, NameResolutionResult, NameResolver, QueryDirective, }, }; +use crate::{name_resolution::visibility_checker::is_use_visible, HirAnalysisDb}; pub(crate) struct ImportResolver<'db> { db: &'db dyn HirAnalysisDb, @@ -473,8 +472,8 @@ impl<'db> ImportResolver<'db> { // the `i_use` resolution. // // This is because: - // 1. the resolution of the first segment changes depending on whether the - // dependent glob is resolved or not at the time of `i_use` resolution, + // 1. the resolution of the first segment changes depending on whether the const + // glob is resolved or not at the time of `i_use` resolution, // 2. the order in which uses are resolved is nondeterministic. // // In normal name resolution rules, the name brought in by a glob always shadows diff --git a/crates/hir-analysis/src/name_resolution/mod.rs b/crates/hir-analysis/src/name_resolution/mod.rs index d3b65ba691..545d6c7d4f 100644 --- a/crates/hir-analysis/src/name_resolution/mod.rs +++ b/crates/hir-analysis/src/name_resolution/mod.rs @@ -6,12 +6,6 @@ mod path_resolver; mod visibility_checker; use either::Either; -pub use import_resolver::ResolvedImports; -pub use name_resolver::{ - NameDerivation, NameDomain, NameQuery, NameRes, NameResBucket, NameResKind, QueryDirective, -}; -pub use path_resolver::EarlyResolvedPath; - use hir::{ analysis_pass::ModuleAnalysisPass, diagnostics::DiagnosticVoucher, @@ -21,16 +15,20 @@ use hir::{ }, visitor::prelude::*, }; +pub use import_resolver::ResolvedImports; +pub use name_resolver::{ + NameDerivation, NameDomain, NameQuery, NameRes, NameResBucket, NameResKind, QueryDirective, +}; +pub use path_resolver::EarlyResolvedPath; use rustc_hash::FxHashSet; -use crate::HirAnalysisDb; - use self::{ diagnostics::{ImportResolutionDiagAccumulator, NameResDiag, NameResolutionDiagAccumulator}, import_resolver::DefaultImporter, name_resolver::{NameResolutionError, ResolvedQueryCacheStore}, path_resolver::EarlyPathResolver, }; +use crate::HirAnalysisDb; // TODO: Implement `resolve_path` and `resolve_segments` after implementing the // late path resolution. @@ -296,7 +294,7 @@ impl<'db, 'a> EarlyPathVisitor<'db, 'a> { return; }; - let domain = NameDomain::from_scope(scope); + let domain = NameDomain::from_scope(self.db, scope); let binding = self.inner.resolve_query(query); match binding.pick(domain) { Ok(_) => {} diff --git a/crates/hir-analysis/src/name_resolution/name_resolver.rs b/crates/hir-analysis/src/name_resolution/name_resolver.rs index 45e2278680..abffd5baa4 100644 --- a/crates/hir-analysis/src/name_resolution/name_resolver.rs +++ b/crates/hir-analysis/src/name_resolution/name_resolver.rs @@ -12,18 +12,17 @@ use hir::{ AnonEdge, EdgeKind, FieldEdge, GenericParamEdge, IngotEdge, LexEdge, ModEdge, ScopeId, SelfEdge, SelfTyEdge, SuperEdge, TraitEdge, TypeEdge, ValueEdge, VariantEdge, }, - IdentId, ItemKind, Use, + GenericParam, GenericParamOwner, IdentId, ItemKind, Use, }, span::DynLazySpan, }; use rustc_hash::{FxHashMap, FxHashSet}; -use crate::HirAnalysisDb; - use super::{ import_resolver::Importer, visibility_checker::{is_scope_visible_from, is_use_visible}, }; +use crate::HirAnalysisDb; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct NameQuery { @@ -144,13 +143,19 @@ impl NameResBucket { /// Returns the resolution of the given `domain`. pub fn pick(&self, domain: NameDomain) -> &NameResolutionResult { - self.bucket - .get(&domain) - .unwrap_or(&Err(NameResolutionError::NotFound)) + for domain in domain.iter() { + if let Some(res) = self.bucket.get(&domain) { + return res; + } + } + + &Err(NameResolutionError::NotFound) } pub fn filter_by_domain(&mut self, domain: NameDomain) { - self.bucket.retain(|d, _| *d == domain); + for domain in domain.iter() { + self.bucket.retain(|d, _| *d == domain); + } } pub(super) fn merge(&mut self, bucket: &NameResBucket) { @@ -172,48 +177,49 @@ impl NameResBucket { /// Push the `res` into the set. fn push(&mut self, res: &NameRes) { - let domain = res.domain; - match self.bucket.entry(domain) { - Entry::Occupied(mut e) => { - let old_res = match e.get_mut() { - Ok(res) => res, - Err(NameResolutionError::NotFound) => { - e.insert(Ok(res.clone())).ok(); - return; - } - Err(NameResolutionError::Ambiguous(ambiguous_set)) => { - if ambiguous_set[0].derivation == res.derivation { - ambiguous_set.push(res.clone()); + for domain in res.domain.iter() { + match self.bucket.entry(domain) { + Entry::Occupied(mut e) => { + let old_res = match e.get_mut() { + Ok(res) => res, + Err(NameResolutionError::NotFound) => { + e.insert(Ok(res.clone())).ok(); + return; } - return; - } - Err(_) => { - return; - } - }; - - let old_derivation = old_res.derivation.clone(); - match res.derivation.cmp(&old_derivation) { - cmp::Ordering::Less => {} - cmp::Ordering::Equal => { - if old_res.kind != res.kind { - let old_res_cloned = old_res.clone(); - let res = res.clone(); - e.insert(Err(NameResolutionError::Ambiguous(vec![ - old_res_cloned, - res, - ]))) - .ok(); + Err(NameResolutionError::Ambiguous(ambiguous_set)) => { + if ambiguous_set[0].derivation == res.derivation { + ambiguous_set.push(res.clone()); + } + return; + } + Err(_) => { + return; + } + }; + + let old_derivation = old_res.derivation.clone(); + match res.derivation.cmp(&old_derivation) { + cmp::Ordering::Less => {} + cmp::Ordering::Equal => { + if old_res.kind != res.kind { + let old_res_cloned = old_res.clone(); + let res = res.clone(); + e.insert(Err(NameResolutionError::Ambiguous(vec![ + old_res_cloned, + res, + ]))) + .ok(); + } + } + cmp::Ordering::Greater => { + e.insert(Ok(res.clone())).ok(); } - } - cmp::Ordering::Greater => { - e.insert(Ok(res.clone())).ok(); } } - } - Entry::Vacant(e) => { - e.insert(Ok(res.clone())); + Entry::Vacant(e) => { + e.insert(Ok(res.clone())); + } } } } @@ -532,7 +538,7 @@ impl<'db, 'a> NameResolver<'db, 'a> { if found_scopes.insert(edge.dest) { let res = NameRes::new_from_scope( edge.dest, - NameDomain::from_scope(edge.dest), + NameDomain::from_scope(self.db, edge.dest), NameDerivation::Def, ); bucket.push(&res); @@ -658,7 +664,7 @@ impl<'db, 'a> NameResolver<'db, 'a> { unresolved_named_imports: FxHashSet, ) -> FxHashMap> { let mut res_collection: FxHashMap> = FxHashMap::default(); - let mut found_domains: FxHashMap = FxHashMap::default(); + let mut found_domains: FxHashMap = FxHashMap::default(); let mut found_kinds: FxHashSet<(IdentId, NameResKind)> = FxHashSet::default(); for edge in target.edges(self.db.as_hir_db()) { @@ -673,11 +679,14 @@ impl<'db, 'a> NameResolver<'db, 'a> { if !found_kinds.insert((name, scope.into())) { continue; } - let res = - NameRes::new_from_scope(scope, NameDomain::from_scope(scope), NameDerivation::Def); + let res = NameRes::new_from_scope( + scope, + NameDomain::from_scope(self.db, scope), + NameDerivation::Def, + ); if res.is_visible(self.db, use_scope) { - *found_domains.entry(name).or_default() |= res.domain as u8; + *found_domains.entry(name).or_default() |= res.domain; res_collection.entry(name).or_default().push(res); } } @@ -690,13 +699,13 @@ impl<'db, 'a> NameResolver<'db, 'a> { .iter() .filter(|res| res.is_visible(self.db, use_scope)) { - if (found_domain & res.domain as u8 != 0) + if (found_domain & res.domain != NameDomain::Invalid) || !found_kinds.insert((name, res.kind)) { continue; } - *found_domains_after_named.entry(name).or_default() |= res.domain as u8; + *found_domains_after_named.entry(name).or_default() |= res.domain; res_collection.entry(name).or_default().push(res.clone()); } } @@ -721,7 +730,7 @@ impl<'db, 'a> NameResolver<'db, 'a> { .copied() .unwrap_or_default(); - if (seen_domain & res.domain as u8 != 0) + if (seen_domain & res.domain != NameDomain::Invalid) || !found_kinds.insert((name, res.kind)) { continue; @@ -810,6 +819,73 @@ impl ResolvedQueryCacheStore { } } +/// Each resolved name is associated with a domain that indicates which domain +/// the name belongs to. +/// The multiple same names can be introduced in a same scope as long as they +/// are in different domains. +/// +/// E.g., A `Foo` in the below example can be introduced in the same scope as a +/// type and variant at the same time. +/// ```fe +/// struct Foo {} +/// enum MyEnum { +/// Foo +/// } +/// use MyEnum::Foo +/// ``` +// #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +// pub enum NameDomain { +// /// The domain is associated with all items except for items that belongs to +// /// the `Value` domain. +// Type = 0b1, +// /// The domain is associated with a local variable and items that are +// /// guaranteed not to have associated names. e.g., `fn`, `const` or enum +// /// variables. +// Value = 0b10, +// +// /// The domain is associated with both `Type` and `Value`. This domain is +// /// used to represent const type parameters. +// #[doc(hidden)] +// TypeAndValue = 0b11, +// +// /// The domain is associated with struct fields. +// Field = 0b100, +// } +// impl NameDomain { +// pub(super) fn from_scope(db: &dyn HirAnalysisDb, scope: ScopeId) -> Self +// { match scope { +// ScopeId::Item(ItemKind::Func(_) | ItemKind::Const(_)) +// | ScopeId::FuncParam(..) +// | ScopeId::Block(..) => Self::Value, +// ScopeId::Item(_) => Self::Type, +// ScopeId::GenericParam(parent, idx) => { +// let parent = +// GenericParamOwner::from_item_opt(parent).unwrap(); +// +// let param = +// &parent.params(db.as_hir_db()).data(db.as_hir_db())[idx]; +// match param { GenericParam::Type(_) => NameDomain::Type, +// GenericParam::Const(_) => NameDomain::Type | +// NameDomain::Value, } +// } +// ScopeId::Field(..) => Self::Field, +// ScopeId::Variant(..) => Self::Value, +// } +// } +// +// pub(super) fn disjoint(self) -> impl Iterator { +// match self { +// Self::Type => Either::Left(std::iter::once(Self::Type)), +// Self::Value => Either::Left(std::iter::once(Self::Value)), +// Self::TypeAndValue => { +// +// Either::Right(std::iter::once(Self::Type). +// chain(std::iter::once(Self::Value))) } +// Self::Field => Either::Left(std::iter::once(Self::Field)), +// } +// } +// } + /// Each resolved name is associated with a domain that indicates which domain /// the name belongs to. /// The multiple same names can be introduced in a same scope as long as they @@ -825,29 +901,105 @@ impl ResolvedQueryCacheStore { /// use MyEnum::Foo /// ``` #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum NameDomain { +pub struct NameDomain { + _inner: u8, +} + +#[allow(non_upper_case_globals)] +impl NameDomain { /// The domain is associated with all items except for items that belongs to /// the `Value` domain. - Type = 0b1, + pub const Type: Self = Self { _inner: 0b1 }; /// The domain is associated with a local variable and items that are /// guaranteed not to have associated names. e.g., `fn`, `const` or enum /// variables. - Value = 0b10, + pub const Value: Self = Self { _inner: 0b10 }; /// The domain is associated with struct fields. - Field = 0b100, -} + pub const Field: Self = Self { _inner: 0b100 }; -impl NameDomain { - pub(super) fn from_scope(scope: ScopeId) -> Self { + const Invalid: Self = Self { _inner: 0b0 }; + + pub(super) fn from_scope(db: &dyn HirAnalysisDb, scope: ScopeId) -> Self { match scope { ScopeId::Item(ItemKind::Func(_) | ItemKind::Const(_)) | ScopeId::FuncParam(..) | ScopeId::Block(..) => Self::Value, - ScopeId::Item(_) | ScopeId::GenericParam(..) => Self::Type, + ScopeId::Item(_) => Self::Type, + ScopeId::GenericParam(parent, idx) => { + let parent = GenericParamOwner::from_item_opt(parent).unwrap(); + + let param = &parent.params(db.as_hir_db()).data(db.as_hir_db())[idx]; + match param { + GenericParam::Type(_) => NameDomain::Type, + GenericParam::Const(_) => NameDomain::Type | NameDomain::Value, + } + } ScopeId::Field(..) => Self::Field, ScopeId::Variant(..) => Self::Value, } } + + pub(super) fn iter(self) -> impl Iterator { + struct NameDomainIter { + _inner: u8, + idx: u8, + } + + impl Iterator for NameDomainIter { + type Item = NameDomain; + + fn next(&mut self) -> Option { + while self.idx < 3 { + let domain = NameDomain { + _inner: (1 << self.idx) & self._inner, + }; + self.idx += 1; + if domain != NameDomain::Invalid { + return Some(domain); + } + } + + None + } + } + + NameDomainIter { + _inner: self._inner, + idx: 0, + } + } +} + +impl Default for NameDomain { + fn default() -> Self { + Self::Invalid + } +} + +impl std::ops::BitOr for NameDomain { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + Self { + _inner: self._inner | rhs._inner, + } + } +} + +impl std::ops::BitAnd for NameDomain { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self::Output { + Self { + _inner: self._inner & rhs._inner, + } + } +} + +impl std::ops::BitOrAssign for NameDomain { + fn bitor_assign(&mut self, rhs: Self) { + self._inner |= rhs._inner; + } } /// The propagator controls how the name query is propagated to the next scope. diff --git a/crates/hir-analysis/src/name_resolution/path_resolver.rs b/crates/hir-analysis/src/name_resolution/path_resolver.rs index e6f49686ee..9adc6b3bba 100644 --- a/crates/hir-analysis/src/name_resolution/path_resolver.rs +++ b/crates/hir-analysis/src/name_resolution/path_resolver.rs @@ -1,14 +1,13 @@ #![allow(unused)] use hir::hir_def::{scope_graph::ScopeId, IdentId, Partial, PathId}; -use crate::{name_resolution::QueryDirective, HirAnalysisDb}; - use super::{ name_resolver::{ NameRes, NameResBucket, NameResolutionError, NameResolver, ResolvedQueryCacheStore, }, NameDomain, NameQuery, }; +use crate::{name_resolution::QueryDirective, HirAnalysisDb}; /// The result of early path resolution. /// There are two kinds of early resolution results: @@ -109,7 +108,7 @@ impl<'db, 'a, 'b, 'c> EarlyPathResolver<'db, 'a, 'b, 'c> { segments: &[Partial], scope: ScopeId, ) -> PathResolutionResult { - let mut i_path = IntermediatePath::new(segments, scope); + let mut i_path = IntermediatePath::new(self.db, segments, scope); loop { match i_path.state(self.db) { IntermediatePathState::ReadyToFinalize => { @@ -117,7 +116,7 @@ impl<'db, 'a, 'b, 'c> EarlyPathResolver<'db, 'a, 'b, 'c> { return Ok(i_path.finalize_as_full(bucket)); } - IntermediatePathState::TypeDependent => return Ok(i_path.finalize_as_partial()), + IntermediatePathState::TypeConst => return Ok(i_path.finalize_as_partial()), IntermediatePathState::Unresolved => { self.resolve_segment(&mut i_path)?; @@ -157,14 +156,14 @@ struct IntermediatePath<'a> { } impl<'a> IntermediatePath<'a> { - fn new(path: &'a [Partial], scope: ScopeId) -> Self { - let domain = NameDomain::from_scope(scope); + fn new(db: &dyn HirAnalysisDb, path: &'a [Partial], scope: ScopeId) -> Self { + let domain = NameDomain::from_scope(db, scope); Self { path, idx: 0, current_res: NameRes::new_from_scope( scope, - NameDomain::from_scope(scope), + NameDomain::from_scope(db, scope), super::NameDerivation::Def, ), trajectory: vec![], @@ -173,7 +172,7 @@ impl<'a> IntermediatePath<'a> { /// Make a `NameQuery` to resolve the current segment. fn make_query(&self, db: &dyn HirAnalysisDb) -> PathResolutionResult { - debug_assert!(self.state(db) != IntermediatePathState::TypeDependent); + debug_assert!(self.state(db) != IntermediatePathState::TypeConst); let Partial::Present(name) = self.path[self.idx] else { return Err(PathResolutionError::new( NameResolutionError::Invalid, @@ -258,13 +257,13 @@ impl<'a> IntermediatePath<'a> { fn state(&self, db: &dyn HirAnalysisDb) -> IntermediatePathState { debug_assert!(self.idx < self.path.len()); - let is_type_dependent = + let is_type_const = (self.current_res.is_type() || self.current_res.is_trait()) && self.idx != 0; - if (self.idx == self.path.len() - 1) && !is_type_dependent { + if (self.idx == self.path.len() - 1) && !is_type_const { IntermediatePathState::ReadyToFinalize - } else if is_type_dependent { - IntermediatePathState::TypeDependent + } else if is_type_const { + IntermediatePathState::TypeConst } else { IntermediatePathState::Unresolved } @@ -279,7 +278,7 @@ enum IntermediatePathState { /// The intermediate path points to a type and the next segment need to be /// resolved with the type context. - TypeDependent, + TypeConst, /// The path resolution need to be continued further. Unresolved, diff --git a/crates/hir-analysis/src/ty/const_ty.rs b/crates/hir-analysis/src/ty/const_ty.rs new file mode 100644 index 0000000000..b25af4a7ed --- /dev/null +++ b/crates/hir-analysis/src/ty/const_ty.rs @@ -0,0 +1,182 @@ +use hir::hir_def::{Body, Expr, IntegerId, LitKind, Partial}; + +use super::{ + ty_def::{InvalidCause, TyId, TyParam, TyVar}, + unify::UnificationTable, +}; +use crate::{ + ty::ty_def::{Kind, TyBase, TyData, TyVarUniverse}, + HirAnalysisDb, +}; + +#[salsa::interned] +pub struct ConstTyId { + #[return_ref] + pub(super) data: ConstTyData, +} + +#[salsa::tracked] +pub(crate) fn evaluate_const_ty( + db: &dyn HirAnalysisDb, + const_ty: ConstTyId, + expected_ty: Option, +) -> ConstTyId { + let ConstTyData::UnEvaluated(body) = const_ty.data(db) else { + let const_ty_ty = const_ty.ty(db); + return match check_const_ty(db, const_ty_ty, expected_ty, &mut UnificationTable::new(db)) { + Ok(_) => const_ty, + Err(cause) => { + let ty = TyId::invalid(db, cause); + return const_ty.swap_ty(db, ty); + } + }; + }; + + let Partial::Present(expr) = body.expr(db.as_hir_db()).data(db.as_hir_db(), *body) else { + let data = ConstTyData::Evaluated( + EvaluatedConstTy::Invalid, + TyId::invalid(db, InvalidCause::Other), + ); + return ConstTyId::new(db, data); + }; + + let mut table = UnificationTable::new(db); + let (resolved, ty) = match expr { + Expr::Lit(LitKind::Bool(b)) => ( + EvaluatedConstTy::LitBool(*b), + TyId::new(db, TyData::TyBase(TyBase::bool())), + ), + + Expr::Lit(LitKind::Int(i)) => ( + EvaluatedConstTy::LitInt(*i), + table.new_var(TyVarUniverse::Integral, &Kind::Star), + ), + + _ => { + return ConstTyId::new( + db, + ConstTyData::Evaluated( + EvaluatedConstTy::Invalid, + TyId::invalid(db, InvalidCause::InvalidConstTyExpr { body: *body }), + ), + ); + } + }; + + let data = match check_const_ty(db, ty, expected_ty, &mut table) { + Ok(ty) => ConstTyData::Evaluated(resolved, ty), + Err(err) => ConstTyData::Evaluated(resolved, TyId::invalid(db, err)), + }; + + ConstTyId::new(db, data) +} + +// FIXME: When we add type inference, we need to use the inference engine to +// check the type of the expression instead of this function. +fn check_const_ty( + db: &dyn HirAnalysisDb, + const_ty_ty: TyId, + expected_ty: Option, + table: &mut UnificationTable, +) -> Result { + if const_ty_ty.is_invalid(db) { + return Err(InvalidCause::Other); + } + + let Some(expected_ty) = expected_ty else { + return Ok(const_ty_ty); + }; + + if table.unify(expected_ty, const_ty_ty) { + Ok(expected_ty) + } else { + let invalid = InvalidCause::ConstTyMismatch { + expected: expected_ty, + given: const_ty_ty, + }; + Err(invalid) + } +} + +impl ConstTyId { + pub fn ty(self, db: &dyn HirAnalysisDb) -> TyId { + match self.data(db) { + ConstTyData::TyVar(_, ty) => *ty, + ConstTyData::TyParam(_, ty) => *ty, + ConstTyData::Evaluated(_, ty) => *ty, + ConstTyData::UnEvaluated(_) => TyId::invalid(db, InvalidCause::Other), + } + } + + pub(super) fn pretty_print(self, db: &dyn HirAnalysisDb) -> String { + match &self.data(db) { + ConstTyData::TyVar(var, _) => var.pretty_print(), + ConstTyData::TyParam(param, ty) => { + format!("const {}: {}", param.pretty_print(db), ty.pretty_print(db)) + } + ConstTyData::Evaluated(resolved, _) => resolved.pretty_print(db), + ConstTyData::UnEvaluated(_) => "".to_string(), + } + } + + pub(super) fn evaluate(self, db: &dyn HirAnalysisDb, expected_ty: Option) -> Self { + evaluate_const_ty(db, self, expected_ty) + } + + pub(super) fn from_body(db: &dyn HirAnalysisDb, body: Body) -> Self { + let data = ConstTyData::UnEvaluated(body); + Self::new(db, data) + } + + pub(super) fn from_opt_body(db: &dyn HirAnalysisDb, body: Partial) -> Self { + match body { + Partial::Present(body) => Self::from_body(db, body), + Partial::Absent => { + let resolved = EvaluatedConstTy::Invalid; + let ty = TyId::invalid(db, InvalidCause::Other); + let data = ConstTyData::Evaluated(resolved, ty); + Self::new(db, data) + } + } + } + + fn swap_ty(self, db: &dyn HirAnalysisDb, ty: TyId) -> Self { + let data = match self.data(db) { + ConstTyData::TyVar(var, _) => ConstTyData::TyVar(var.clone(), ty), + ConstTyData::TyParam(param, _) => ConstTyData::TyParam(param.clone(), ty), + ConstTyData::Evaluated(evaluated, _) => ConstTyData::Evaluated(evaluated.clone(), ty), + ConstTyData::UnEvaluated(_) => { + return self; + } + }; + + Self::new(db, data) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ConstTyData { + TyVar(TyVar, TyId), + TyParam(TyParam, TyId), + Evaluated(EvaluatedConstTy, TyId), + UnEvaluated(Body), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum EvaluatedConstTy { + LitInt(IntegerId), + LitBool(bool), + Invalid, +} + +impl EvaluatedConstTy { + pub fn pretty_print(&self, db: &dyn HirAnalysisDb) -> String { + match self { + EvaluatedConstTy::LitInt(val) => { + format!("{}", val.data(db.as_hir_db())) + } + EvaluatedConstTy::LitBool(val) => format!("{}", val), + EvaluatedConstTy::Invalid => "".to_string(), + } + } +} diff --git a/crates/hir-analysis/src/ty/constraint.rs b/crates/hir-analysis/src/ty/constraint.rs index fb5bbde5da..34841d5fd8 100644 --- a/crates/hir-analysis/src/ty/constraint.rs +++ b/crates/hir-analysis/src/ty/constraint.rs @@ -9,14 +9,6 @@ use hir::hir_def::{ use rustc_hash::FxHashMap; use salsa::function::Configuration; -use crate::{ - ty::{ - trait_lower::{lower_impl_trait, lower_trait}, - unify::InferenceKey, - }, - HirAnalysisDb, -}; - use super::{ constraint_solver::{is_goal_satisfiable, GoalSatisfiability}, trait_def::{Implementor, TraitDef, TraitInstId}, @@ -24,6 +16,14 @@ use super::{ ty_def::{AdtDef, FuncDef, Subst, TyBase, TyData, TyId}, ty_lower::{collect_generic_params, lower_hir_ty, GenericParamOwnerId}, }; +use crate::{ + ty::{ + trait_lower::{lower_impl_trait, lower_trait}, + ty_def::TyVarUniverse, + unify::InferenceKey, + }, + HirAnalysisDb, +}; /// Returns a constraints list which is derived from the given type. /// # Returns @@ -46,10 +46,10 @@ pub(crate) fn ty_constraints( let (base, args) = ty.decompose_ty_app(db); let (params, base_constraints) = match base.data(db) { - TyData::TyBase(TyBase::Adt(adt)) => (adt.params(db), collect_adt_constraints(db, adt)), + TyData::TyBase(TyBase::Adt(adt)) => (adt.params(db), collect_adt_constraints(db, *adt)), TyData::TyBase(TyBase::Func(func_def)) => ( func_def.params(db), - collect_func_def_constraints(db, func_def), + collect_func_def_constraints(db, *func_def), ), _ => { return ( @@ -69,7 +69,7 @@ pub(crate) fn ty_constraints( // Generalize unbound type parameters. for &arg in params.iter().skip(arg_idx) { let key = InferenceKey(arg_idx as u32); - let ty_var = TyId::ty_var(db, arg.kind(db).clone(), key); + let ty_var = TyId::ty_var(db, TyVarUniverse::General, arg.kind(db).clone(), key); subst.insert(arg, ty_var); arg_idx += 1; } @@ -107,7 +107,12 @@ pub(crate) fn trait_inst_constraints( subst.insert( self_ty_param, - TyId::ty_var(db, self_ty_kind.clone(), InferenceKey(0_u32)), + TyId::ty_var( + db, + TyVarUniverse::General, + self_ty_kind.clone(), + InferenceKey(0_u32), + ), ); let constraint = def_constraints.apply_subst(db, &mut subst); @@ -492,9 +497,9 @@ impl<'db> ConstraintCollector<'db> { fn collect_constraints_from_generic_params(&mut self) { let param_set = collect_generic_params(self.db, self.owner); let params_list = self.owner.params(self.db); - assert!(param_set.params.len() == params_list.len(self.db.as_hir_db())); + assert!(param_set.params(self.db).len() == params_list.len(self.db.as_hir_db())); for (&ty, hir_param) in param_set - .params + .params(self.db) .iter() .zip(params_list.data(self.db.as_hir_db())) { diff --git a/crates/hir-analysis/src/ty/constraint_solver.rs b/crates/hir-analysis/src/ty/constraint_solver.rs index e0ef6fcc34..feba6b9aae 100644 --- a/crates/hir-analysis/src/ty/constraint_solver.rs +++ b/crates/hir-analysis/src/ty/constraint_solver.rs @@ -3,14 +3,13 @@ //! but the algorithm here is slightly extended to support multi parametrized //! traits. -use crate::{ty::constraint::ty_constraints, HirAnalysisDb}; - use super::{ constraint::{compute_super_assumptions, AssumptionListId, PredicateId, PredicateListId}, trait_def::{TraitEnv, TraitInstId}, ty_def::{Subst, TyId}, unify::UnificationTable, }; +use crate::{ty::constraint::ty_constraints, HirAnalysisDb}; type Goal = PredicateId; @@ -166,7 +165,7 @@ impl<'db> ConstraintSolver<'db> { return GoalSatisfiability::NotSatisfied(self.goal); }; - // Checks if the all subgoals are satisfied. + // Checks if the all sub goals are satisfied. for &sub_goal in sub_goals.predicates(self.db) { match is_goal_satisfiable(self.db, sub_goal, self.assumptions) { GoalSatisfiability::Satisfied => {} diff --git a/crates/hir-analysis/src/ty/def_analysis.rs b/crates/hir-analysis/src/ty/def_analysis.rs index 7d3805b7ff..4a074b42a9 100644 --- a/crates/hir-analysis/src/ty/def_analysis.rs +++ b/crates/hir-analysis/src/ty/def_analysis.rs @@ -6,14 +6,28 @@ use std::collections::{hash_map::Entry, BTreeSet}; use hir::{ hir_def::{ - scope_graph::ScopeId, FieldDef, Func, FuncParamListId, IdentId, Impl as HirImpl, ImplTrait, - ItemKind, PathId, Trait, TraitRefId, TypeAlias, TypeId as HirTyId, VariantKind, + scope_graph::ScopeId, FieldDef, Func, FuncParamListId, GenericParam, IdentId, + Impl as HirImpl, ImplTrait, ItemKind, PathId, Trait, TraitRefId, TypeAlias, + TypeId as HirTyId, VariantKind, }, visitor::prelude::*, }; use rustc_hash::{FxHashMap, FxHashSet}; use salsa::function::Configuration; +use super::{ + const_ty::ConstTyId, + constraint::{ + collect_impl_block_constraints, collect_super_traits, AssumptionListId, SuperTraitCycle, + }, + constraint_solver::{is_goal_satisfiable, GoalSatisfiability}, + diagnostics::{ImplDiag, TraitConstraintDiag, TraitLowerDiag, TyDiagCollection, TyLowerDiag}, + trait_def::{ingot_trait_env, Implementor, TraitDef, TraitMethod}, + trait_lower::{lower_trait, lower_trait_ref, TraitRefLowerError}, + ty_def::{AdtDef, AdtRef, AdtRefId, FuncDef, InvalidCause, TyData, TyId}, + ty_lower::{collect_generic_params, lower_adt, lower_kind, GenericParamOwnerId}, + visitor::{walk_ty, TyVisitor}, +}; use crate::{ name_resolution::{resolve_path_early, EarlyResolvedPath, NameDomain, NameResKind}, ty::{ @@ -23,28 +37,12 @@ use crate::{ }, method_table::collect_methods, trait_lower::lower_impl_trait, - ty_lower::lower_type_alias, + ty_lower::{lower_func, lower_hir_ty, lower_type_alias}, unify::UnificationTable, }, HirAnalysisDb, }; -use super::{ - constraint::{ - collect_impl_block_constraints, collect_super_traits, AssumptionListId, SuperTraitCycle, - }, - constraint_solver::{is_goal_satisfiable, GoalSatisfiability}, - diagnostics::{ImplDiag, TraitConstraintDiag, TraitLowerDiag, TyDiagCollection, TyLowerDiag}, - trait_def::{ingot_trait_env, Implementor, TraitDef, TraitMethod}, - trait_lower::{lower_trait, lower_trait_ref, TraitRefLowerError}, - ty_def::{AdtDef, AdtRef, AdtRefId, FuncDef, InvalidCause, TyId}, - ty_lower::{ - collect_generic_params, lower_adt, lower_func, lower_hir_ty, lower_kind, - GenericParamOwnerId, - }, - visitor::{walk_ty, TyVisitor}, -}; - /// This function implements analysis for the ADT definition. /// The analysis includes the following: /// - Check if the types in the ADT is well-formed. @@ -273,12 +271,20 @@ impl<'db> DefAnalyzer<'db> { } } - // This method ensures that field/variant types are fully applied. - fn verify_fully_applied(&mut self, ty: HirTyId, span: DynLazySpan) -> bool { + /// This method verifies if + /// 1. the given `ty` has `*` kind(i.e, concrete type) + /// 2. the given `ty` is not const type + /// TODo: This method is a stop-gap implementation until we design a true + /// const type system. + fn verify_normal_star_type(&mut self, ty: HirTyId, span: DynLazySpan) -> bool { let ty = lower_hir_ty(self.db, ty, self.scope()); - if !ty.is_mono_type(self.db) { + if !ty.has_star_kind(self.db) { + self.diags + .push(TyLowerDiag::expected_star_kind_ty(span).into()); + false + } else if ty.is_const_ty(self.db) { self.diags - .push(TyLowerDiag::not_fully_applied_type(span).into()); + .push(TyLowerDiag::normal_type_expected(self.db, span, ty).into()); false } else { true @@ -431,15 +437,53 @@ impl<'db> Visitor for DefAnalyzer<'db> { return; } + if ty.is_const_ty(self.db) { + let diag = + TraitConstraintDiag::const_ty_bound(self.db, ty, ctxt.span().unwrap().ty().into()) + .into(); + self.diags.push(diag); + return; + } + self.current_ty = Some((ty, ctxt.span().unwrap().ty().into())); walk_where_predicate(self, ctxt, pred); } + fn visit_field_def(&mut self, ctxt: &mut VisitorCtxt<'_, LazyFieldDefSpan>, field: &FieldDef) { - if let Some(ty) = field.ty.to_opt() { - if self.verify_fully_applied(ty, ctxt.span().unwrap().ty().into()) { - walk_field_def(self, ctxt, field); + let Some(ty) = field.ty.to_opt() else { + return; + }; + + if !self.verify_normal_star_type(ty, ctxt.span().unwrap().ty().into()) { + return; + } + + let Some(name) = field.name.to_opt() else { + return; + }; + + // Checks if the field type is the same as the type of const type parameter. + if let Some(const_ty) = find_const_ty_param(self.db, name, ctxt.scope()) { + let const_ty_ty = const_ty.ty(self.db); + let field_ty = lower_hir_ty(self.db, ty, ctxt.scope()); + if !const_ty_ty.contains_invalid(self.db) + && !field_ty.contains_invalid(self.db) + && field_ty != const_ty_ty + { + self.diags.push( + TyLowerDiag::const_ty_mismatch( + self.db, + ctxt.span().unwrap().ty().into(), + const_ty_ty, + field_ty, + ) + .into(), + ); + return; } } + + walk_field_def(self, ctxt, field); } fn visit_variant_def( @@ -454,7 +498,7 @@ impl<'db> Visitor for DefAnalyzer<'db> { continue; }; - self.verify_fully_applied(elem_ty, span.elem_ty(i).into()); + self.verify_normal_star_type(elem_ty, span.elem_ty(i).into()); } } walk_variant_def(self, ctxt, variant); @@ -499,12 +543,27 @@ impl<'db> Visitor for DefAnalyzer<'db> { } } - self.current_ty = Some(( - self.def.params(self.db)[idx], - ctxt.span().unwrap().into_type_param().name().into(), - )); + match param { + GenericParam::Type(_) => { + self.current_ty = Some(( + self.def.params(self.db)[idx], + ctxt.span().unwrap().into_type_param().name().into(), + )); + walk_generic_param(self, ctxt, param) + } + GenericParam::Const(_) => { + let ty = self.def.params(self.db)[idx]; + let Some(const_ty_param) = ty.const_ty_param(self.db) else { + return; + }; - walk_generic_param(self, ctxt, param) + if let Some(diag) = const_ty_param + .emit_diag(self.db, ctxt.span().unwrap().into_const_param().ty().into()) + { + self.diags.push(diag) + } + } + } } fn visit_kind_bound( @@ -633,6 +692,10 @@ impl<'db> Visitor for DefAnalyzer<'db> { walk_func(self, ctxt, hir_func); + if let Some(ret_ty) = hir_func.ret_ty(self.db.as_hir_db()) { + self.verify_normal_star_type(ret_ty, hir_func.lazy_span().ret_ty().into()); + } + self.assumptions = constraints; self.def = def; } @@ -689,7 +752,7 @@ impl<'db> Visitor for DefAnalyzer<'db> { self.verify_self_type(hir_ty, ty_span.clone()); } - if !self.verify_fully_applied(hir_ty, ty_span) { + if !self.verify_normal_star_type(hir_ty, ty_span) { return; } @@ -780,21 +843,38 @@ fn analyze_trait_ref( ) -> Option { let trait_inst = match lower_trait_ref(db, trait_ref, scope) { Ok(trait_ref) => trait_ref, - Err(TraitRefLowerError::TraitNotFound) => { - return None; - } Err(TraitRefLowerError::ArgNumMismatch { expected, given }) => { return Some(TraitConstraintDiag::trait_arg_num_mismatch(span, expected, given).into()); } - Err(TraitRefLowerError::ArgumentKindMisMatch { expected, given }) => { + Err(TraitRefLowerError::ArgKindMisMatch { expected, given }) => { return Some(TraitConstraintDiag::kind_mismatch(db, span, &expected, given).into()); } Err(TraitRefLowerError::AssocTy(_)) => { return Some(TyLowerDiag::assoc_ty(span).into()); } + + Err(TraitRefLowerError::ArgTypeMismatch { expected, given }) => match (expected, given) { + (Some(expected), Some(given)) => { + return Some(TyLowerDiag::const_ty_mismatch(db, span, expected, given).into()) + } + + (Some(expected), None) => { + return Some(TyLowerDiag::const_ty_expected(db, span, expected).into()) + } + + (None, Some(given)) => { + return Some(TyLowerDiag::normal_type_expected(db, span, given).into()) + } + + (None, None) => unreachable!(), + }, + + Err(TraitRefLowerError::Other) => { + return None; + } }; if let Some(assumptions) = assumptions { @@ -820,7 +900,7 @@ impl DefKind { Self::Trait(def) => def.params(db), Self::ImplTrait(def) => def.params(db), Self::Impl(hir_impl, _) => { - &collect_generic_params(db, GenericParamOwnerId::new(db, hir_impl.into())).params + collect_generic_params(db, GenericParamOwnerId::new(db, hir_impl.into())).params(db) } Self::Func(def) => def.params(db), } @@ -921,18 +1001,35 @@ fn analyze_impl_trait_specific_error( // 4. Checks if conflict occurs. // If there is no implementor type even if the trait ref and implementor type is // well-formed, it means that the conflict does occur. - let impls = trait_env.implementors_for(db, trait_inst); - for conflict_cand in impls { - if conflict_cand.does_conflict(db, current_impl, &mut UnificationTable::new(db)) { + analyze_conflict_impl(db, current_impl, &mut diags); + return Err(diags); + }; + + fn analyze_conflict_impl( + db: &dyn HirAnalysisDb, + implementor: Implementor, + diags: &mut Vec, + ) { + let trait_ = implementor.trait_(db); + let env = ingot_trait_env(db, trait_.ingot(db)); + let Some(impls) = env.impls.get(&trait_.def(db)) else { + return; + }; + + for cand in impls { + if cand.does_conflict(db, implementor, &mut UnificationTable::new(db)) { diags.push( - TraitLowerDiag::conflict_impl(impl_trait, conflict_cand.hir_impl_trait(db)) - .into(), + TraitLowerDiag::conflict_impl( + cand.hir_impl_trait(db), + implementor.hir_impl_trait(db), + ) + .into(), ); - return Err(diags); + + return; } } - unreachable!() - }; + } // 5. Checks if implementor type satisfies the kind bound which is required by // the trait. @@ -1251,3 +1348,32 @@ impl<'db> ImplTraitMethodAnalyzer<'db> { } } } + +fn find_const_ty_param( + db: &dyn HirAnalysisDb, + ident: IdentId, + scope: ScopeId, +) -> Option { + let path = PathId::from_ident(db.as_hir_db(), ident); + let EarlyResolvedPath::Full(bucket) = resolve_path_early(db, path, scope) else { + return None; + }; + + let res = bucket.pick(NameDomain::Value).as_ref().ok()?; + let NameResKind::Scope(scope) = res.kind else { + return None; + }; + + let (item, idx) = match scope { + ScopeId::GenericParam(item, idx) => (item, idx), + _ => return None, + }; + + let owner = GenericParamOwnerId::from_item_opt(db, item).unwrap(); + let param_set = collect_generic_params(db, owner); + let ty = param_set.params(db).get(idx)?; + match ty.data(db) { + TyData::ConstTy(const_ty) => Some(*const_ty), + _ => None, + } +} diff --git a/crates/hir-analysis/src/ty/diagnostics.rs b/crates/hir-analysis/src/ty/diagnostics.rs index 02ad514d05..100a6328ef 100644 --- a/crates/hir-analysis/src/ty/diagnostics.rs +++ b/crates/hir-analysis/src/ty/diagnostics.rs @@ -9,15 +9,13 @@ use hir::{ span::{DynLazySpan, LazySpan}, HirDb, SpannedHirDb, }; - -use crate::HirAnalysisDb; +use itertools::Itertools; use super::{ constraint::PredicateId, ty_def::{Kind, TyId}, }; - -use itertools::Itertools; +use crate::HirAnalysisDb; #[salsa::accumulator] pub struct AdtDefDiagAccumulator(pub(super) TyDiagCollection); @@ -53,7 +51,7 @@ impl TyDiagCollection { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum TyLowerDiag { - NotFullyAppliedType(DynLazySpan), + ExpectedStarKind(DynLazySpan), InvalidTypeArgKind(DynLazySpan, String), RecursiveType { primary_span: DynLazySpan, @@ -86,12 +84,37 @@ pub enum TyLowerDiag { name: IdentId, }, + InvalidConstParamTy { + primary: DynLazySpan, + pretty_ty: String, + }, + + RecursiveConstParamTy(DynLazySpan), + + ConstTyMismatch { + primary: DynLazySpan, + expected: String, + actual: String, + }, + + ConstTyExpected { + primary: DynLazySpan, + expected: String, + }, + + NormalTypeExpected { + primary: DynLazySpan, + given: String, + }, + AssocTy(DynLazySpan), + + InvalidConstTyExpr(DynLazySpan), } impl TyLowerDiag { - pub fn not_fully_applied_type(span: DynLazySpan) -> Self { - Self::NotFullyAppliedType(span) + pub fn expected_star_kind_ty(span: DynLazySpan) -> Self { + Self::ExpectedStarKind(span) } pub fn invalid_type_arg_kind( @@ -136,6 +159,15 @@ impl TyLowerDiag { } } + pub(super) fn invalid_const_param_ty( + db: &dyn HirAnalysisDb, + primary: DynLazySpan, + ty: TyId, + ) -> Self { + let pretty_ty = ty.pretty_print(db).to_string(); + Self::InvalidConstParamTy { primary, pretty_ty } + } + pub(super) fn inconsistent_kind_bound( db: &dyn HirAnalysisDb, span: DynLazySpan, @@ -164,6 +196,39 @@ impl TyLowerDiag { } } + pub(super) fn const_ty_mismatch( + db: &dyn HirAnalysisDb, + primary: DynLazySpan, + expected: TyId, + actual: TyId, + ) -> Self { + let expected = expected.pretty_print(db).to_string(); + let actual = actual.pretty_print(db).to_string(); + Self::ConstTyMismatch { + primary, + expected, + actual, + } + } + + pub(super) fn const_ty_expected( + db: &dyn HirAnalysisDb, + primary: DynLazySpan, + expected: TyId, + ) -> Self { + let expected = expected.pretty_print(db).to_string(); + Self::ConstTyExpected { primary, expected } + } + + pub(super) fn normal_type_expected( + db: &dyn HirAnalysisDb, + primary: DynLazySpan, + given: TyId, + ) -> Self { + let given = given.pretty_print(db).to_string(); + Self::NormalTypeExpected { primary, given } + } + pub(super) fn duplicated_arg_name( primary: DynLazySpan, conflict_with: DynLazySpan, @@ -182,7 +247,7 @@ impl TyLowerDiag { fn local_code(&self) -> u16 { match self { - Self::NotFullyAppliedType(_) => 0, + Self::ExpectedStarKind(_) => 0, Self::InvalidTypeArgKind(_, _) => 1, Self::RecursiveType { .. } => 2, Self::UnboundTypeAliasParam { .. } => 3, @@ -191,13 +256,19 @@ impl TyLowerDiag { Self::KindBoundNotAllowed(_) => 6, Self::GenericParamAlreadyDefinedInParent { .. } => 7, Self::DuplicatedArgName { .. } => 8, - Self::AssocTy(_) => 9, + Self::InvalidConstParamTy { .. } => 9, + Self::RecursiveConstParamTy { .. } => 10, + Self::ConstTyMismatch { .. } => 11, + Self::ConstTyExpected { .. } => 12, + Self::NormalTypeExpected { .. } => 13, + Self::AssocTy(_) => 14, + Self::InvalidConstTyExpr(_) => 15, } } fn message(&self) -> String { match self { - Self::NotFullyAppliedType(_) => "expected fully applied type".to_string(), + Self::ExpectedStarKind(_) => "expected `*` kind in this context".to_string(), Self::InvalidTypeArgKind(_, _) => "invalid type argument kind".to_string(), Self::RecursiveType { .. } => "recursive type is not allowed".to_string(), @@ -217,15 +288,35 @@ impl TyLowerDiag { "duplicated argument name in function definition is not allowed".to_string() } + Self::InvalidConstParamTy { pretty_ty, .. } => { + format!("`{}` is forbidden as a const parameter type", pretty_ty) + } + + Self::ConstTyMismatch { .. } => { + "given type doesn't match the expected const type".to_string() + } + + Self::ConstTyExpected { .. } => "expected const type".to_string(), + + Self::NormalTypeExpected { .. } => "expected a normal type".to_string(), + + Self::RecursiveConstParamTy(_) => { + "recursive const parameter type is not allowed".to_string() + } + Self::AssocTy(_) => "associated type is not supported ".to_string(), + + Self::InvalidConstTyExpr(_) => { + "the expression is not supported yet in a const type context".to_string() + } } } fn sub_diags(&self, db: &dyn SpannedHirDb) -> Vec { match self { - Self::NotFullyAppliedType(span) => vec![SubDiagnostic::new( + Self::ExpectedStarKind(span) => vec![SubDiagnostic::new( LabelStyle::Primary, - "expected fully applied type here".to_string(), + "expected `*` kind here".to_string(), span.resolve(db), )], @@ -348,11 +439,62 @@ impl TyLowerDiag { ] } + Self::InvalidConstParamTy { primary, .. } => { + vec![SubDiagnostic::new( + LabelStyle::Primary, + "only integer or bool types are allowed as a const parameter type".to_string(), + primary.resolve(db), + )] + } + + Self::ConstTyMismatch { + primary, + expected, + actual, + } => { + vec![SubDiagnostic::new( + LabelStyle::Primary, + format!( + "expected `{}` type here, but `{}` is given", + expected, actual + ), + primary.resolve(db), + )] + } + + Self::ConstTyExpected { primary, expected } => { + vec![SubDiagnostic::new( + LabelStyle::Primary, + format!("expected const type of `{}` here", expected), + primary.resolve(db), + )] + } + + Self::NormalTypeExpected { primary, given } => { + vec![SubDiagnostic::new( + LabelStyle::Primary, + format!("expected a normal type here, but `{}` is given", given,), + primary.resolve(db), + )] + } + + Self::RecursiveConstParamTy(span) => vec![SubDiagnostic::new( + LabelStyle::Primary, + "recursive const parameter type is detected here".to_string(), + span.resolve(db), + )], + Self::AssocTy(span) => vec![SubDiagnostic::new( LabelStyle::Primary, "associated type is not implemented".to_string(), span.resolve(db), )], + + Self::InvalidConstTyExpr(span) => vec![SubDiagnostic::new( + LabelStyle::Primary, + "only literal expression is supported".to_string(), + span.resolve(db), + )], } } @@ -494,6 +636,8 @@ pub enum TraitConstraintDiag { InfiniteBoundRecursion(DynLazySpan, String), ConcreteTypeBound(DynLazySpan, String), + + ConstTyBound(DynLazySpan, String), } impl TraitConstraintDiag { @@ -550,6 +694,11 @@ impl TraitConstraintDiag { Self::InfiniteBoundRecursion(span, msg) } + pub(super) fn const_ty_bound(db: &dyn HirAnalysisDb, ty: TyId, span: DynLazySpan) -> Self { + let msg = format!("`{}` is a const type", ty.pretty_print(db)); + Self::ConstTyBound(span, msg) + } + pub(super) fn concrete_type_bound(db: &dyn HirAnalysisDb, span: DynLazySpan, ty: TyId) -> Self { let msg = format!("`{}` is a concrete type", ty.pretty_print(db)); Self::ConcreteTypeBound(span, msg) @@ -563,6 +712,7 @@ impl TraitConstraintDiag { Self::TraitBoundNotSat(_, _) => 3, Self::InfiniteBoundRecursion(_, _) => 4, Self::ConcreteTypeBound(_, _) => 5, + Self::ConstTyBound(_, _) => 6, } } @@ -581,6 +731,8 @@ impl TraitConstraintDiag { Self::ConcreteTypeBound(_, _) => { "trait bound for concrete type is not allowed".to_string() } + + Self::ConstTyBound(_, _) => "trait bound for const type is not allowed".to_string(), } } @@ -634,6 +786,12 @@ impl TraitConstraintDiag { msg.clone(), span.resolve(db), )], + + Self::ConstTyBound(span, msg) => vec![SubDiagnostic::new( + LabelStyle::Primary, + msg.clone(), + span.resolve(db), + )], } } diff --git a/crates/hir-analysis/src/ty/method_table.rs b/crates/hir-analysis/src/ty/method_table.rs index b02b4d8be0..70a9c48d65 100644 --- a/crates/hir-analysis/src/ty/method_table.rs +++ b/crates/hir-analysis/src/ty/method_table.rs @@ -2,13 +2,12 @@ use common::input::IngotKind; use hir::hir_def::{IdentId, Impl, IngotId}; use rustc_hash::FxHashMap; -use crate::HirAnalysisDb; - use super::{ ty_def::{FuncDef, InvalidCause, TyBase, TyId}, ty_lower::{lower_func, lower_hir_ty}, unify::UnificationTable, }; +use crate::HirAnalysisDb; #[salsa::tracked(return_ref)] pub(crate) fn collect_methods(db: &dyn HirAnalysisDb, ingot: IngotId) -> MethodTable { diff --git a/crates/hir-analysis/src/ty/mod.rs b/crates/hir-analysis/src/ty/mod.rs index cad78c1b31..539779c224 100644 --- a/crates/hir-analysis/src/ty/mod.rs +++ b/crates/hir-analysis/src/ty/mod.rs @@ -1,4 +1,3 @@ -use crate::HirAnalysisDb; use hir::{analysis_pass::ModuleAnalysisPass, hir_def::TopLevelMod}; use self::{ @@ -12,7 +11,9 @@ use self::{ }, ty_def::AdtRefId, }; +use crate::HirAnalysisDb; +pub mod const_ty; pub mod constraint_solver; pub mod def_analysis; pub mod diagnostics; diff --git a/crates/hir-analysis/src/ty/trait_def.rs b/crates/hir-analysis/src/ty/trait_def.rs index 87b52ab07b..37ac3b6a59 100644 --- a/crates/hir-analysis/src/ty/trait_def.rs +++ b/crates/hir-analysis/src/ty/trait_def.rs @@ -8,11 +8,6 @@ use hir::{ }; use rustc_hash::FxHashMap; -use crate::{ - ty::{constraint::collect_implementor_constraints, trait_lower::collect_trait_impls}, - HirAnalysisDb, -}; - use super::{ constraint::{ collect_super_traits, collect_trait_constraints, trait_inst_constraints, AssumptionListId, @@ -22,8 +17,13 @@ use super::{ diagnostics::{TraitConstraintDiag, TyDiagCollection}, trait_lower::collect_implementor_methods, ty_def::{FuncDef, Kind, Subst, TyId}, + ty_lower::GenericParamTypeSet, unify::UnificationTable, }; +use crate::{ + ty::{constraint::collect_implementor_constraints, trait_lower::collect_trait_impls}, + HirAnalysisDb, +}; /// Returns [`TraitEnv`] for the given ingot. #[salsa::tracked(return_ref)] @@ -54,7 +54,7 @@ pub(crate) fn trait_implementors(db: &dyn HirAnalysisDb, trait_: TraitInstId) -> /// implementors which can be used in the ingot. #[derive(Debug, PartialEq, Eq, Clone)] pub(crate) struct TraitEnv { - impls: FxHashMap>, + pub(super) impls: FxHashMap>, hir_to_implementor: FxHashMap, ingot: IngotId, } @@ -74,7 +74,7 @@ impl TraitEnv { .map(|(_, external)| collect_trait_impls(db, *external)) .chain(std::iter::once(collect_trait_impls(db, ingot))) { - // `collect_trait_impls` ensure that there are no conflicting impls, so we can + // `collect_trait_impls` ensures that there are no conflicting impls, so we can // just extend the map. for (trait_def, implementors) in impl_map.iter() { impls @@ -144,9 +144,9 @@ impl Implementor { table: &mut UnificationTable, ) -> (Self, impl Subst) { let mut subst = FxHashMap::default(); - for param in self.params(db) { - let var = table.new_var(param.kind(db)); - subst.insert(*param, var); + for ¶m in self.params(db) { + let var = table.new_var_from_param(db, param); + subst.insert(param, var); } let hir_impl = self.hir_impl_trait(db); @@ -279,15 +279,21 @@ impl TraitInstId { #[salsa::tracked] pub struct TraitDef { pub trait_: Trait, - #[return_ref] - pub params: Vec, - /// We collects self type here to know the expected kind of implementor - /// type in `Implementor` lowering phase. - pub self_param: TyId, + pub(crate) param_set: GenericParamTypeSet, #[return_ref] pub methods: BTreeMap, } +impl TraitDef { + pub fn params(self, db: &dyn HirAnalysisDb) -> &[TyId] { + self.param_set(db).params(db) + } + + pub fn self_param(self, db: &dyn HirAnalysisDb) -> TyId { + self.param_set(db).trait_self(db).unwrap() + } +} + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct TraitMethod(pub FuncDef); diff --git a/crates/hir-analysis/src/ty/trait_lower.rs b/crates/hir-analysis/src/ty/trait_lower.rs index 818f64f197..ed5f3940e7 100644 --- a/crates/hir-analysis/src/ty/trait_lower.rs +++ b/crates/hir-analysis/src/ty/trait_lower.rs @@ -7,18 +7,22 @@ use hir::hir_def::{ }; use rustc_hash::FxHashMap; -use crate::{ - name_resolution::{resolve_path_early, EarlyResolvedPath, NameDomain, NameResKind}, - ty::ty_lower::{lower_func, lower_hir_ty}, - HirAnalysisDb, -}; - use super::{ trait_def::{Implementor, TraitDef, TraitInstId, TraitMethod}, ty_def::{FuncDef, InvalidCause, Kind, TyId}, - ty_lower::{collect_generic_params, lower_generic_arg_list, GenericParamOwnerId}, + ty_lower::{ + collect_generic_params, lower_generic_arg_list, GenericParamOwnerId, GenericParamTypeSet, + }, unify::UnificationTable, }; +use crate::{ + name_resolution::{resolve_path_early, EarlyResolvedPath, NameDomain, NameResKind}, + ty::{ + ty_def::TyData, + ty_lower::{lower_func, lower_hir_ty}, + }, + HirAnalysisDb, +}; type TraitImplTable = FxHashMap>; @@ -28,20 +32,20 @@ pub(crate) fn lower_trait(db: &dyn HirAnalysisDb, trait_: Trait) -> TraitDef { } /// Collect all trait implementors in the ingot. -/// The returned table doesn't contain the dependent(external) ingot +/// The returned table doesn't contain the const(external) ingot /// implementors. If you need to obtain the environment that contains all /// available implementors in the ingot, please use /// [`TraitEnv`](super::trait_def::TraitEnv). #[salsa::tracked(return_ref)] pub(crate) fn collect_trait_impls(db: &dyn HirAnalysisDb, ingot: IngotId) -> TraitImplTable { - let dependent_impls = ingot + let const_impls = ingot .external_ingots(db.as_hir_db()) .iter() .map(|(_, external)| collect_trait_impls(db, *external)) .collect(); let impl_traits = ingot.all_impl_traits(db.as_hir_db()); - ImplementorCollector::new(db, dependent_impls).collect(impl_traits) + ImplementorCollector::new(db, const_impls).collect(impl_traits) } /// Returns the corresponding implementors for the given [`ImplTrait`]. @@ -75,15 +79,9 @@ pub(crate) fn lower_impl_trait( } let param_owner = GenericParamOwnerId::new(db, impl_trait.into()); - let params = collect_generic_params(db, param_owner); + let params = collect_generic_params(db, param_owner).params(db).to_vec(); - Some(Implementor::new( - db, - trait_, - ty, - params.params.clone(), - impl_trait, - )) + Some(Implementor::new(db, trait_, ty, params, impl_trait)) } /// Lower a trait reference to a trait instance. @@ -94,26 +92,26 @@ pub(crate) fn lower_trait_ref( scope: ScopeId, ) -> Result { let hir_db = db.as_hir_db(); - let args = if let Some(args) = trait_ref.generic_args(hir_db) { + let mut args = if let Some(args) = trait_ref.generic_args(hir_db) { lower_generic_arg_list(db, args, scope) } else { vec![] }; let Partial::Present(path) = trait_ref.path(hir_db) else { - return Err(TraitRefLowerError::TraitNotFound); + return Err(TraitRefLowerError::Other); }; let trait_def = match resolve_path_early(db, path, scope) { EarlyResolvedPath::Full(bucket) => match bucket.pick(NameDomain::Type) { Ok(res) => { let NameResKind::Scope(ScopeId::Item(ItemKind::Trait(trait_))) = res.kind else { - return Err(TraitRefLowerError::TraitNotFound); + return Err(TraitRefLowerError::Other); }; lower_trait(db, trait_) } - Err(_) => return Err(TraitRefLowerError::TraitNotFound), + Err(_) => return Err(TraitRefLowerError::Other), }, EarlyResolvedPath::Partial { .. } => { @@ -128,13 +126,45 @@ pub(crate) fn lower_trait_ref( }); } - for (param, arg) in trait_def.params(db).iter().zip(args.iter()) { + for (param, arg) in trait_def.params(db).iter().zip(args.iter_mut()) { if !param.kind(db).does_match(arg.kind(db)) { - return Err(TraitRefLowerError::ArgumentKindMisMatch { + return Err(TraitRefLowerError::ArgKindMisMatch { expected: param.kind(db).clone(), given: *arg, }); } + + let expected_const_ty = match param.data(db) { + TyData::ConstTy(expected_ty) => expected_ty.ty(db).into(), + _ => None, + }; + + match arg.evaluate_const_ty(db, expected_const_ty) { + Ok(ty) => *arg = ty, + + Err(InvalidCause::ConstTyMismatch { expected, given }) => { + return Err(TraitRefLowerError::ArgTypeMismatch { + expected: Some(expected), + given: Some(given), + }); + } + + Err(InvalidCause::ConstTyExpected { expected }) => { + return Err(TraitRefLowerError::ArgTypeMismatch { + expected: Some(expected), + given: None, + }); + } + + Err(InvalidCause::NormalTypeExpected { given }) => { + return Err(TraitRefLowerError::ArgTypeMismatch { + expected: None, + given: Some(given), + }) + } + + _ => return Err(TraitRefLowerError::Other), + } } let ingot = scope.ingot(hir_db); @@ -159,10 +189,6 @@ pub(crate) fn collect_implementor_methods( #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) enum TraitRefLowerError { - /// The trait reference is not a valid trait reference. This error is - /// reported by the name resolution and no need to report it again. - TraitNotFound, - /// The trait reference contains an associated type, which is not supported /// yet. AssocTy(PathId), @@ -172,24 +198,35 @@ pub(crate) enum TraitRefLowerError { /// The kind of the argument doesn't match the kind of the parameter of the /// trait. - ArgumentKindMisMatch { expected: Kind, given: TyId }, + ArgKindMisMatch { expected: Kind, given: TyId }, + + /// The argument type doesn't match the const parameter type. + ArgTypeMismatch { + expected: Option, + given: Option, + }, + + /// Other errors, which is reported by another pass. So we don't need to + /// report this error kind. + Other, } struct TraitBuilder<'db> { db: &'db dyn HirAnalysisDb, trait_: Trait, - params: Vec, - self_arg: TyId, + param_set: GenericParamTypeSet, methods: BTreeMap, } impl<'db> TraitBuilder<'db> { fn new(db: &'db dyn HirAnalysisDb, trait_: Trait) -> Self { + let params_owner_id = GenericParamOwnerId::new(db, trait_.into()); + let param_set = collect_generic_params(db, params_owner_id); + Self { db, trait_, - params: vec![], - self_arg: TyId::invalid(db, InvalidCause::Other), + param_set, methods: BTreeMap::default(), } } @@ -198,20 +235,12 @@ impl<'db> TraitBuilder<'db> { self.collect_params(); self.collect_methods(); - TraitDef::new( - self.db, - self.trait_, - self.params, - self.self_arg, - self.methods, - ) + TraitDef::new(self.db, self.trait_, self.param_set, self.methods) } fn collect_params(&mut self) { let params_owner_id = GenericParamOwnerId::new(self.db, self.trait_.into()); - let params_set = collect_generic_params(self.db, params_owner_id); - self.params = params_set.params.clone(); - self.self_arg = params_set.trait_self.unwrap(); + self.param_set = collect_generic_params(self.db, params_owner_id); } fn collect_methods(&mut self) { @@ -234,15 +263,15 @@ impl<'db> TraitBuilder<'db> { struct ImplementorCollector<'db> { db: &'db dyn HirAnalysisDb, impl_table: TraitImplTable, - dependent_impl_maps: Vec<&'db TraitImplTable>, + const_impl_maps: Vec<&'db TraitImplTable>, } impl<'db> ImplementorCollector<'db> { - fn new(db: &'db dyn HirAnalysisDb, dependent_impl_maps: Vec<&'db TraitImplTable>) -> Self { + fn new(db: &'db dyn HirAnalysisDb, const_impl_maps: Vec<&'db TraitImplTable>) -> Self { Self { db, impl_table: TraitImplTable::default(), - dependent_impl_maps, + const_impl_maps, } } @@ -267,7 +296,7 @@ impl<'db> ImplementorCollector<'db> { fn does_conflict(&mut self, implementor: Implementor) -> bool { let def = implementor.trait_def(self.db); for impl_map in self - .dependent_impl_maps + .const_impl_maps .iter() .chain(std::iter::once(&&self.impl_table)) { diff --git a/crates/hir-analysis/src/ty/ty_def.rs b/crates/hir-analysis/src/ty/ty_def.rs index 8caac0585e..59e06bee38 100644 --- a/crates/hir-analysis/src/ty/ty_def.rs +++ b/crates/hir-analysis/src/ty/ty_def.rs @@ -7,30 +7,31 @@ use hir::{ kw, prim_ty::{IntTy as HirIntTy, PrimTy as HirPrimTy, UintTy as HirUintTy}, scope_graph::ScopeId, - Contract, Enum, Func, FuncParam as HirFuncParam, IdentId, IngotId, ItemKind, Partial, + Body, Contract, Enum, Func, FuncParam as HirFuncParam, IdentId, IngotId, ItemKind, Partial, Struct, TypeAlias as HirTypeAlias, TypeId as HirTyId, VariantKind, }, span::DynLazySpan, }; use rustc_hash::FxHashMap; -use crate::{ - ty::constraint_solver::{check_ty_app_sat, GoalSatisfiability}, - HirAnalysisDb, -}; - use super::{ + const_ty::{ConstTyData, ConstTyId}, constraint::{ collect_adt_constraints, collect_func_def_constraints, AssumptionListId, ConstraintListId, }, diagnostics::{TraitConstraintDiag, TyDiagCollection, TyLowerDiag}, - ty_lower::{lower_hir_ty, GenericParamOwnerId}, + ty_lower::{lower_hir_ty, GenericParamOwnerId, GenericParamTypeSet}, unify::{InferenceKey, UnificationTable}, visitor::TyVisitor, }; +use crate::{ + ty::constraint_solver::{check_ty_app_sat, GoalSatisfiability}, + HirAnalysisDb, +}; #[salsa::interned] pub struct TyId { + #[return_ref] pub data: TyData, } @@ -47,6 +48,22 @@ impl TyId { matches!(self.data(db), TyData::Invalid(_)) } + /// Returns `true` if the type is an integral type(like `u32`, `i32` etc.) + pub fn is_integral(self, db: &dyn HirAnalysisDb) -> bool { + match self.data(db) { + TyData::TyBase(ty_base) => ty_base.is_integral(), + _ => false, + } + } + + /// Returns `true` if the type is a bool type. + pub fn is_bool(self, db: &dyn HirAnalysisDb) -> bool { + match self.data(db) { + TyData::TyBase(ty_base) => ty_base.is_bool(), + _ => false, + } + } + /// Returns `IngotId` that declares the type. pub fn ingot(self, db: &dyn HirAnalysisDb) -> Option { match self.data(db) { @@ -59,7 +76,7 @@ impl TyId { pub fn invalid_cause(self, db: &dyn HirAnalysisDb) -> Option { match self.data(db) { - TyData::Invalid(cause) => Some(cause), + TyData::Invalid(cause) => Some(cause.clone()), _ => None, } } @@ -69,13 +86,13 @@ impl TyId { match self.data(db) { TyData::Invalid(_) => true, TyData::TyApp(lhs, rhs) => lhs.contains_invalid(db) || rhs.contains_invalid(db), + TyData::ConstTy(const_ty) => const_ty.ty(db).contains_invalid(db), _ => false, } } - /// Returns `true` if the type is declared as a monotype or fully applied - /// type. - pub fn is_mono_type(self, db: &dyn HirAnalysisDb) -> bool { + /// Returns `true` if the type has a `*` kind. + pub fn has_star_kind(self, db: &dyn HirAnalysisDb) -> bool { !matches!(self.kind(db), Kind::Abs(_, _)) } @@ -92,7 +109,7 @@ impl TyId { pub(super) fn base_ty(self, db: &dyn HirAnalysisDb) -> Option { match self.decompose_ty_app(db).0.data(db) { - TyData::TyBase(concrete) => Some(concrete), + TyData::TyBase(concrete) => Some(*concrete), _ => None, } } @@ -105,10 +122,19 @@ impl TyId { Self::new(db, TyData::TyBase(TyBase::tuple(n))) } + pub(super) fn array(db: &dyn HirAnalysisDb) -> Self { + let base = TyBase::Prim(PrimTy::Array); + Self::new(db, TyData::TyBase(base)) + } + pub(super) fn unit(db: &dyn HirAnalysisDb) -> Self { Self::tuple(db, 0) } + pub(super) fn const_ty(db: &dyn HirAnalysisDb, const_ty: ConstTyId) -> Self { + Self::new(db, TyData::ConstTy(const_ty)) + } + pub(super) fn adt(db: &dyn HirAnalysisDb, adt: AdtDef) -> Self { Self::new(db, TyData::TyBase(TyBase::Adt(adt))) } @@ -121,12 +147,12 @@ impl TyId { matches!(self.data(db), TyData::TyParam(ty_param) if ty_param.is_trait_self()) } + pub(super) fn is_const_ty(self, db: &dyn HirAnalysisDb) -> bool { + matches!(self.data(db), TyData::ConstTy(_)) + } + pub(super) fn contains_ty_param(self, db: &dyn HirAnalysisDb) -> bool { - match self.data(db) { - TyData::TyParam(_) => true, - TyData::TyApp(lhs, rhs) => lhs.contains_ty_param(db) || rhs.contains_ty_param(db), - _ => false, - } + !self.type_params(db).is_empty() } pub(super) fn contains_trait_self(self, db: &dyn HirAnalysisDb) -> bool { @@ -140,9 +166,9 @@ impl TyId { pub(super) fn generalize(self, db: &dyn HirAnalysisDb, table: &mut UnificationTable) -> Self { let params = self.type_params(db); let mut subst = FxHashMap::default(); - for param in params.iter() { - let new_var = table.new_var(param.kind(db)); - subst.insert(*param, new_var); + for ¶m in params.iter() { + let new_var = table.new_var_from_param(db, param); + subst.insert(param, new_var); } self.apply_subst(db, &mut subst) @@ -154,36 +180,65 @@ impl TyId { db: &dyn HirAnalysisDb, span: DynLazySpan, ) -> Option { - match self.data(db) { - TyData::TyApp(lhs, rhs) => { - if let Some(diag) = lhs.emit_diag(db, span.clone()) { - Some(diag) - } else { - rhs.emit_diag(db, span) - } - } + struct EmitDiagVisitor { + diag: Option, + span: DynLazySpan, + } - TyData::Invalid(cause) => match cause { - InvalidCause::NotFullyApplied => { - Some(TyLowerDiag::not_fully_applied_type(span).into()) - } + impl TyVisitor for EmitDiagVisitor { + fn visit_invalid(&mut self, db: &dyn HirAnalysisDb, cause: &InvalidCause) { + let span = self.span.clone(); + let diag = match cause { + InvalidCause::NotFullyApplied => { + TyLowerDiag::expected_star_kind_ty(span).into() + } - InvalidCause::KindMismatch { expected, given } => { - Some(TyLowerDiag::invalid_type_arg_kind(db, span, expected, given).into()) - } + InvalidCause::KindMismatch { expected, given } => { + TyLowerDiag::invalid_type_arg_kind(db, span, expected.clone(), *given) + .into() + } - InvalidCause::UnboundTypeAliasParam { - alias, - n_given_args: n_given_arg, - } => Some(TyLowerDiag::unbound_type_alias_param(span, alias, n_given_arg).into()), + InvalidCause::InvalidConstParamTy { ty } => { + TyLowerDiag::invalid_const_param_ty(db, span, *ty).into() + } - InvalidCause::AssocTy => Some(TyLowerDiag::assoc_ty(span).into()), + InvalidCause::RecursiveConstParamTy => { + TyLowerDiag::RecursiveConstParamTy(span).into() + } - InvalidCause::Other => None, - }, + InvalidCause::ConstTyMismatch { expected, given } => { + TyLowerDiag::const_ty_mismatch(db, span, *expected, *given).into() + } - _ => None, + InvalidCause::ConstTyExpected { expected } => { + TyLowerDiag::const_ty_expected(db, span, *expected).into() + } + + InvalidCause::NormalTypeExpected { given } => { + TyLowerDiag::normal_type_expected(db, span, *given).into() + } + + InvalidCause::UnboundTypeAliasParam { + alias, + n_given_args: n_given_arg, + } => TyLowerDiag::unbound_type_alias_param(span, *alias, *n_given_arg).into(), + + InvalidCause::AssocTy => TyLowerDiag::assoc_ty(span).into(), + + InvalidCause::InvalidConstTyExpr { body } => { + TyLowerDiag::InvalidConstTyExpr(body.lazy_span().into()).into() + } + + InvalidCause::Other => return, + }; + + self.diag.get_or_insert(diag); + } } + + let mut visitor = EmitDiagVisitor { diag: None, span }; + visitor.visit_ty(db, self); + visitor.diag } pub(super) fn emit_sat_diag( @@ -217,21 +272,47 @@ impl TyId { collect_type_params(db, self) } - pub(super) fn ty_var(db: &dyn HirAnalysisDb, kind: Kind, key: InferenceKey) -> Self { - Self::new(db, TyData::TyVar(TyVar { kind, key })) + pub(super) fn ty_var( + db: &dyn HirAnalysisDb, + universe: TyVarUniverse, + kind: Kind, + key: InferenceKey, + ) -> Self { + Self::new( + db, + TyData::TyVar(TyVar { + universe, + kind, + key, + }), + ) + } + + pub(super) fn const_ty_var(db: &dyn HirAnalysisDb, ty: TyId, key: InferenceKey) -> Self { + let ty_var = TyVar { + universe: TyVarUniverse::General, + kind: ty.kind(db).clone(), + key, + }; + + let data = ConstTyData::TyVar(ty_var, ty); + Self::new(db, TyData::ConstTy(ConstTyId::new(db, data))) } /// Perform type level application. - /// If the kind is mismatched, return `TyData::Invalid`. pub(super) fn app(db: &dyn HirAnalysisDb, abs: Self, arg: Self) -> TyId { let k_abs = abs.kind(db); let k_arg = arg.kind(db); + let arg = arg + .evaluate_const_ty(db, abs.expected_const_ty(db)) + .unwrap_or_else(|cause| Self::invalid(db, cause)); + let arg = match k_abs { Kind::Abs(k_expected, _) if k_expected.as_ref().does_match(k_arg) => arg, - Kind::Abs(k_abs_arg, _) => Self::invalid( + Kind::Abs(k_expected, _) => Self::invalid( db, - InvalidCause::kind_mismatch(k_abs_arg.as_ref().into(), arg), + InvalidCause::kind_mismatch(k_expected.as_ref().into(), arg), ), Kind::Star => Self::invalid(db, InvalidCause::kind_mismatch(None, arg)), Kind::Any => arg, @@ -302,6 +383,64 @@ impl TyId { }, } } + + pub(super) fn const_ty_param(self, db: &dyn HirAnalysisDb) -> Option { + if let TyData::ConstTy(const_ty) = self.data(db) { + Some(const_ty.ty(db)) + } else { + None + } + } + + pub(super) fn evaluate_const_ty( + self, + db: &dyn HirAnalysisDb, + expected_ty: Option, + ) -> Result { + match (expected_ty, self.data(db)) { + (Some(expected_const_ty), TyData::ConstTy(const_ty)) => { + if expected_const_ty.is_invalid(db) { + Err(InvalidCause::Other) + } else { + let evaluated_const_ty = const_ty.evaluate(db, expected_const_ty.into()); + let evaluated_const_ty_ty = evaluated_const_ty.ty(db); + if let Some(cause) = evaluated_const_ty_ty.invalid_cause(db) { + Err(cause) + } else { + Ok(TyId::const_ty(db, evaluated_const_ty)) + } + } + } + + (Some(expected_const_ty), _) => { + if expected_const_ty.is_invalid(db) { + Err(InvalidCause::Other) + } else { + Err(InvalidCause::ConstTyExpected { + expected: expected_const_ty, + }) + } + } + + (None, TyData::ConstTy(const_ty)) => { + let evaluated_const_ty = const_ty.evaluate(db, None); + Err(InvalidCause::NormalTypeExpected { + given: TyId::const_ty(db, evaluated_const_ty), + }) + } + + (None, _) => Ok(self), + } + } + + /// Returns the next expected type of a type argument. + fn expected_const_ty(self, db: &dyn HirAnalysisDb) -> Option { + let (base, args) = self.decompose_ty_app(db); + match base.data(db) { + TyData::TyBase(ty_base) => ty_base.expected_const_ty(db, args.len()), + _ => None, + } + } } /// Represents a ADT type definition. @@ -310,8 +449,7 @@ pub struct AdtDef { pub adt_ref: AdtRefId, /// Type parameters of the ADT. - #[return_ref] - pub params: Vec, + param_set: GenericParamTypeSet, /// Fields of the ADT, if the ADT is an enum, this represents variants. #[return_ref] @@ -323,6 +461,10 @@ impl AdtDef { self.adt_ref(db).name(db) } + pub(crate) fn params(self, db: &dyn HirAnalysisDb) -> &[TyId] { + self.param_set(db).params(db) + } + pub(crate) fn variant_ty_span( self, db: &dyn HirAnalysisDb, @@ -376,6 +518,16 @@ impl AdtDef { pub(super) fn constraints(self, db: &dyn HirAnalysisDb) -> ConstraintListId { collect_adt_constraints(db, self) } + + /// Returns the expected type of the `idx`-th type parameter when the + /// parameter is declared as a const type. + fn expected_const_ty(self, db: &dyn HirAnalysisDb, idx: usize) -> Option { + let TyData::ConstTy(const_ty) = self.params(db).get(idx)?.data(db) else { + return None; + }; + + Some(const_ty.ty(db)) + } } #[salsa::tracked] @@ -384,9 +536,7 @@ pub struct FuncDef { pub name: IdentId, - /// Generic parameters of the function. - #[return_ref] - pub params: Vec, + params_set: GenericParamTypeSet, /// Argument types of the function. #[return_ref] @@ -403,9 +553,13 @@ impl FuncDef { .ingot(db.as_hir_db()) } + pub fn params(self, db: &dyn HirAnalysisDb) -> &[TyId] { + self.params_set(db).params(db) + } + pub fn receiver_ty(self, db: &dyn HirAnalysisDb) -> Option { if self.hir_func(db).is_method(db.as_hir_db()) { - self.arg_tys(db).get(0).copied() + self.arg_tys(db).first().copied() } else { None } @@ -423,6 +577,16 @@ impl FuncDef { .map(|list| list.data(db.as_hir_db()).as_ref()) .unwrap_or(EMPTY) } + + /// Returns the expected type of the `idx`-th type parameter when the + /// parameter is declared as a const type. + fn expected_const_ty(self, db: &dyn HirAnalysisDb, idx: usize) -> Option { + let TyData::ConstTy(const_ty) = self.params(db).get(idx)?.data(db) else { + return None; + }; + + Some(const_ty.ty(db)) + } } /// This struct represents a field of an ADT. If the ADT is an enum, this @@ -484,10 +648,7 @@ pub enum TyData { /// A concrete type, e.g., `i32`, `u32`, `bool`, `String`, `Result` etc. TyBase(TyBase), - // TODO: DependentTy, - // TermTy(TermTy) - // DependentTyParam(TyParam, TyConst), - // DependentTyVar(TyVar, TyConst), + ConstTy(ConstTyId), // Invalid type which means the type is ill-formed. // This type can be unified with any other types. @@ -501,10 +662,32 @@ pub enum InvalidCause { NotFullyApplied, /// Kind mismatch between two types. - KindMismatch { expected: Option, given: TyId }, + KindMismatch { + expected: Option, + given: TyId, + }, - /// Associated Type is not allowed at the moment. - AssocTy, + InvalidConstParamTy { + ty: TyId, + }, + + RecursiveConstParamTy, + + /// The given type doesn't match the expected const type. + ConstTyMismatch { + expected: TyId, + given: TyId, + }, + + /// The given type is not a const type where it is required. + ConstTyExpected { + expected: TyId, + }, + + /// The given type is const type where it is *NOT* required. + NormalTypeExpected { + given: TyId, + }, /// Type alias parameter is not bound. /// NOTE: In our type system, type alias is a macro, so we can't perform @@ -514,6 +697,16 @@ pub enum InvalidCause { n_given_args: usize, }, + /// Associated Type is not allowed at the moment. + AssocTy, + + // The given expression is not supported yet in the const type context. + // TODO: Remove this error kind and introduce a new error kind for more specific cause when + // type inference is implemented. + InvalidConstTyExpr { + body: Body, + }, + // TraitConstraintNotSat(PredicateId), /// `Other` indicates the cause is already reported in other analysis /// passes, e.g., parser or name resolution. @@ -573,10 +766,31 @@ impl fmt::Display for Kind { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TyVar { + pub universe: TyVarUniverse, pub kind: Kind, pub(super) key: InferenceKey, } +/// Represents the universe of a type variable that indicates what type domain +/// can be unified with the type variable. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum TyVarUniverse { + /// Type variable that can be unified with any other types. + General, + + /// Type variable that can be unified with only integral types. + Integral, +} + +impl TyVar { + pub(super) fn pretty_print(&self) -> String { + match self.universe { + TyVarUniverse::General => format!("${}", self.key.0), + TyVarUniverse::Integral => "".to_string(), + } + } +} + /// Type generics parameter. We also treat `Self` type in a trait definition as /// a special type parameter. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -589,6 +803,10 @@ pub struct TyParam { } impl TyParam { + pub(super) fn pretty_print(&self, db: &dyn HirAnalysisDb) -> String { + self.name.data(db.as_hir_db()).to_string() + } + pub fn self_ty_param(kind: Kind) -> Self { Self { name: kw::SELF_TY, @@ -610,10 +828,28 @@ pub enum TyBase { } impl TyBase { + pub fn is_integral(self) -> bool { + match self { + Self::Prim(prim) => prim.is_integral(), + _ => false, + } + } + + pub fn is_bool(self) -> bool { + match self { + Self::Prim(prim) => prim.is_bool(), + _ => false, + } + } + pub(super) fn tuple(n: usize) -> Self { Self::Prim(PrimTy::Tuple(n)) } + pub(super) fn bool() -> Self { + Self::Prim(PrimTy::Bool) + } + fn pretty_print(&self, db: &dyn HirAnalysisDb) -> String { match self { Self::Prim(prim) => match prim { @@ -648,6 +884,16 @@ impl TyBase { .to_string(), } } + + /// Returns the expected type of the `idx`-th type parameter when the + /// parameter is declared as a const type. + fn expected_const_ty(&self, db: &dyn HirAnalysisDb, idx: usize) -> Option { + match self { + Self::Prim(prim_ty) => prim_ty.expected_const_ty(db, idx), + Self::Adt(adt) => adt.expected_const_ty(db, idx), + Self::Func(func) => func.expected_const_ty(db, idx), + } + } } #[derive(Debug, Clone, PartialEq, Copy, Eq, Hash)] @@ -671,6 +917,43 @@ pub enum PrimTy { Ptr, } +impl PrimTy { + pub fn is_integral(self) -> bool { + matches!( + self, + Self::U8 + | Self::U16 + | Self::U32 + | Self::U64 + | Self::U128 + | Self::U256 + | Self::I8 + | Self::I16 + | Self::I32 + | Self::I64 + | Self::I128 + | Self::I256 + ) + } + + pub fn is_bool(self) -> bool { + matches!(self, Self::Bool) + } + + /// Returns the expected type of the `idx`-th type parameter when the + /// parameter is declared as a const type. + fn expected_const_ty(self, db: &dyn HirAnalysisDb, idx: usize) -> Option { + match self { + Self::Array if idx == 1 => { + // FIXME: Change `U256` to `usize` when we add `usize` type. + Some(TyId::new(db, TyData::TyBase(TyBase::Prim(PrimTy::U256)))) + } + Self::Tuple(_) => None, + _ => None, + } + } +} + #[salsa::interned] pub struct AdtRefId { pub data: AdtRef, @@ -774,6 +1057,9 @@ impl HasKind for TyData { Kind::Abs(_, ret) => ret.as_ref().clone(), _ => Kind::Any, }, + + TyData::ConstTy(const_ty) => const_ty.ty(db).kind(db).clone(), + TyData::Invalid(_) => Kind::Any, } } @@ -850,6 +1136,17 @@ pub(crate) fn collect_type_params(db: &dyn HirAnalysisDb, ty: TyId) -> BTreeSet< let param_ty = TyId::new(_db, TyData::TyParam(param.clone())); self.0.insert(param_ty); } + + fn visit_const_param( + &mut self, + db: &dyn HirAnalysisDb, + param: &TyParam, + const_ty_ty: TyId, + ) { + let const_ty = ConstTyId::new(db, ConstTyData::TyParam(param.clone(), const_ty_ty)); + let const_param_ty = TyId::new(db, TyData::ConstTy(const_ty)); + self.0.insert(const_param_ty); + } } let mut collector = TypeParamCollector(BTreeSet::new()); @@ -860,10 +1157,11 @@ pub(crate) fn collect_type_params(db: &dyn HirAnalysisDb, ty: TyId) -> BTreeSet< #[salsa::tracked(return_ref)] pub(crate) fn pretty_print_ty(db: &dyn HirAnalysisDb, ty: TyId) -> String { match ty.data(db) { - TyData::TyVar(var) => format!("%{}", var.key.0), - TyData::TyParam(param) => param.name.data(db.as_hir_db()).to_string(), + TyData::TyVar(var) => var.pretty_print(), + TyData::TyParam(param) => param.pretty_print(db), TyData::TyApp(_, _) => pretty_print_ty_app(db, ty), TyData::TyBase(ty_con) => ty_con.pretty_print(db), + TyData::ConstTy(const_ty) => const_ty.pretty_print(db), _ => "".to_string(), } } @@ -923,8 +1221,8 @@ fn decompose_ty_app(db: &dyn HirAnalysisDb, ty: TyId) -> (TyId, Vec) { fn visit_ty(&mut self, db: &dyn HirAnalysisDb, ty: TyId) { match ty.data(db) { TyData::TyApp(lhs, rhs) => { - self.visit_ty(db, lhs); - self.args.push(rhs); + self.visit_ty(db, *lhs); + self.args.push(*rhs); } _ => self.base = Some(ty), } @@ -942,7 +1240,7 @@ fn decompose_ty_app(db: &dyn HirAnalysisDb, ty: TyId) -> (TyId, Vec) { base: None, args: Vec::new(), }; - decomposer.visit_app(db, lhs, rhs); + decomposer.visit_app(db, *lhs, *rhs); ( decomposer.base.unwrap(), decomposer.args.into_iter().collect(), diff --git a/crates/hir-analysis/src/ty/ty_lower.rs b/crates/hir-analysis/src/ty/ty_lower.rs index f097551a0f..74fd0751cd 100644 --- a/crates/hir-analysis/src/ty/ty_lower.rs +++ b/crates/hir-analysis/src/ty/ty_lower.rs @@ -10,21 +10,32 @@ use hir::hir_def::{ use rustc_hash::FxHashMap; use salsa::function::Configuration; +use super::{ + const_ty::{ConstTyData, ConstTyId}, + ty_def::{ + AdtDef, AdtField, AdtRef, AdtRefId, FuncDef, InvalidCause, Kind, TyData, TyId, TyParam, + }, +}; use crate::{ name_resolution::{resolve_path_early, EarlyResolvedPath, NameDomain, NameResKind}, HirAnalysisDb, }; -use super::ty_def::{ - AdtDef, AdtField, AdtRef, AdtRefId, FuncDef, InvalidCause, Kind, TyData, TyId, TyParam, -}; - /// Lowers the given HirTy to `TyId`. -#[salsa::tracked] +#[salsa::tracked(recovery_fn=recover_lower_hir_ty_cycle)] pub fn lower_hir_ty(db: &dyn HirAnalysisDb, ty: HirTyId, scope: ScopeId) -> TyId { TyBuilder::new(db, scope).lower_ty(ty) } +fn recover_lower_hir_ty_cycle( + db: &dyn HirAnalysisDb, + _cycle: &salsa::Cycle, + _ty: HirTyId, + _scope: ScopeId, +) -> TyId { + TyId::invalid(db, InvalidCause::RecursiveConstParamTy) +} + /// Lower HIR ADT definition(`struct/enum/contract`) to [`AdtDef`]. #[salsa::tracked] pub fn lower_adt(db: &dyn HirAnalysisDb, adt: AdtRefId) -> AdtDef { @@ -34,9 +45,7 @@ pub fn lower_adt(db: &dyn HirAnalysisDb, adt: AdtRefId) -> AdtDef { #[salsa::tracked] pub fn lower_func(db: &dyn HirAnalysisDb, func: Func) -> Option { let name = func.name(db.as_hir_db()).to_opt()?; - let generic_params = collect_generic_params(db, GenericParamOwnerId::new(db, func.into())) - .params - .clone(); + let params_set = collect_generic_params(db, GenericParamOwnerId::new(db, func.into())); let args = match func.params(db.as_hir_db()) { Partial::Present(args) => args @@ -57,11 +66,11 @@ pub fn lower_func(db: &dyn HirAnalysisDb, func: Func) -> Option { .map(|ty| lower_hir_ty(db, ty, func.scope())) .unwrap_or_else(|| TyId::unit(db)); - Some(FuncDef::new(db, func, name, generic_params, args, ret_ty)) + Some(FuncDef::new(db, func, name, params_set, args, ret_ty)) } /// Collects the generic parameters of the given generic parameter owner. -#[salsa::tracked(return_ref)] +#[salsa::tracked] pub(crate) fn collect_generic_params( db: &dyn HirAnalysisDb, owner: GenericParamOwnerId, @@ -75,13 +84,13 @@ pub(crate) fn lower_type_alias( db: &dyn HirAnalysisDb, alias: HirTypeAlias, ) -> Result { - let params = collect_generic_params(db, GenericParamOwnerId::new(db, alias.into())); + let param_set = collect_generic_params(db, GenericParamOwnerId::new(db, alias.into())); let Some(hir_ty) = alias.ty(db.as_hir_db()).to_opt() else { return Ok(TyAlias { alias, alias_to: TyId::invalid(db, InvalidCause::Other), - params: params.params.clone(), + param_set, }); }; @@ -94,33 +103,45 @@ pub(crate) fn lower_type_alias( Ok(TyAlias { alias, alias_to, - params: params.params.clone(), + param_set, }) } +#[doc(hidden)] +#[salsa::tracked(return_ref)] +pub(crate) fn evaluate_params_precursor( + db: &dyn HirAnalysisDb, + set: GenericParamTypeSet, +) -> Vec { + set.params_precursor(db) + .iter() + .map(|p| p.evaluate(db, set.scope(db))) + .collect() +} + fn recover_lower_type_alias_cycle( db: &dyn HirAnalysisDb, cycle: &salsa::Cycle, _alias: HirTypeAlias, ) -> Result { + use once_cell::sync::Lazy; + use regex::Regex; + + // Filter out the cycle participants that are not type aliases. + // This is inefficient workaround because it's not possible to obtain the + // ingredient index of the tracked function. Please refer to https://github.com/salsa-rs/salsa/pull/461 for more details. + static RE: Lazy = Lazy::new(|| Regex::new(r"lower_type_alias\(?(\d)\)").unwrap()); let alias_cycle = cycle - .participant_keys() - .filter_map(|key| { - let ingredient_index = key.ingredient_index(); - // This is temporary fragile solution to filter out the cycle participants, the - // correctness of this strategy is based on the fact that cycle participants are - // only `lower_type_alias` and `lower_hir_ty` functions. - // TODO: We can refactor here if https://github.com/salsa-rs/salsa/pull/461 is approved. - if matches!( - db.cycle_recovery_strategy(ingredient_index), - salsa::cycle::CycleRecoveryStrategy::Fallback - ) { - Some(lower_type_alias::key_from_id(key.key_index())) - } else { - None - } + .all_participants(db) + .into_iter() + .filter_map(|participant| { + let caps = RE.captures(&participant)?; + let id = caps.get(1)?; + let id = salsa::Id::from_u32(id.as_str().parse().ok()?); + Some(lower_type_alias::key_from_id(id)) }) .collect(); + Err(AliasCycle(alias_cycle)) } @@ -147,12 +168,16 @@ impl AliasCycle { pub(crate) struct TyAlias { alias: HirTypeAlias, alias_to: TyId, - params: Vec, + param_set: GenericParamTypeSet, } impl TyAlias { + fn params<'db>(&self, db: &'db dyn HirAnalysisDb) -> &'db [TyId] { + self.param_set.params(db) + } + fn apply_subst(&self, db: &dyn HirAnalysisDb, arg_tys: &[TyId]) -> TyId { - if arg_tys.len() < self.params.len() { + if arg_tys.len() < self.params(db).len() { return TyId::invalid( db, InvalidCause::UnboundTypeAliasParam { @@ -163,7 +188,7 @@ impl TyAlias { } let mut subst = FxHashMap::default(); - for (¶m, &arg) in self.params.iter().zip(arg_tys.iter()) { + for (¶m, &arg) in self.params(db).iter().zip(arg_tys.iter()) { let arg = if param.kind(db) != arg.kind(db) { TyId::invalid(db, InvalidCause::kind_mismatch(param.kind(db).into(), arg)) } else { @@ -196,8 +221,13 @@ impl<'db> TyBuilder<'db> { HirTyKind::Tuple(tuple_id) => self.lower_tuple(*tuple_id), - HirTyKind::Array(_, _) => { - todo!() + HirTyKind::Array(hir_elem_ty, len) => { + let elem_ty = self.lower_opt_hir_ty(*hir_elem_ty); + let len_ty = ConstTyId::from_opt_body(self.db, *len); + let len_ty = TyId::const_ty(self.db, len_ty); + let array = TyId::array(self.db); + let array_1 = TyId::app(self.db, array, elem_ty); + TyId::app(self.db, array_1, len_ty) } } } @@ -224,7 +254,7 @@ impl<'db> TyBuilder<'db> { let ty = alias.apply_subst(self.db, &arg_tys); if !ty.is_invalid(self.db) { - let param_num = alias.params.len(); + let param_num = alias.params(self.db).len(); arg_tys[param_num..] .iter() .fold(ty, |acc, arg| TyId::app(self.db, acc, *arg)) @@ -268,23 +298,17 @@ impl<'db> TyBuilder<'db> { self.db, GenericParamOwnerId::new(self.db, trait_.into()), ); - params.trait_self.unwrap() + params.trait_self(self.db).unwrap() } ItemKind::Impl(impl_) => { let hir_ty = impl_.ty(self.db.as_hir_db()); - hir_ty - .to_opt() - .map(|hir_ty| lower_hir_ty(self.db, hir_ty, scope)) - .unwrap_or_else(|| TyId::invalid(self.db, InvalidCause::Other)) + self.lower_opt_hir_ty(hir_ty) } ItemKind::ImplTrait(impl_trait) => { let hir_ty = impl_trait.ty(self.db.as_hir_db()); - hir_ty - .to_opt() - .map(|hir_ty| lower_hir_ty(self.db, hir_ty, scope)) - .unwrap_or_else(|| TyId::invalid(self.db, InvalidCause::Other)) + self.lower_opt_hir_ty(hir_ty) } _ => TyId::invalid(self.db, InvalidCause::Other), @@ -300,10 +324,7 @@ impl<'db> TyBuilder<'db> { } fn lower_ptr(&mut self, pointee: Partial) -> TyId { - let pointee = pointee - .to_opt() - .map(|pointee| lower_hir_ty(self.db, pointee, self.scope)) - .unwrap_or_else(|| TyId::invalid(self.db, InvalidCause::Other)); + let pointee = self.lower_opt_hir_ty(pointee); let ptr = TyId::ptr(self.db); TyId::app(self.db, ptr, pointee) @@ -313,12 +334,9 @@ impl<'db> TyBuilder<'db> { let elems = tuple_id.data(self.db.as_hir_db()); let len = elems.len(); let tuple = TyId::tuple(self.db, len); - elems.iter().fold(tuple, |acc, elem| { - let elem_ty = elem - .to_opt() - .map(|elem| lower_hir_ty(self.db, elem, self.scope)) - .unwrap_or_else(|| TyId::invalid(self.db, InvalidCause::Other)); - if !elem_ty.is_mono_type(self.db) { + elems.iter().fold(tuple, |acc, &elem| { + let elem_ty = self.lower_opt_hir_ty(elem); + if !elem_ty.has_star_kind(self.db) { return TyId::invalid(self.db, InvalidCause::NotFullyApplied); } @@ -341,7 +359,7 @@ impl<'db> TyBuilder<'db> { let owner_id = GenericParamOwnerId::new(self.db, owner); let params = collect_generic_params(self.db, owner_id); - return Either::Left(params.params[idx]); + return Either::Left(params.get_param(self.db, idx).unwrap()); } _ => unreachable!(), }; @@ -392,6 +410,13 @@ impl<'db> TyBuilder<'db> { } } } + + fn lower_opt_hir_ty(&self, hir_ty: Partial) -> TyId { + hir_ty + .to_opt() + .map(|hir_ty| lower_hir_ty(self.db, hir_ty, self.scope)) + .unwrap_or_else(|| TyId::invalid(self.db, InvalidCause::Other)) + } } pub(super) fn lower_generic_arg(db: &dyn HirAnalysisDb, arg: &GenericArg, scope: ScopeId) -> TyId { @@ -402,7 +427,10 @@ pub(super) fn lower_generic_arg(db: &dyn HirAnalysisDb, arg: &GenericArg, scope: .map(|ty| lower_hir_ty(db, ty, scope)) .unwrap_or_else(|| TyId::invalid(db, InvalidCause::Other)), - GenericArg::Const(_) => todo!(), + GenericArg::Const(const_arg) => { + let const_ty = ConstTyId::from_opt_body(db, const_arg.body); + TyId::const_ty(db, const_ty) + } } } @@ -420,7 +448,7 @@ pub(super) fn lower_generic_arg_list( struct AdtTyBuilder<'db> { db: &'db dyn HirAnalysisDb, adt: AdtRefId, - params: Vec, + params: GenericParamTypeSet, variants: Vec, } @@ -429,7 +457,7 @@ impl<'db> AdtTyBuilder<'db> { Self { db, adt, - params: Vec::new(), + params: GenericParamTypeSet::empty(db, adt.scope(db)), variants: Vec::new(), } } @@ -448,7 +476,7 @@ impl<'db> AdtTyBuilder<'db> { }; let owner_id = GenericParamOwnerId::new(self.db, owner); - self.params = collect_generic_params(self.db, owner_id).params.clone(); + self.params = collect_generic_params(self.db, owner_id); } fn collect_variants(&mut self) { @@ -502,10 +530,28 @@ impl<'db> AdtTyBuilder<'db> { } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) struct GenericParamTypeSet { - pub(crate) params: Vec, - pub(crate) trait_self: Option, +#[doc(hidden)] +#[salsa::interned] +pub struct GenericParamTypeSet { + params_precursor: Vec, + pub trait_self: Option, + scope: ScopeId, +} + +impl GenericParamTypeSet { + pub(crate) fn params(self, db: &dyn HirAnalysisDb) -> &[TyId] { + evaluate_params_precursor(db, self) + } + + pub(crate) fn empty(db: &dyn HirAnalysisDb, scope: ScopeId) -> Self { + Self::new(db, Vec::new(), None, scope) + } + + fn get_param(&self, db: &dyn HirAnalysisDb, idx: usize) -> Option { + self.params_precursor(db) + .get(idx) + .map(|p| p.evaluate(db, self.scope(db))) + } } struct GenericParamCollector<'db> { @@ -518,11 +564,7 @@ struct GenericParamCollector<'db> { impl<'db> GenericParamCollector<'db> { fn new(db: &'db dyn HirAnalysisDb, owner: GenericParamOwner) -> Self { - let trait_self = TyParamPrecursor { - name: Partial::Present(kw::SELF_TY), - idx: None, - kind: None, - }; + let trait_self = TyParamPrecursor::trait_self(None); Self { db, @@ -540,16 +582,17 @@ impl<'db> GenericParamCollector<'db> { GenericParam::Type(param) => { let name = param.name; - let kind = self.find_kind_from_bound(param.bounds.as_slice()); - self.params.push(TyParamPrecursor { - name, - idx: Some(idx), - kind, - }); + let kind = self.extract_kind(param.bounds.as_slice()); + self.params + .push(TyParamPrecursor::ty_param(name, idx, kind)); } - GenericParam::Const(_) => { - todo!() + GenericParam::Const(param) => { + let name = param.name; + let hir_ty = param.ty.to_opt(); + + self.params + .push(TyParamPrecursor::const_ty_param(name, idx, hir_ty)) } } } @@ -565,14 +608,14 @@ impl<'db> GenericParamCollector<'db> { for pred in where_clause.data(hir_db) { match self.param_idx_from_ty(pred.ty.to_opt()) { ParamLoc::Idx(idx) => { - if self.params[idx].kind.is_none() { - self.params[idx].kind = self.find_kind_from_bound(pred.bounds.as_slice()); + if self.params[idx].kind.is_none() && !self.params[idx].is_const_ty { + self.params[idx].kind = self.extract_kind(pred.bounds.as_slice()); } } ParamLoc::TraitSelf => { if self.trait_self.kind.is_none() { - self.trait_self.kind = self.find_kind_from_bound(pred.bounds.as_slice()); + self.trait_self.kind = self.extract_kind(pred.bounds.as_slice()); } } @@ -585,18 +628,13 @@ impl<'db> GenericParamCollector<'db> { self.collect_generic_params(); self.collect_kind_in_where_clause(); - let params = self - .params - .into_iter() - .map(|param| param.into_ty(self.db)) - .collect(); let trait_self = matches!(self.owner, GenericParamOwner::Trait(_)) - .then(|| self.trait_self.into_ty(self.db)); + .then(|| self.trait_self.evaluate(self.db, self.owner.scope())); - GenericParamTypeSet { params, trait_self } + GenericParamTypeSet::new(self.db, self.params, trait_self, self.owner.scope()) } - fn find_kind_from_bound(&self, bounds: &[TypeBound]) -> Option { + fn extract_kind(&self, bounds: &[TypeBound]) -> Option { for bound in bounds { if let TypeBound::Kind(Partial::Present(k)) = bound { return Some(lower_kind(k)); @@ -653,15 +691,18 @@ enum ParamLoc { NonParam, } -#[derive(Debug)] -struct TyParamPrecursor { +#[doc(hidden)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TyParamPrecursor { name: Partial, idx: Option, kind: Option, + const_ty_ty: Option, + is_const_ty: bool, } impl TyParamPrecursor { - pub fn into_ty(self, db: &dyn HirAnalysisDb) -> TyId { + fn evaluate(&self, db: &dyn HirAnalysisDb, scope: ScopeId) -> TyId { let Partial::Present(name) = self.name else { return TyId::invalid(db, InvalidCause::Other); }; @@ -669,10 +710,60 @@ impl TyParamPrecursor { let param = TyParam { name, idx: self.idx, - kind: self.kind.unwrap_or(Kind::Star), + kind: self.kind.clone().unwrap_or(Kind::Star), + }; + + if !self.is_const_ty { + return TyId::new(db, TyData::TyParam(param)); + } + + let const_ty_ty = match self.const_ty_ty { + Some(ty) => { + let ty = lower_hir_ty(db, ty, scope); + if !(ty.contains_invalid(db) || ty.is_integral(db) || ty.is_bool(db)) { + TyId::invalid(db, InvalidCause::InvalidConstParamTy { ty }) + } else { + ty + } + } + + None => TyId::invalid(db, InvalidCause::Other), }; - TyId::new(db, TyData::TyParam(param)) + let const_ty = ConstTyId::new(db, ConstTyData::TyParam(param, const_ty_ty)); + + TyId::new(db, TyData::ConstTy(const_ty)) + } + + fn ty_param(name: Partial, idx: usize, kind: Option) -> Self { + Self { + name, + idx: idx.into(), + kind, + const_ty_ty: None, + is_const_ty: false, + } + } + + fn const_ty_param(name: Partial, idx: usize, ty: Option) -> Self { + Self { + name, + idx: idx.into(), + kind: None, + const_ty_ty: ty, + is_const_ty: true, + } + } + + fn trait_self(kind: Option) -> Self { + let name = Partial::Present(kw::SELF_TY); + Self { + name, + idx: None, + kind, + const_ty_ty: None, + is_const_ty: false, + } } } @@ -719,4 +810,9 @@ impl GenericParamOwnerId { pub(super) fn params(self, db: &dyn HirAnalysisDb) -> GenericParamListId { self.data(db).params(db.as_hir_db()) } + + pub(super) fn from_item_opt(db: &dyn HirAnalysisDb, item: ItemKind) -> Option { + let owner = GenericParamOwner::from_item_opt(item)?; + Self::new(db, owner).into() + } } diff --git a/crates/hir-analysis/src/ty/unify.rs b/crates/hir-analysis/src/ty/unify.rs index cca3d6c8d5..0584f9e438 100644 --- a/crates/hir-analysis/src/ty/unify.rs +++ b/crates/hir-analysis/src/ty/unify.rs @@ -1,20 +1,21 @@ //! This module contains the unification table for type inference and trait //! satisfiability checking. -use ena::unify::{InPlaceUnificationTable, UnifyKey, UnifyValue}; - -use crate::HirAnalysisDb; +use ena::unify::{InPlace, InPlaceUnificationTable, UnifyKey, UnifyValue}; use super::{ trait_def::{Implementor, TraitInstId}, - ty_def::{Kind, Subst, TyData, TyId}, + ty_def::{Kind, Subst, TyData, TyId, TyVar, TyVarUniverse}, }; +use crate::{ty::const_ty::ConstTyData, HirAnalysisDb}; pub(crate) struct UnificationTable<'db> { db: &'db dyn HirAnalysisDb, table: InPlaceUnificationTable, } +pub type Snapshot = ena::unify::Snapshot>; + impl<'db> UnificationTable<'db> { pub fn new(db: &'db dyn HirAnalysisDb) -> Self { Self { @@ -23,16 +24,24 @@ impl<'db> UnificationTable<'db> { } } + pub fn rollback_to(&mut self, snapshot: Snapshot) { + self.table.rollback_to(snapshot); + } + + pub fn snapshot(&mut self) -> Snapshot { + self.table.snapshot() + } + pub fn unify(&mut self, lhs: T, rhs: T) -> bool where T: Unifiable, { - let snapshot = self.table.snapshot(); + let snapshot = self.snapshot(); if lhs.unify(self, rhs) { self.table.commit(snapshot); true } else { - self.table.rollback_to(snapshot); + self.rollback_to(snapshot); false } } @@ -49,25 +58,17 @@ impl<'db> UnificationTable<'db> { let ty2 = self.apply(self.db, ty2); match (ty1.data(self.db), ty2.data(self.db)) { - (TyData::TyVar(var1), TyData::TyVar(var2)) => { - self.table.unify_var_var(var1.key, var2.key).is_ok() - } + (TyData::TyVar(_), TyData::TyVar(_)) => self.unify_var_var(ty1, ty2), - (TyData::TyVar(var), _) if !ty2.free_inference_keys(self.db).contains(&var.key) => self - .table - .unify_var_value(var.key, InferenceValue::Bounded(ty2)) - .is_ok(), + (TyData::TyVar(var), _) => self.unify_var_value(var, ty2), - (_, TyData::TyVar(var)) if !ty1.free_inference_keys(self.db).contains(&var.key) => self - .table - .unify_var_value(var.key, InferenceValue::Bounded(ty1)) - .is_ok(), + (_, TyData::TyVar(var)) => self.unify_var_value(var, ty1), (TyData::TyApp(ty1_1, ty1_2), TyData::TyApp(ty2_1, ty2_2)) => { - let ok = self.unify_ty(ty1_1, ty2_1); + let ok = self.unify_ty(*ty1_1, *ty2_1); if ok { - let ty1_2 = self.apply(self.db, ty1_2); - let ty2_2 = self.apply(self.db, ty2_2); + let ty1_2 = self.apply(self.db, *ty1_2); + let ty2_2 = self.apply(self.db, *ty2_2); self.unify_ty(ty1_2, ty2_2) } else { false @@ -80,23 +81,126 @@ impl<'db> UnificationTable<'db> { (TyData::Invalid(_), _) | (_, TyData::Invalid(_)) => true, + (TyData::ConstTy(const_ty1), TyData::ConstTy(const_ty2)) => { + if !self.unify_ty(const_ty1.ty(self.db), const_ty2.ty(self.db)) { + return false; + } + + match (const_ty1.data(self.db), const_ty2.data(self.db)) { + (ConstTyData::TyVar(..), ConstTyData::TyVar(..)) => { + self.unify_var_var(ty1, ty2) + } + + (ConstTyData::TyVar(var, _), _) => self.unify_var_value(var, ty2), + + (_, ConstTyData::TyVar(var, _)) => self.unify_var_value(var, ty1), + + (ConstTyData::Evaluated(val1, _), ConstTyData::Evaluated(val2, _)) => { + val1 == val2 + } + + _ => false, + } + } + _ => false, } } - pub fn new_var(&mut self, kind: &Kind) -> TyId { + pub fn new_var(&mut self, universe: TyVarUniverse, kind: &Kind) -> TyId { let key = self.new_key(kind); - TyId::ty_var(self.db, kind.clone(), key) + TyId::ty_var(self.db, universe, kind.clone(), key) + } + + pub(super) fn new_var_from_param(&mut self, db: &dyn HirAnalysisDb, ty: TyId) -> TyId { + match ty.data(db) { + TyData::TyParam(param) => { + let key = self.new_key(¶m.kind); + let universe = TyVarUniverse::General; + TyId::ty_var(db, universe, param.kind.clone(), key) + } + + TyData::ConstTy(const_ty) => { + if let ConstTyData::TyParam(_, ty) = const_ty.data(db) { + let key = self.new_key(ty.kind(db)); + TyId::const_ty_var(db, *ty, key) + } else { + panic!() + } + } + _ => panic!(), + } } pub fn new_key(&mut self, kind: &Kind) -> InferenceKey { - self.table.new_key(InferenceValue::Unbounded(kind.clone())) + self.table.new_key(InferenceValue::Unbound(kind.clone())) } - pub fn probe(&mut self, key: InferenceKey) -> Option { + fn probe(&mut self, key: InferenceKey) -> Option { match self.table.probe_value(key) { - InferenceValue::Bounded(ty) => Some(ty), - InferenceValue::Unbounded(_) => None, + InferenceValue::Bound(ty) => Some(ty), + InferenceValue::Unbound(_) => None, + } + } + + /// Try to unify two type variables. + /// Returns `true` if the two variables are unifiable. + /// + /// When the two variables are in the same universe, we can just unify them. + /// + /// When the two variables are *NOT* in the same universe, a type variable + /// that has a broader universe are narrowed down to the narrower one. + /// + /// NOTE: This assumes that we have only two universes: General and Int. + fn unify_var_var(&mut self, ty_var1: TyId, ty_var2: TyId) -> bool { + let (var1, var2) = match (ty_var1.data(self.db), ty_var2.data(self.db)) { + (TyData::TyVar(var1), TyData::TyVar(var2)) => (var1, var2), + (TyData::ConstTy(const_ty1), TyData::ConstTy(const_ty2)) => { + match (const_ty1.data(self.db), const_ty2.data(self.db)) { + (ConstTyData::TyVar(var1, _), ConstTyData::TyVar(var2, _)) => (var1, var2), + _ => panic!(), + } + } + _ => panic!(), + }; + + if var1.universe == var2.universe { + self.table.unify_var_var(var1.key, var2.key).is_ok() + } else if matches!(var1.universe, TyVarUniverse::General) { + // Narrow down the universe of var1 to Int. + self.table + .unify_var_value(var1.key, InferenceValue::Bound(ty_var2)) + .is_ok() + } else { + // Narrow down the universe of var2 to Int. + self.table + .unify_var_value(var2.key, InferenceValue::Bound(ty_var1)) + .is_ok() + } + } + + /// Try to unify a type variable to a type. + /// We perform the following checks: + /// 1. Occurrence check: The same type variable must not occur in the type. + /// 2. Universe check: The universe of the type variable must match the + /// universe of the type. + fn unify_var_value(&mut self, var: &TyVar, value: TyId) -> bool { + if value.free_inference_keys(self.db).contains(&var.key) { + return false; + } + + match (var.universe, value.is_integral(self.db)) { + (TyVarUniverse::General, _) => self + .table + .unify_var_value(var.key, InferenceValue::Bound(value)) + .is_ok(), + + (TyVarUniverse::Integral, true) => self + .table + .unify_var_value(var.key, InferenceValue::Bound(value)) + .is_ok(), + + _ => false, } } } @@ -105,6 +209,13 @@ impl<'db> Subst for UnificationTable<'db> { fn get(&mut self, ty: TyId) -> Option { match ty.data(self.db) { TyData::TyVar(var) => self.probe(var.key), + TyData::ConstTy(const_ty) => { + if let ConstTyData::TyVar(var, _) = const_ty.data(self.db) { + self.probe(var.key) + } else { + None + } + } _ => None, } } @@ -115,8 +226,8 @@ pub struct InferenceKey(pub(super) u32); #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum InferenceValue { - Bounded(TyId), - Unbounded(Kind), + Bound(TyId), + Unbound(Kind), } impl UnifyKey for InferenceKey { @@ -140,21 +251,21 @@ impl UnifyValue for InferenceValue { fn unify_values(v1: &Self, v2: &Self) -> Result { match (v1, v2) { - (InferenceValue::Unbounded(k1), InferenceValue::Unbounded(k2)) => { + (InferenceValue::Unbound(k1), InferenceValue::Unbound(k2)) => { assert!(k1.does_match(k2)); - Ok(InferenceValue::Unbounded(k1.clone())) + Ok(InferenceValue::Unbound(k1.clone())) } - (InferenceValue::Unbounded(_), InferenceValue::Bounded(ty)) - | (InferenceValue::Bounded(ty), InferenceValue::Unbounded(_)) => { - Ok(InferenceValue::Bounded(*ty)) + (InferenceValue::Unbound(_), InferenceValue::Bound(ty)) + | (InferenceValue::Bound(ty), InferenceValue::Unbound(_)) => { + Ok(InferenceValue::Bound(*ty)) } - (InferenceValue::Bounded(ty1), InferenceValue::Bounded(ty2)) => { + (InferenceValue::Bound(ty1), InferenceValue::Bound(ty2)) => { if ty1 != ty2 { Err(()) } else { - Ok(InferenceValue::Bounded(*ty1)) + Ok(InferenceValue::Bound(*ty1)) } } } @@ -198,3 +309,5 @@ impl Unifiable for Implementor { table.unify(self.ty(db), other.ty(db)) } } + +pub trait Foo {} diff --git a/crates/hir-analysis/src/ty/visitor.rs b/crates/hir-analysis/src/ty/visitor.rs index 9d5ff7da9f..d09e2023a0 100644 --- a/crates/hir-analysis/src/ty/visitor.rs +++ b/crates/hir-analysis/src/ty/visitor.rs @@ -1,7 +1,9 @@ +use super::{ + const_ty::{ConstTyData, ConstTyId}, + ty_def::{AdtDef, FuncDef, InvalidCause, PrimTy, TyBase, TyData, TyId, TyParam, TyVar}, +}; use crate::HirAnalysisDb; -use super::ty_def::{AdtDef, FuncDef, InvalidCause, PrimTy, TyBase, TyData, TyId, TyParam, TyVar}; - pub trait TyVisitor { fn visit_ty(&mut self, db: &dyn HirAnalysisDb, ty: TyId) { walk_ty(self, db, ty) @@ -13,14 +15,18 @@ pub trait TyVisitor { #[allow(unused_variables)] fn visit_param(&mut self, db: &dyn HirAnalysisDb, ty_param: &TyParam) {} + #[allow(unused_variables)] + fn visit_const_param(&mut self, db: &dyn HirAnalysisDb, ty_param: &TyParam, const_ty_ty: TyId) { + } + fn visit_app(&mut self, db: &dyn HirAnalysisDb, abs: TyId, arg: TyId) { self.visit_ty(db, abs); self.visit_ty(db, arg); } #[allow(unused_variables)] - fn visit_ty_con(&mut self, db: &dyn HirAnalysisDb, ty_con: &TyBase) { - walk_ty_con(self, db, ty_con); + fn visit_ty_base(&mut self, db: &dyn HirAnalysisDb, ty_base: &TyBase) { + walk_ty_base(self, db, ty_base); } #[allow(unused_variables)] @@ -34,6 +40,11 @@ pub trait TyVisitor { #[allow(unused_variables)] fn visit_func(&mut self, db: &dyn HirAnalysisDb, func: FuncDef) {} + + #[allow(unused_variables)] + fn visit_const_ty(&mut self, db: &dyn HirAnalysisDb, const_ty: &ConstTyId) { + walk_const_ty(self, db, const_ty) + } } pub fn walk_ty(visitor: &mut V, db: &dyn HirAnalysisDb, ty: TyId) @@ -41,19 +52,21 @@ where V: TyVisitor + ?Sized, { match ty.data(db) { - TyData::TyVar(var) => visitor.visit_var(db, &var), + TyData::TyVar(var) => visitor.visit_var(db, var), - TyData::TyParam(param) => visitor.visit_param(db, ¶m), + TyData::TyParam(param) => visitor.visit_param(db, param), - TyData::TyApp(abs, arg) => visitor.visit_app(db, abs, arg), + TyData::TyApp(abs, arg) => visitor.visit_app(db, *abs, *arg), - TyData::TyBase(ty_con) => visitor.visit_ty_con(db, &ty_con), + TyData::TyBase(ty_con) => visitor.visit_ty_base(db, ty_con), - TyData::Invalid(cause) => visitor.visit_invalid(db, &cause), + TyData::ConstTy(const_ty) => visitor.visit_const_ty(db, const_ty), + + TyData::Invalid(cause) => visitor.visit_invalid(db, cause), } } -pub fn walk_ty_con(visitor: &mut V, db: &dyn HirAnalysisDb, ty_con: &TyBase) +pub fn walk_ty_base(visitor: &mut V, db: &dyn HirAnalysisDb, ty_con: &TyBase) where V: TyVisitor + ?Sized, { @@ -63,3 +76,15 @@ where TyBase::Func(func) => visitor.visit_func(db, *func), } } + +pub fn walk_const_ty(visitor: &mut V, db: &dyn HirAnalysisDb, const_ty: &ConstTyId) +where + V: TyVisitor + ?Sized, +{ + visitor.visit_ty(db, const_ty.ty(db)); + match &const_ty.data(db) { + ConstTyData::TyVar(var, _) => visitor.visit_var(db, var), + ConstTyData::TyParam(param, ty) => visitor.visit_const_param(db, param, *ty), + ConstTyData::Evaluated(..) | ConstTyData::UnEvaluated(..) => {} + } +} diff --git a/crates/hir-analysis/tests/early_path_resolution.rs b/crates/hir-analysis/tests/early_path_resolution.rs index 89667ee774..9f2ef70013 100644 --- a/crates/hir-analysis/tests/early_path_resolution.rs +++ b/crates/hir-analysis/tests/early_path_resolution.rs @@ -1,6 +1,4 @@ mod test_db; -use test_db::{HirAnalysisTestDb, HirPropertyFormatter}; - use std::path::Path; use dir_test::{dir_test, Fixture}; @@ -15,6 +13,7 @@ use hir::{ visitor::prelude::*, HirDb, SpannedHirDb, }; +use test_db::{HirAnalysisTestDb, HirPropertyFormatter}; #[dir_test( dir: "$CARGO_MANIFEST_DIR/test_files/early_path_resolution", diff --git a/crates/hir-analysis/tests/import.rs b/crates/hir-analysis/tests/import.rs index 24e9becd99..59ac247c21 100644 --- a/crates/hir-analysis/tests/import.rs +++ b/crates/hir-analysis/tests/import.rs @@ -1,6 +1,4 @@ mod test_db; -use test_db::{HirAnalysisTestDb, HirPropertyFormatter}; - use std::path::Path; use dir_test::{dir_test, Fixture}; @@ -8,6 +6,7 @@ use fe_compiler_test_utils::snap_test; use fe_hir_analysis::name_resolution::{ImportAnalysisPass, NameDerivation, ResolvedImports}; use hir::{analysis_pass::ModuleAnalysisPass, hir_def::Use}; use rustc_hash::FxHashMap; +use test_db::{HirAnalysisTestDb, HirPropertyFormatter}; #[dir_test( dir: "$CARGO_MANIFEST_DIR/test_files/imports", diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index 6906d217ec..3de03053b0 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml @@ -13,7 +13,7 @@ description = "Provides HIR definition and lowering for Fe lang" salsa = { git = "https://github.com/salsa-rs/salsa", package = "salsa-2022" } derive_more = "0.99" cranelift-entity = "0.91" -num-bigint = "0.4.3" +num-bigint = "0.4" num-traits = "0.2.15" camino = "1.1.4" rustc-hash = "1.1.0" diff --git a/crates/hir/src/hir_def/item.rs b/crates/hir/src/hir_def/item.rs index d3bf69f5e2..781bb9a5e7 100644 --- a/crates/hir/src/hir_def/item.rs +++ b/crates/hir/src/hir_def/item.rs @@ -6,6 +6,12 @@ use common::InputFile; use parser::ast; +use super::{ + kw, + scope_graph::{ScopeGraph, ScopeId}, + AttrListId, Body, FuncParamListId, GenericParamListId, IdentId, IngotId, Partial, TupleTypeId, + TypeId, UseAlias, WhereClauseId, +}; use crate::{ hir_def::TraitRefId, lower, @@ -21,13 +27,6 @@ use crate::{ HirDb, }; -use super::{ - kw, - scope_graph::{ScopeGraph, ScopeId}, - AttrListId, Body, FuncParamListId, GenericParamListId, IdentId, IngotId, Partial, TupleTypeId, - TypeId, UseAlias, WhereClauseId, -}; - #[derive( Debug, Clone, @@ -608,7 +607,7 @@ impl Func { return false; }; - let Some(first_param) = params.data(db).get(0) else { + let Some(first_param) = params.data(db).first() else { return false; }; diff --git a/crates/hir/src/hir_def/types.rs b/crates/hir/src/hir_def/types.rs index 497d6103b1..1afde5c31b 100644 --- a/crates/hir/src/hir_def/types.rs +++ b/crates/hir/src/hir_def/types.rs @@ -24,11 +24,8 @@ impl TypeId { #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum TypeKind { Ptr(Partial), - /// The `PathId` is the path to the type, the `Option` is the generic - /// arguments. Path(Partial, GenericArgListId), SelfType(GenericArgListId), - /// The `Vec` contains the types of the tuple elements. Tuple(TupleTypeId), /// The first `TypeId` is the element type, the second `Body` is the length. Array(Partial, Partial), diff --git a/crates/parser2/Cargo.toml b/crates/parser2/Cargo.toml index 3b1581bd91..cef56961c5 100644 --- a/crates/parser2/Cargo.toml +++ b/crates/parser2/Cargo.toml @@ -12,7 +12,7 @@ description = "Parser lib for Fe." [dependencies] rowan = "0.15.10" logos = "0.12.1" -fxhash = "0.2.1" +rustc-hash = "1.1.0" lazy_static = "1.4.0" derive_more = "0.99" diff --git a/crates/parser2/src/parser/mod.rs b/crates/parser2/src/parser/mod.rs index f81a75aa4d..787c442444 100644 --- a/crates/parser2/src/parser/mod.rs +++ b/crates/parser2/src/parser/mod.rs @@ -1,12 +1,10 @@ use std::collections::VecDeque; pub(crate) use item::ItemListScope; - -use fxhash::{FxHashMap, FxHashSet}; - -use crate::{syntax_node::SyntaxNode, GreenNode, ParseError, SyntaxKind, TextRange}; +use rustc_hash::{FxHashMap, FxHashSet}; use self::token_stream::{BackTrackableTokenStream, LexicalToken, TokenStream}; +use crate::{syntax_node::SyntaxNode, GreenNode, ParseError, SyntaxKind, TextRange}; pub mod token_stream; @@ -322,7 +320,7 @@ impl Parser { /// Proceeds the parser to the recovery token of the current scope. pub fn recover(&mut self) { - let mut recovery_set: FxHashSet = fxhash::FxHashSet::default(); + let mut recovery_set: FxHashSet = FxHashSet::default(); let mut scope_index = self.parents.len() - 1; loop { match self diff --git a/crates/uitest/fixtures/ty/const_ty/const_ty_expected.fe b/crates/uitest/fixtures/ty/const_ty/const_ty_expected.fe new file mode 100644 index 0000000000..c75cfcb6e2 --- /dev/null +++ b/crates/uitest/fixtures/ty/const_ty/const_ty_expected.fe @@ -0,0 +1,4 @@ +pub struct Foo { + N: u256 +} +fn foo(x: Foo) {} \ No newline at end of file diff --git a/crates/uitest/fixtures/ty/const_ty/const_ty_expected.snap b/crates/uitest/fixtures/ty/const_ty/const_ty_expected.snap new file mode 100644 index 0000000000..0d9dfc57da --- /dev/null +++ b/crates/uitest/fixtures/ty/const_ty/const_ty_expected.snap @@ -0,0 +1,12 @@ +--- +source: crates/uitest/tests/ty.rs +expression: diags +input_file: crates/uitest/fixtures/ty/const_ty/const_ty_expected.fe +--- +error[3-0012]: expected const type + ┌─ const_ty_expected.fe:4:11 + │ +4 │ fn foo(x: Foo) {} + │ ^^^^^^^^ expected const type of `u256` here + + diff --git a/crates/uitest/fixtures/ty/const_ty/const_ty_mismatch.fe b/crates/uitest/fixtures/ty/const_ty/const_ty_mismatch.fe new file mode 100644 index 0000000000..5995f4e0d3 --- /dev/null +++ b/crates/uitest/fixtures/ty/const_ty/const_ty_mismatch.fe @@ -0,0 +1,26 @@ +pub struct Foo { + N: u256 +} + +pub struct Foo2 { + t: T, + N: u256 +} + + +pub struct Bar { + N: bool +} + +pub fn foo(x: Foo) {} +pub fn foo2(x: Foo2) {} + +pub fn bar(x: Bar<3>) {} + +pub struct Bar2 { + N: u64 +} + +pub enum Baz { + MyField{N: u64, x: i32} +} \ No newline at end of file diff --git a/crates/uitest/fixtures/ty/const_ty/const_ty_mismatch.snap b/crates/uitest/fixtures/ty/const_ty/const_ty_mismatch.snap new file mode 100644 index 0000000000..505d737f79 --- /dev/null +++ b/crates/uitest/fixtures/ty/const_ty/const_ty_mismatch.snap @@ -0,0 +1,48 @@ +--- +source: crates/uitest/tests/ty.rs +expression: diags +input_file: crates/uitest/fixtures/ty/const_ty/const_ty_mismatch.fe +--- +error[3-0011]: given type doesn't match the expected const type + ┌─ const_ty_mismatch.fe:2:8 + │ +2 │ N: u256 + │ ^^^^ expected `u32` type here, but `u256` is given + +error[3-0011]: given type doesn't match the expected const type + ┌─ const_ty_mismatch.fe:7:8 + │ +7 │ N: u256 + │ ^^^^ expected `u32` type here, but `u256` is given + +error[3-0011]: given type doesn't match the expected const type + ┌─ const_ty_mismatch.fe:15:15 + │ +15 │ pub fn foo(x: Foo) {} + │ ^^^^^^^^^ expected `u32` type here, but `bool` is given + +error[3-0011]: given type doesn't match the expected const type + ┌─ const_ty_mismatch.fe:16:16 + │ +16 │ pub fn foo2(x: Foo2) {} + │ ^^^^^^^^^^^^^^^^ expected `u32` type here, but `bool` is given + +error[3-0011]: given type doesn't match the expected const type + ┌─ const_ty_mismatch.fe:18:15 + │ +18 │ pub fn bar(x: Bar<3>) {} + │ ^^^^^^ expected `bool` type here, but `` is given + +error[3-0011]: given type doesn't match the expected const type + ┌─ const_ty_mismatch.fe:21:8 + │ +21 │ N: u64 + │ ^^^ expected `u32` type here, but `u64` is given + +error[3-0011]: given type doesn't match the expected const type + ┌─ const_ty_mismatch.fe:25:16 + │ +25 │ MyField{N: u64, x: i32} + │ ^^^ expected `u32` type here, but `u64` is given + + diff --git a/crates/uitest/fixtures/ty/const_ty/normal_type_expected.fe b/crates/uitest/fixtures/ty/const_ty/normal_type_expected.fe new file mode 100644 index 0000000000..0268988a90 --- /dev/null +++ b/crates/uitest/fixtures/ty/const_ty/normal_type_expected.fe @@ -0,0 +1,9 @@ +pub struct Foo { + t: T +} +pub fn foo(x: Foo<1>) {} + +pub struct Bar { + u: T, + n: N, +} \ No newline at end of file diff --git a/crates/uitest/fixtures/ty/const_ty/normal_type_expected.snap b/crates/uitest/fixtures/ty/const_ty/normal_type_expected.snap new file mode 100644 index 0000000000..c2f0b3d0f7 --- /dev/null +++ b/crates/uitest/fixtures/ty/const_ty/normal_type_expected.snap @@ -0,0 +1,18 @@ +--- +source: crates/uitest/tests/ty.rs +expression: diags +input_file: crates/uitest/fixtures/ty/const_ty/normal_type_expected.fe +--- +error[3-0013]: expected a normal type + ┌─ normal_type_expected.fe:4:15 + │ +4 │ pub fn foo(x: Foo<1>) {} + │ ^^^^^^ expected a normal type here, but `1` is given + +error[3-0013]: expected a normal type + ┌─ normal_type_expected.fe:8:8 + │ +8 │ n: N, + │ ^ expected a normal type here, but `const N: u32` is given + + diff --git a/crates/uitest/fixtures/ty/const_ty/trait_const_ty.fe b/crates/uitest/fixtures/ty/const_ty/trait_const_ty.fe new file mode 100644 index 0000000000..468ddff41e --- /dev/null +++ b/crates/uitest/fixtures/ty/const_ty/trait_const_ty.fe @@ -0,0 +1,9 @@ +pub trait Trait {} +impl Trait for i32 {} +impl Trait for i64 {} +pub struct Foo + where T: Trait +{} + +pub trait Trait2 {} +impl Trait2<1> for i32 {} diff --git a/crates/uitest/fixtures/ty/const_ty/trait_const_ty.snap b/crates/uitest/fixtures/ty/const_ty/trait_const_ty.snap new file mode 100644 index 0000000000..b0b96d67bf --- /dev/null +++ b/crates/uitest/fixtures/ty/const_ty/trait_const_ty.snap @@ -0,0 +1,30 @@ +--- +source: crates/uitest/tests/ty.rs +expression: diags +input_file: crates/uitest/fixtures/ty/const_ty/trait_const_ty.fe +--- +error[3-0011]: given type doesn't match the expected const type + ┌─ trait_const_ty.fe:3:20 + │ +3 │ impl Trait for i64 {} + │ ^^^^^^^^ expected `u32` type here, but `u64` is given + +error[3-0011]: given type doesn't match the expected const type + ┌─ trait_const_ty.fe:5:14 + │ +5 │ where T: Trait + │ ^^^^^^^^ expected `u32` type here, but `u64` is given + +error[3-0012]: expected const type + ┌─ trait_const_ty.fe:2:6 + │ +2 │ impl Trait for i32 {} + │ ^^^^^^^^^^ expected const type of `u32` here + +error[3-0013]: expected a normal type + ┌─ trait_const_ty.fe:9:6 + │ +9 │ impl Trait2<1> for i32 {} + │ ^^^^^^^^^ expected a normal type here, but `1` is given + + diff --git a/crates/uitest/fixtures/ty/def/const_generics_cycle.fe b/crates/uitest/fixtures/ty/def/const_generics_cycle.fe new file mode 100644 index 0000000000..ea05b06bdb --- /dev/null +++ b/crates/uitest/fixtures/ty/def/const_generics_cycle.fe @@ -0,0 +1,2 @@ +pub struct Foo {} +pub struct Bar {} \ No newline at end of file diff --git a/crates/uitest/fixtures/ty/def/const_generics_cycle.snap b/crates/uitest/fixtures/ty/def/const_generics_cycle.snap new file mode 100644 index 0000000000..abe37453f3 --- /dev/null +++ b/crates/uitest/fixtures/ty/def/const_generics_cycle.snap @@ -0,0 +1,18 @@ +--- +source: crates/uitest/tests/ty.rs +expression: diags +input_file: crates/uitest/fixtures/ty/def/const_generics_cycle.fe +--- +error[3-0009]: `T` is forbidden as a const parameter type + ┌─ const_generics_cycle.fe:1:28 + │ +1 │ pub struct Foo {} + │ ^ only integer or bool types are allowed as a const parameter type + +error[3-0009]: `Bar` is forbidden as a const parameter type + ┌─ const_generics_cycle.fe:2:28 + │ +2 │ pub struct Bar {} + │ ^^^ only integer or bool types are allowed as a const parameter type + + diff --git a/crates/uitest/fixtures/ty/def/const_generics_invalid_ty.fe b/crates/uitest/fixtures/ty/def/const_generics_invalid_ty.fe new file mode 100644 index 0000000000..4cd439542c --- /dev/null +++ b/crates/uitest/fixtures/ty/def/const_generics_invalid_ty.fe @@ -0,0 +1,4 @@ +pub struct Foo {} +pub struct Bar {} + +pub fn foo() {} \ No newline at end of file diff --git a/crates/uitest/fixtures/ty/def/const_generics_invalid_ty.snap b/crates/uitest/fixtures/ty/def/const_generics_invalid_ty.snap new file mode 100644 index 0000000000..00743b0d27 --- /dev/null +++ b/crates/uitest/fixtures/ty/def/const_generics_invalid_ty.snap @@ -0,0 +1,18 @@ +--- +source: crates/uitest/tests/ty.rs +expression: diags +input_file: crates/uitest/fixtures/ty/def/const_generics_invalid_ty.fe +--- +error[3-0009]: `Foo` is forbidden as a const parameter type + ┌─ const_generics_invalid_ty.fe:2:25 + │ +2 │ pub struct Bar {} + │ ^^^ only integer or bool types are allowed as a const parameter type + +error[3-0009]: `Foo` is forbidden as a const parameter type + ┌─ const_generics_invalid_ty.fe:4:21 + │ +4 │ pub fn foo() {} + │ ^^^ only integer or bool types are allowed as a const parameter type + + diff --git a/crates/uitest/fixtures/ty/def/const_generics_trait_bound.fe b/crates/uitest/fixtures/ty/def/const_generics_trait_bound.fe new file mode 100644 index 0000000000..16ad0947f9 --- /dev/null +++ b/crates/uitest/fixtures/ty/def/const_generics_trait_bound.fe @@ -0,0 +1,9 @@ +pub trait Trait {} + +pub struct Foo +where U: Trait +{} + +pub fn foo() +where U: Trait +{} \ No newline at end of file diff --git a/crates/uitest/fixtures/ty/def/const_generics_trait_bound.snap b/crates/uitest/fixtures/ty/def/const_generics_trait_bound.snap new file mode 100644 index 0000000000..dfc8ee558a --- /dev/null +++ b/crates/uitest/fixtures/ty/def/const_generics_trait_bound.snap @@ -0,0 +1,18 @@ +--- +source: crates/uitest/tests/ty.rs +expression: diags +input_file: crates/uitest/fixtures/ty/def/const_generics_trait_bound.fe +--- +error[6-0006]: trait bound for const type is not allowed + ┌─ const_generics_trait_bound.fe:4:7 + │ +4 │ where U: Trait + │ ^ `const U: u32` is a const type + +error[6-0006]: trait bound for const type is not allowed + ┌─ const_generics_trait_bound.fe:8:7 + │ +8 │ where U: Trait + │ ^ `const U: bool` is a const type + + diff --git a/crates/uitest/fixtures/ty/def/not_fully_applied.snap b/crates/uitest/fixtures/ty/def/not_fully_applied.snap deleted file mode 100644 index 2c592e13a8..0000000000 --- a/crates/uitest/fixtures/ty/def/not_fully_applied.snap +++ /dev/null @@ -1,60 +0,0 @@ ---- -source: crates/uitest/tests/ty.rs -expression: diags -input_file: crates/uitest/fixtures/ty/def/not_fully_applied.fe ---- -error[2-0002]: `T` is not found - ┌─ not_fully_applied.fe:21:10 - │ -21 │ impl Gen { - │ ^ `T` is not found - -error[3-0000]: expected fully applied type - ┌─ not_fully_applied.fe:7:8 - │ -7 │ f: Gen, - │ ^^^^^^^^ expected fully applied type here - -error[3-0000]: expected fully applied type - ┌─ not_fully_applied.fe:12:13 - │ -12 │ Variant(Gen, Gen) - │ ^^^^^^^^ expected fully applied type here - -error[3-0000]: expected fully applied type - ┌─ not_fully_applied.fe:12:23 - │ -12 │ Variant(Gen, Gen) - │ ^^^^^^^^ expected fully applied type here - -error[3-0000]: expected fully applied type - ┌─ not_fully_applied.fe:13:36 - │ -13 │ Variant2{ x: Gen, y: Gen } - │ ^^^^^^^^ expected fully applied type here - -error[3-0000]: expected fully applied type - ┌─ not_fully_applied.fe:17:8 - │ -17 │ f: Gen, - │ ^^^^^^^^ expected fully applied type here - -error[3-0000]: expected fully applied type - ┌─ not_fully_applied.fe:22:12 - │ -22 │ fn foo(self) {} - │ ^^^^ expected fully applied type here - -error[3-0000]: expected fully applied type - ┌─ not_fully_applied.fe:24:18 - │ -24 │ fn bar(self: Self) {} - │ ^^^^ expected fully applied type here - -error[3-0000]: expected fully applied type - ┌─ not_fully_applied.fe:28:13 - │ -28 │ fn foo(gen: Gen) {} - │ ^^^ expected fully applied type here - - diff --git a/crates/uitest/fixtures/ty/def/not_fully_applied.fe b/crates/uitest/fixtures/ty/def/not_star_kind.fe similarity index 85% rename from crates/uitest/fixtures/ty/def/not_fully_applied.fe rename to crates/uitest/fixtures/ty/def/not_star_kind.fe index 795f3458b1..0273803f81 100644 --- a/crates/uitest/fixtures/ty/def/not_fully_applied.fe +++ b/crates/uitest/fixtures/ty/def/not_star_kind.fe @@ -25,4 +25,6 @@ impl Gen { } -fn foo(gen: Gen) {} \ No newline at end of file +fn foo(gen: Gen) {} + +fn foo(gen: Gen) -> Gen {} \ No newline at end of file diff --git a/crates/uitest/fixtures/ty/def/not_star_kind.snap b/crates/uitest/fixtures/ty/def/not_star_kind.snap new file mode 100644 index 0000000000..8574add65d --- /dev/null +++ b/crates/uitest/fixtures/ty/def/not_star_kind.snap @@ -0,0 +1,81 @@ +--- +source: crates/uitest/tests/ty.rs +expression: diags +input_file: crates/uitest/fixtures/ty/def/not_star_kind.fe +--- +error[2-0001]: `foo` conflicts with other definitions + ┌─ not_star_kind.fe:28:4 + │ +28 │ fn foo(gen: Gen) {} + │ ^^^ `foo` is defined here +29 │ +30 │ fn foo(gen: Gen) -> Gen {} + │ --- `foo` is redefined here + +error[2-0002]: `T` is not found + ┌─ not_star_kind.fe:21:10 + │ +21 │ impl Gen { + │ ^ `T` is not found + +error[3-0000]: expected `*` kind in this context + ┌─ not_star_kind.fe:7:8 + │ +7 │ f: Gen, + │ ^^^^^^^^ expected `*` kind here + +error[3-0000]: expected `*` kind in this context + ┌─ not_star_kind.fe:12:13 + │ +12 │ Variant(Gen, Gen) + │ ^^^^^^^^ expected `*` kind here + +error[3-0000]: expected `*` kind in this context + ┌─ not_star_kind.fe:12:23 + │ +12 │ Variant(Gen, Gen) + │ ^^^^^^^^ expected `*` kind here + +error[3-0000]: expected `*` kind in this context + ┌─ not_star_kind.fe:13:36 + │ +13 │ Variant2{ x: Gen, y: Gen } + │ ^^^^^^^^ expected `*` kind here + +error[3-0000]: expected `*` kind in this context + ┌─ not_star_kind.fe:17:8 + │ +17 │ f: Gen, + │ ^^^^^^^^ expected `*` kind here + +error[3-0000]: expected `*` kind in this context + ┌─ not_star_kind.fe:22:12 + │ +22 │ fn foo(self) {} + │ ^^^^ expected `*` kind here + +error[3-0000]: expected `*` kind in this context + ┌─ not_star_kind.fe:24:18 + │ +24 │ fn bar(self: Self) {} + │ ^^^^ expected `*` kind here + +error[3-0000]: expected `*` kind in this context + ┌─ not_star_kind.fe:28:13 + │ +28 │ fn foo(gen: Gen) {} + │ ^^^ expected `*` kind here + +error[3-0000]: expected `*` kind in this context + ┌─ not_star_kind.fe:30:13 + │ +30 │ fn foo(gen: Gen) -> Gen {} + │ ^^^^^^^^ expected `*` kind here + +error[3-0000]: expected `*` kind in this context + ┌─ not_star_kind.fe:30:26 + │ +30 │ fn foo(gen: Gen) -> Gen {} + │ ^^^ expected `*` kind here + + diff --git a/crates/uitest/fixtures/ty/def/trait_impl_conflict.snap b/crates/uitest/fixtures/ty/def/trait_impl_conflict.snap index bac73c9883..df604b66fd 100644 --- a/crates/uitest/fixtures/ty/def/trait_impl_conflict.snap +++ b/crates/uitest/fixtures/ty/def/trait_impl_conflict.snap @@ -1,23 +1,23 @@ --- source: crates/uitest/tests/ty.rs expression: diags -input_file: crates/uitest/fixtures/ty/trait_impl_conflict.fe +input_file: crates/uitest/fixtures/ty/def/trait_impl_conflict.fe --- error[5-0001]: conflict trait implementation - ┌─ trait_impl_conflict.fe:4:26 + ┌─ trait_impl_conflict.fe:8:26 │ 4 │ impl Foo for (i32, i32) {} - │ ^^^^^^^^^^ conflict trait implementation + │ ---------- conflict with this trait implementation · 8 │ impl Foo for (T, T) {} - │ ------ conflict with this trait implementation + │ ^^^^^^ conflict trait implementation error[5-0001]: conflict trait implementation - ┌─ trait_impl_conflict.fe:16:28 + ┌─ trait_impl_conflict.fe:17:28 │ 16 │ impl Foo for S {} - │ ^^^^^^^ conflict trait implementation + │ ------- conflict with this trait implementation 17 │ impl Foo for S {} - │ ----------- conflict with this trait implementation + │ ^^^^^^^^^^^ conflict trait implementation diff --git a/crates/uitest/fixtures/ty/def/trait_impl_conflict_const.fe b/crates/uitest/fixtures/ty/def/trait_impl_conflict_const.fe new file mode 100644 index 0000000000..4129b47935 --- /dev/null +++ b/crates/uitest/fixtures/ty/def/trait_impl_conflict_const.fe @@ -0,0 +1,10 @@ +pub trait Trait {} + +impl Trait<1> for i32 {} +impl Trait for i32 {} + +pub struct Foo {} + +impl Trait for Foo {} +impl Trait<10> for Foo<10> {} + diff --git a/crates/uitest/fixtures/ty/def/trait_impl_conflict_const.snap b/crates/uitest/fixtures/ty/def/trait_impl_conflict_const.snap new file mode 100644 index 0000000000..edf22d9b03 --- /dev/null +++ b/crates/uitest/fixtures/ty/def/trait_impl_conflict_const.snap @@ -0,0 +1,22 @@ +--- +source: crates/uitest/tests/ty.rs +expression: diags +input_file: crates/uitest/fixtures/ty/def/trait_impl_conflict_const.fe +--- +error[5-0001]: conflict trait implementation + ┌─ trait_impl_conflict_const.fe:4:33 + │ +3 │ impl Trait<1> for i32 {} + │ --- conflict with this trait implementation +4 │ impl Trait for i32 {} + │ ^^^ conflict trait implementation + +error[5-0001]: conflict trait implementation + ┌─ trait_impl_conflict_const.fe:9:20 + │ +8 │ impl Trait for Foo {} + │ ------ conflict with this trait implementation +9 │ impl Trait<10> for Foo<10> {} + │ ^^^^^^^ conflict trait implementation + + diff --git a/crates/uitest/fixtures/ty/trait_bound/const_ty.fe b/crates/uitest/fixtures/ty/trait_bound/const_ty.fe new file mode 100644 index 0000000000..51748d19c8 --- /dev/null +++ b/crates/uitest/fixtures/ty/trait_bound/const_ty.fe @@ -0,0 +1,17 @@ +pub trait Trait {} +impl Trait<2> for i32 {} + + +pub struct Wrapper> +where T: Trait<1> +{ + t: T +} + +fn foo_err(b: Wrapper) {} + +struct Foo {} +impl Trait for Foo {} + +fn bar_ok(t: Wrapper>) {} +fn baz_err(t: Wrapper>) {} \ No newline at end of file diff --git a/crates/uitest/fixtures/ty/trait_bound/const_ty.snap b/crates/uitest/fixtures/ty/trait_bound/const_ty.snap new file mode 100644 index 0000000000..6e3687c20f --- /dev/null +++ b/crates/uitest/fixtures/ty/trait_bound/const_ty.snap @@ -0,0 +1,18 @@ +--- +source: crates/uitest/tests/ty.rs +expression: diags +input_file: crates/uitest/fixtures/ty/trait_bound/const_ty.fe +--- +error[6-0003]: trait bound is not satisfied + ┌─ const_ty.fe:11:15 + │ +11 │ fn foo_err(b: Wrapper) {} + │ ^^^^^^^^^^^^ `i32` doesn't implement `Trait<1>` + +error[6-0003]: trait bound is not satisfied + ┌─ const_ty.fe:17:15 + │ +17 │ fn baz_err(t: Wrapper>) {} + │ ^^^^^^^^^^^^^^^ `Foo<2>` doesn't implement `Trait<1>` + + diff --git a/crates/uitest/tests/ty.rs b/crates/uitest/tests/ty.rs index afa7ed2e2f..9ca45523a7 100644 --- a/crates/uitest/tests/ty.rs +++ b/crates/uitest/tests/ty.rs @@ -17,6 +17,19 @@ fn run_ty_def(fixture: Fixture<&str>) { snap_test!(diags, fixture.path()); } +#[dir_test( + dir: "$CARGO_MANIFEST_DIR/fixtures/ty/const_ty", + glob: "*.fe" +)] +fn run_const_ty(fixture: Fixture<&str>) { + let mut driver = DriverDataBase::default(); + let path = Path::new(fixture.path()); + let top_mod = driver.top_mod_from_file(path, fixture.content()); + driver.run_on_top_mod(top_mod); + let diags = driver.format_diags(); + snap_test!(diags, fixture.path()); +} + #[dir_test( dir: "$CARGO_MANIFEST_DIR/fixtures/ty/trait_bound", glob: "*.fe" @@ -45,9 +58,10 @@ fn run_trait_impl(fixture: Fixture<&str>) { #[cfg(target_family = "wasm")] mod wasm { - use super::*; use wasm_bindgen_test::wasm_bindgen_test; + use super::*; + mod def { use super::*; @@ -68,6 +82,25 @@ mod wasm { } } + mod const_ty { + use super::*; + + #[dir_test( + dir: "$CARGO_MANIFEST_DIR/fixtures/ty/const_ty", + glob: "*.fe", + postfix: "wasm" + )] + #[dir_test_attr( + #[wasm_bindgen_test] + )] + fn run_const_ty(fixture: Fixture<&str>) { + let mut driver = DriverDataBase::default(); + let path = Path::new(fixture.path()); + let top_mod = driver.top_mod_from_file(path, fixture.content()); + driver.run_on_top_mod(top_mod); + } + } + mod trait_bound { use super::*;