diff --git a/checker/specification/staging.md b/checker/specification/staging.md index 4739e695..4b6700fc 100644 --- a/checker/specification/staging.md +++ b/checker/specification/staging.md @@ -1,3 +1,43 @@ Currently implementing: > This file is for work-in-progress and can help separating features that are being implemented to regressions + +### Narrowing + +#### Equality + +```ts +declare let a: string; +if (a === "hi") { + a satisfies "hello" +} +``` + +- Expected "hello", found "hi" + +#### Condition as a function + +```ts +declare let a: string; + +const equalsHi = (p: string) => p === "hi"; + +if (equalsHi(a)) { + a satisfies "hello" +} +``` + +- Expected "hello", found "hi" + +#### Passed around + +```ts +declare let a: string; + +const b = a; +if (b === "hi") { + a satisfies "hello" +} +``` + +- Expected "hello", found "hi" diff --git a/checker/specification/test.rs b/checker/specification/test.rs index c597d974..ff854aef 100644 --- a/checker/specification/test.rs +++ b/checker/specification/test.rs @@ -31,8 +31,8 @@ mod specification { // } // TODO under cfg -// const SIMPLE_DTS: Option<&str> = Some(include_str!("../definitions/simple.d.ts")); -const SIMPLE_DTS: Option<&str> = None; +const SIMPLE_DTS: Option<&str> = Some(include_str!("../definitions/simple.d.ts")); +// const SIMPLE_DTS: Option<&str> = None; /// Called by each test fn check_errors( diff --git a/checker/specification/to_implement.md b/checker/specification/to_implement.md index a4bbfa75..f255c057 100644 --- a/checker/specification/to_implement.md +++ b/checker/specification/to_implement.md @@ -633,44 +633,6 @@ function optionalNumber(n: number | undefined): string { - Cannot return string, found number | 2 -#### Equality - -```ts -declare let a: string; -if (a === "hi") { - a satisfies "hello" -} -``` - -- Expected "hello", found "hi" - -#### Condition as a function - -```ts -declare let a: string; - -const equalsHi = (p: string) => p === "hi"; - -if (equalsHi(a)) { - a satisfies "hello" -} -``` - -- Expected "hello", found "hi" - -#### Passed around - -```ts -declare let a: string; - -const b = a; -if (b === "hi") { - a satisfies "hello" -} -``` - -- Expected "hello", found "hi" - ### Mapped types #### Specialisation diff --git a/checker/src/context/information.rs b/checker/src/context/information.rs index 51212f22..556a8296 100644 --- a/checker/src/context/information.rs +++ b/checker/src/context/information.rs @@ -38,6 +38,10 @@ pub struct LocalInformation { /// *not quite the best place, but used in [`InformationChain`]* pub(crate) object_constraints: HashMap, + /// WIP narrowing + /// TODO how will chaining but not cycles work + pub(crate) narrowed_values: HashMap, + /// For super calls etc /// /// TODO not great that this has to be Option to satisfy Default diff --git a/checker/src/context/mod.rs b/checker/src/context/mod.rs index 5a9df5e4..a514d173 100644 --- a/checker/src/context/mod.rs +++ b/checker/src/context/mod.rs @@ -1183,11 +1183,12 @@ pub(crate) fn get_value_of_variable( let res = res.or_else(|| fact.variable_current_value.get(&on).copied()); - // TODO WIP narrowing - // TODO in remaining info, don't loop again if let Some(res) = res { - return Some(res); + let narrowed_value = + info.get_chain_of_info().find_map(|info| info.narrowed_values.get(&res).copied()); + + return Some(narrowed_value.unwrap_or(res)); } } None diff --git a/checker/src/features/conditional.rs b/checker/src/features/conditional.rs index 306de014..b73c7caa 100644 --- a/checker/src/features/conditional.rs +++ b/checker/src/features/conditional.rs @@ -39,6 +39,12 @@ where let mut truthy_environment = environment .new_lexical_environment(Scope::Conditional { antecedent: condition, is_switch: None }); + super::narrowing::narrow_based_on_expression( + condition, + &mut truthy_environment.info, + &checking_data.types, + ); + let result = then_evaluate(&mut truthy_environment, checking_data); let Context { diff --git a/checker/src/features/mod.rs b/checker/src/features/mod.rs index a8f608e3..8c6d8318 100644 --- a/checker/src/features/mod.rs +++ b/checker/src/features/mod.rs @@ -14,6 +14,7 @@ pub mod exceptions; pub mod functions; pub mod iteration; pub mod modules; +pub mod narrowing; pub mod objects; pub mod operations; pub mod template_literal; diff --git a/checker/src/features/narrowing.rs b/checker/src/features/narrowing.rs new file mode 100644 index 00000000..d65b1a57 --- /dev/null +++ b/checker/src/features/narrowing.rs @@ -0,0 +1,29 @@ +use crate::{ + features::operations::CanonicalEqualityAndInequality, + types::{Constructor, PolyNature, TypeStore}, + LocalInformation, Type, TypeId, +}; + +/// TODO +/// - Ors and ands +/// - Restrictions +pub fn narrow_based_on_expression( + condition: TypeId, + into: &mut LocalInformation, + types: &TypeStore, +) { + let r#type = types.get_type_by_id(condition); + if let Type::Constructor(Constructor::CanonicalRelationOperator { + lhs, + operator: CanonicalEqualityAndInequality::StrictEqual, + rhs, + }) = r#type + { + if let Type::RootPolyType(PolyNature::Parameter { .. }) = types.get_type_by_id(*lhs) { + crate::utilities::notify!("lhs is {:?} with {:?}", lhs, types.get_type_by_id(*rhs)); + } + + // TODO reflexive ? + into.narrowed_values.insert(*lhs, *rhs); + } +}