diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml new file mode 100644 index 00000000..4c772b00 --- /dev/null +++ b/.github/workflows/examples.yml @@ -0,0 +1,55 @@ +name: Examples + +on: + push: + branches: [main] + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + run-examples: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Build Ezno + run: cargo build --release + env: + CARGO_PROFILE_RELEASE_DEBUG: true + + - name: Run checker on example files + shell: bash + run: | + files=( + "https://jsr.io/@yossydev/hello-world/1.0.0/index.ts" + "https://jsr.io/@bengineering/shuffle-binary/0.0.1/index.ts" + "https://jsr.io/@bengineering/mulberry32/0.0.1/mod.ts" + "https://jsr.io/@luca/cases/1.0.0/mod.ts" + "https://jsr.io/@std/assert/1.0.2/assertion_error.ts" + "https://jsr.io/@std/text/1.0.3/levenshtein_distance.ts" + "https://jsr.io/@gnome/monads/0.0.0/src/option.ts" + "https://raw.githubusercontent.com/getify/deePool/master/src/deePool.js" + "https://raw.githubusercontent.com/silen-z/fiveway/main/packages/fiveway/src/id.ts" + ) + + for url in "${files[@]}"; do + header="--- $url ---" + echo $header + curl -s $url > temp.ts + ./target/release/ezno check temp.ts --timings + echo "${header//?/-}" + echo "" + done \ No newline at end of file diff --git a/.github/workflows/performance-and-size.yml b/.github/workflows/performance-and-size.yml index 8775f83e..e58e86c5 100644 --- a/.github/workflows/performance-and-size.yml +++ b/.github/workflows/performance-and-size.yml @@ -93,6 +93,7 @@ jobs: cargo run -p ezno-parser --example code_blocks_to_script all.md --comment-headers --out ./all.tsx ./target/release/ezno check all.tsx --timings || true + hyperfine -i './target/release/ezno check all.tsx' echo "::endgroup::" - name: Run checker performance on large file @@ -108,6 +109,7 @@ jobs: done ./target/release/ezno check large.tsx --timings --max-diagnostics 0 || true + hyperfine -i './target/release/ezno check large.tsx' echo "::endgroup::" - name: Run parsing & stringing (minfied) benchmarks diff --git a/checker/definitions/internal.ts.d.bin b/checker/definitions/internal.ts.d.bin index 1460481e..720f6309 100644 Binary files a/checker/definitions/internal.ts.d.bin and b/checker/definitions/internal.ts.d.bin differ diff --git a/checker/definitions/overrides.d.ts b/checker/definitions/overrides.d.ts index 45ea44d4..03781523 100644 --- a/checker/definitions/overrides.d.ts +++ b/checker/definitions/overrides.d.ts @@ -77,7 +77,8 @@ declare class Array { let i: number = 0; while (i < length) { const value = this[i]; - if (!cb(value, i++)) { + const result = cb(value, i++) as boolean; + if (!result) { return false } } @@ -103,10 +104,10 @@ declare class Array { if (length === 0) { return "" } - let s: string = "" + this[0]; + let s: string = "" + (this[0] as string); while (i < length) { s += joiner; - s += this[i++]; + s += this[i++] as string; } return s } @@ -118,10 +119,18 @@ declare class Array { return this[index] } } + + static isArray(item: any) { + return item instanceof Array; + } } type Record = { [P in K]: T } +type LessThan = ExclusiveRange; +type GreaterThan = ExclusiveRange; +type Integer = MultipleOf<1>; + declare class Map { #keys: Array = []; #value: Array = []; @@ -129,9 +138,9 @@ declare class Map { declare class Math { @Constant - static sin(x: number): number; + static sin(x: number): InclusiveRange<-1, 1>; @Constant - static cos(x: number): number; + static cos(x: number): InclusiveRange<-1, 1>; @Constant static tan(x: number): number; @Constant @@ -147,11 +156,14 @@ declare class Math { @Constant static trunc(x: number): number; + @Constant + static imul(x: number, y: number): number; + static PI: 3.141592653589793 static E: 2.718281828459045 @InputOutput - static random(): number; + static random(): InclusiveRange<0, 1>; } @Primitive("string") @@ -172,6 +184,20 @@ declare class String { split(splitter: string): Array; } +@Primitive("number") +declare class Number { + static NEGATIVE_INFINITY: NegativeInfinity; + static POSITIVE_INFINITY: Infinity; + + // static isFinite(item: any) { + // return !(item === Number.NEGATIVE_INFINITY || item === Number.POSITIVE_INFINITY || Number.isNaN(item)) + // } + + static isNaN(item: any) { + return item !== item; + } +} + declare class Promise { } declare class RegExp { @@ -377,7 +403,7 @@ declare class Object { return entries } - // TODO multiple arguments + // TODO spread source static assign(target: object, source: object): object { for (const key in source) { target[key] = source[key] diff --git a/checker/definitions/simple.d.ts b/checker/definitions/simple.d.ts index 6534d8ec..c1b03c9d 100644 --- a/checker/definitions/simple.d.ts +++ b/checker/definitions/simple.d.ts @@ -37,7 +37,7 @@ declare function debug_state(): void // A function, as it should be! @Constant -declare function satisfies(t: T): T +declare function satisfies(t: YE): YE interface ImportEnv { [key: string]: string; @@ -75,7 +75,7 @@ declare class Array { map(cb: (t: T, i?: number) => U): Array { const { length } = this, mapped: Array = []; let i: number = 0; - while (i < length) { + while (i < (length as number)) { const value = this[i]; const newValue = cb(value, i++); mapped.push(newValue) @@ -176,6 +176,11 @@ declare class Array { // return this[index] // } // } + + /// TODO incorrect definition, doesn't account for realms + static isArray(item: any) { + return item instanceof Array; + } } declare class Map { @@ -206,6 +211,10 @@ declare class Map { type Record = { [P in K]: T } +type LessThan = ExclusiveRange; +type GreaterThan = ExclusiveRange; +type Integer = MultipleOf<1>; + /** * Exclude from T those types that are assignable to U */ @@ -218,9 +227,9 @@ type Extract = T extends U ? T : never; declare class Math { @Constant - static sin(x: number): number; + static sin(x: number): InclusiveRange<-1, 1>; @Constant - static cos(x: number): number; + static cos(x: number): InclusiveRange<-1, 1>; @Constant static tan(x: number): number; @Constant @@ -232,15 +241,18 @@ declare class Math { @Constant static log(x: number): number; - // TODO newer method + // TODO newer methods @Constant static trunc(x: number): number; + @Constant + static imul(x: number, y: number): number; + static PI: 3.141592653589793 static E: 2.718281828459045 @InputOutput - static random(): number; + static random(): 0 | InclusiveRange<0, 1>; } @Primitive("string") @@ -261,6 +273,20 @@ declare class String { split(splitter: string): Array; } +@Primitive("number") +declare class Number { + static NEGATIVE_INFINITY: NegativeInfinity; + static POSITIVE_INFINITY: Infinity; + + // static isFinite(item: any) { + // return !(item === Number.NEGATIVE_INFINITY || item === Number.POSITIVE_INFINITY || Number.isNaN(item)) + // } + + static isNaN(item: any) { + return item !== item; + } +} + declare class Promise { } type ResponseBody = string; diff --git a/checker/examples/run_checker.rs b/checker/examples/run_checker.rs index b06a5214..aab40341 100644 --- a/checker/examples/run_checker.rs +++ b/checker/examples/run_checker.rs @@ -78,7 +78,7 @@ fn main() { let end = now.elapsed(); let count = result.diagnostics.into_iter().len(); eprintln!("Found {count} diagnostics in {end:?}"); - } else if args.iter().any(|arg| arg == "--debug-diagnostics") { + } else if args.iter().any(|arg| arg == "--verbose-diagnostics") { eprintln!("Diagnostics:"); for diagnostic in result.diagnostics { eprintln!("{diagnostic:?}"); diff --git a/checker/fuzz/Cargo.toml b/checker/fuzz/Cargo.toml index 788146ed..2359021c 100644 --- a/checker/fuzz/Cargo.toml +++ b/checker/fuzz/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ezno-parser-fuzz" +name = "ezno-checker-fuzz" version = "0.0.0" publish = false edition = "2021" diff --git a/checker/specification/specification.md b/checker/specification/specification.md index faaf8845..6fb08cab 100644 --- a/checker/specification/specification.md +++ b/checker/specification/specification.md @@ -366,8 +366,10 @@ object[key] satisfies boolean; > TODO this creates a fat or type ```ts -const array = [1, 2, 3]; -array[Math.random()] satisfies string; +function func(idx: number) { + const array = [1, 2, 3]; + array[idx] satisfies string; +} ``` - Expected string, found 1 | 2 | 3 | undefined @@ -1061,7 +1063,7 @@ function func(value: number) { return "another" } -loop satisfies (a: number) => "is three" | "another"; +func satisfies (a: number) => "is three" | "another"; function loop(value: number) { for (let i = 0; i < 10; i++) { @@ -2371,6 +2373,164 @@ fakeRead(array1) - Invalid assignment through parameter +### Operators + +#### Always known math + +```ts +// True regardless of +function func(a: number) { return a ** 0 } + +func satisfies string; + +declare let x: NotNotANumber; +(x ** 1 === x) satisfies true; +``` + +- Expected string, found (a: number) => 1 + +#### Inequality checks + +```ts +function func1(a: GreaterThan<4>) { + (a > 3) satisfies true; + (a < 3) satisfies false; + (a < 10) satisfies string; +} + +// thanks narrowing 🙏 +function func2(a: number) { + if (a < 2) { + (a > 6) satisfies false; + (a < 7) satisfies true; + (a > 0) satisfies null; + } +} +``` + +- This equality is always false as GreaterThan<4> and 3 have no overlap +- Expected string, found boolean +- This equality is always false as LessThan<2> and 6 have no overlap +- Expected null, found boolean + +#### Arithmetic operand check + +> This is allowed under non strict casts option (and will return NaN) but the tests run with strict casts on + +> This would need to support [Symbol.toPrimitive] + a bunch of error handling + +```ts +console + 2 +``` + +> TODO temp diagnostic + +- Cannot Console Add 2 + +#### Inequality operand check + +```ts +function isLessThan(a: number) { + a < console; +} +``` + +> TODO temp diagnostic + +- Cannot number LessThan Console + +#### Unary operand check + +```ts +function func(a: number, b: boolean) { + const x = !a; + const y = ~b; + (!b), (~a); +} +``` + +> TODO temp diagnostic + +- Cannot LogicalNot number +- Cannot BitwiseNot boolean + +#### Disjoint equality + +```ts +function neverEqual(a: string, b: number) { + (a === b) satisfies false; +} + +function sometimes(a: string | number, b: number) { + (a === b) satisfies string; +} +``` + +- This equality is always false as string and number have no overlap +- Expected string, found boolean + +#### Disjoint equality for number intrinsics + +```ts +declare function getNumberBetweenFive(): InclusiveRange<0, 5> & Integer; + +getNumberBetweenFive() === 2; +getNumberBetweenFive() === 2.2; +getNumberBetweenFive() === 7; +``` + +- This equality is always false as InclusiveRange<0, 5> & Integer and 2.2 have no overlap +- This equality is always false as InclusiveRange<0, 5> & Integer and 7 have no overlap + +#### Identity equality + +> Can only do it not NaN + +```ts +function func(a: string, b: number) { + (a === a) satisfies string; + (b === b) satisfies null; +} +``` + +- Expected string, found true +- Expected null, found boolean + +#### Ranges for interal types + +```ts +function func(a: number) { + (Math.sin(a) > -2) satisfies true; + (Math.sin(a) > -1) satisfies string; +} +``` + +- Expected string, found boolean + +#### Ranges after operators + +```ts +function func(a: number) { + (Math.sin(a) * 5) satisfies null; + ((Math.sin(a) + 10)) * 2 satisfies string; +} +``` + +- Expected null, found InclusiveRange\<-5, 5> +- Expected string, found InclusiveRange\<18, 22> + +#### Not disjoint + +```ts +function func(param: number) { + if (param !== 2) { + return param === 2 + } +} +``` + +- This equality is always false as Not<2> and 2 have no overlap + ### Statements, declarations and expressions > Some of these are part of synthesis, rather than checking @@ -2433,66 +2593,6 @@ interface X { - Expected 4, found 5 - Type { a: 3 } is not assignable to type X -#### `RegExp` constructor - -> RegExp = Regular expression -> In the future, their definition could be considered and evaluated at runtime - -```ts -const regexp = /hi/ satisfies string; -``` - -- Expected string, found /hi/ - -#### Invalid regular expressions - -```ts -const regexp1 = /(?a2)/; -const regexp2 = new RegExp("?a2"); -``` - -- Invalid regular expression: Invalid token at named capture group identifier -- Invalid regular expression: Invalid atom character - -#### Constant `RegExp.exec` - -```ts -const regexp = /hi/; -const match = regexp.exec("hi"); -match satisfies number; -match.index satisfies string; -match.input satisfies boolean; -``` - -- Expected number, found ["hi"] -- Expected string, found 0 -- Expected boolean, found "hi" - -#### Constant `RegExp.exec` groups - -```ts -const regexp = /Hi (?.*)/; -const match = regexp.exec("Hi Ben"); -match.input satisfies number; -match.groups satisfies string; -``` - -- Expected number, found "Hi Ben" -- Expected string, found { name: "Ben" } - -#### Constant `RegExp.exec` groups greedy - -```ts -const regexp = /.*(?[a-z]+)(?[0-9]+)/; -const match = regexp.exec("ez as abc123"); -match.input satisfies number; -match.groups.x satisfies "c"; -match.groups.y satisfies boolean; -``` - -- Expected number, found "ez as abc123" -- Expected boolean, found "123" - #### Null and undefined ```ts @@ -2769,6 +2869,112 @@ myTag`${name}Hello ` satisfies string; - Expected string, found { static_parts: ["", "Hello "], other: "Ben" } +#### Interface generic constraint checking + +```ts +interface BoxString { + inner: T +} + +type BoxedFour = BoxString<"4">; +type BoxedFive = BoxString<5>; +``` + +- Generic argument 5 does not match string + +#### Mismatch in generic argument count + +```ts +interface BoxString { + inner: T +} + +let x: BoxString; +``` + +- Expected 1 type argument, but got 2 + +#### Optional property access + +```ts +interface X { + a: string + b: string +} + +function func(x: X | null) { + x.a; + x?.b satisfies number; +} +``` + +> TODO message should just be null + +- No property 'a' on X | null +- Expected number, found undefined | string + +### Regular expressions + +#### `RegExp` constructor + +> RegExp = Regular expression +> In the future, their definition could be considered and evaluated at runtime + +```ts +const regexp = /hi/ satisfies string; +``` + +- Expected string, found /hi/ + +#### Invalid regular expressions + +```ts +const regexp1 = /(?a2)/; +const regexp2 = new RegExp("?a2"); +``` + +- Invalid regular expression: Invalid token at named capture group identifier +- Invalid regular expression: Invalid atom character + +#### Constant `RegExp.exec` + +```ts +const regexp = /hi/; +const match = regexp.exec("hi"); +match satisfies number; +match.index satisfies string; +match.input satisfies boolean; +``` + +- Expected number, found ["hi"] +- Expected string, found 0 +- Expected boolean, found "hi" + +#### Constant `RegExp.exec` groups + +```ts +const regexp = /Hi (?.*)/; +const match = regexp.exec("Hi Ben"); +match.input satisfies number; +match.groups satisfies string; +``` + +- Expected number, found "Hi Ben" +- Expected string, found { name: "Ben" } + +#### Constant `RegExp.exec` groups greedy + +```ts +const regexp = /.*(?[a-z]+)(?[0-9]+)/; +const match = regexp.exec("ez as abc123"); +match.input satisfies number; +match.groups.x satisfies "c"; +match.groups.y satisfies boolean; +``` + +- Expected number, found "ez as abc123" +- Expected boolean, found "123" + ### Async and `Promise`s > Position of await is not checked (here is fine because top level await) @@ -3089,7 +3295,7 @@ type X = number; const a: X = 2; ``` -- Type 'X' has no generic parameters +- Cannot pass a type argument to a non-generic type #### Type alias with type parameters @@ -4179,6 +4385,51 @@ function func2(param: any): asserts param is boolean { - Cannot return asserts any is string because the function is expected to return asserts any is number +#### External predicate + +```ts +function func(param: number | Array) { + if (Array.isArray(param)) { + param satisfies null; + } +} +``` + +- Expected null, found Array\ + +#### Number `isNan` + +```ts +function func(param: number) { + if (param !== param) { + param satisfies string; + } + + // Derives from `!==` + if (Number.isNaN(param)) { + param satisfies null; + } +} +``` + +- Expected string, found Not\ +- Expected null, found Not\ + +#### Narrowing falsy values + +```ts +function getName(name?: string) { + if (name) { + name satisfies undefined; + return name + } else { + return "default" + } +} +``` + +- Expected undefined, found string + ### Object constraint > Any references to a annotated variable **must** be within its LHS type. These test that it carries down to objects. @@ -4725,13 +4976,13 @@ register(document.title) 6 satisfies GreaterThan<2>; -4 satisfies GreaterThan<2>; -6 satisfies LessThan<2>; --4 satisfies LessThan<2>; +2 satisfies LessThan<2>; +-3 satisfies LessThan<2>; ``` - Expected MultipleOf\<2>, found 5 - Expected GreaterThan\<2>, found -4 -- Expected LessThan\<2>, found 6 +- Expected LessThan\<2>, found 2 #### `Not` diff --git a/checker/specification/to_implement.md b/checker/specification/to_implement.md index 4561c860..11cce36d 100644 --- a/checker/specification/to_implement.md +++ b/checker/specification/to_implement.md @@ -519,18 +519,6 @@ y satisfies string; ### Expressions -#### Bad arithmetic operator - -> This is allowed under non strict casts option (and will return NaN) but the tests run with strict casts on - -> This would need to support [Symbol.toPrimitive] + a bunch of error handling - -```ts -console + 2 -``` - -- Expected number, found Console - #### Array spread ```ts @@ -733,86 +721,19 @@ f(false) ### Narrowing -#### Has property - -> TODO maybe need to constrain side effects here - -```ts -function func(parameter: { property: string }) { - if (parameter.property === "hello") { - parameter.property satisfies 4; - } -} -``` - -- Expected 4, found "hello" - -> TODO `typeof`, `instanceof`, conditional, across a function - -#### Conditional operator - -```ts -function optionalNumber(n: number | undefined): string { - return n ?? 2 -} -``` - -- 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"; +#### In `while` loop -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" - -#### Optional property access +> This is fine because of registering of assignments +> This does not work because i = fv + 1 ```ts -interface X { - a: string - b: string +let i: number = 0; +while (i++ < 5) { + i satisfies null; } - -declare let x: X | null; - -x.a; -x?.b satisfies number; ``` -- Cannot get 'a' on null -- Expected number, found string +- Expected null, found LessThan<6> ### Generics @@ -849,32 +770,6 @@ a satisfies 0; b satisfies string; - Expected string, found 1 -#### Always known math - -```ts -function func(a: number) { return a ** 0 } - -print_type(func) - -declare let x: NotNotANumber; - -print_type(x ** 1 === x) -``` - -- Expected string, found 1 -- True - -#### Less than checks - -```ts -function x(a: GreaterThan<4>) { - (a < 3) satisfies false; - (a < 10) satisfies string; -} -``` - -- Expected string, found boolean - ### Control flow #### Conditional break diff --git a/checker/src/context/environment.rs b/checker/src/context/environment.rs index 9c5754d2..a4a2dece 100644 --- a/checker/src/context/environment.rs +++ b/checker/src/context/environment.rs @@ -5,7 +5,7 @@ use crate::{ context::{get_on_ctx, information::ReturnState}, diagnostics::{ NotInLoopOrCouldNotFindLabel, PropertyKeyRepresentation, TypeCheckError, - TypeStringRepresentation, TDZ, + TypeStringRepresentation, VariableUsedInTDZ, }, events::{Event, FinalEvent, RootReference}, features::{ @@ -661,7 +661,7 @@ impl<'a> Environment<'a> { .get_chain_of_info() .any(|info| info.variable_current_value.contains_key(&variable_id)) { - return Err(AssignmentError::TDZ(TDZ { + return Err(AssignmentError::VariableUsedInTDZ(VariableUsedInTDZ { position: assignment_position, variable_name: variable_name.to_owned(), })); @@ -1035,10 +1035,12 @@ impl<'a> Environment<'a> { if let Some(current_value) = current_value { Ok(VariableWithValue(og_var.clone(), current_value)) } else { - checking_data.diagnostics_container.add_error(TypeCheckError::TDZ(TDZ { - variable_name: self.get_variable_name(og_var.get_id()).to_owned(), - position, - })); + checking_data.diagnostics_container.add_error(TypeCheckError::VariableUsedInTDZ( + VariableUsedInTDZ { + variable_name: self.get_variable_name(og_var.get_id()).to_owned(), + position, + }, + )); Ok(VariableWithValue(og_var.clone(), TypeId::ERROR_TYPE)) } } @@ -1093,7 +1095,7 @@ impl<'a> Environment<'a> { already_checked: Default::default(), mode: Default::default(), contributions: Default::default(), - others: SubTypingOptions::satisfies(), + others: SubTypingOptions::default(), // TODO don't think there is much case in constraining it here object_constraints: None, }; @@ -1519,4 +1521,28 @@ impl<'a> Environment<'a> { }, )); } + + pub fn new_infer_type( + &mut self, + expected: TypeId, + infer_name: &str, + types: &mut TypeStore, + ) -> TypeId { + if let Scope::TypeAnnotationCondition { ref mut infer_parameters } = self.context_type.scope + { + let infer_type = types.register_type(Type::RootPolyType(PolyNature::InferGeneric { + name: infer_name.to_owned(), + extends: expected, + })); + + let existing = infer_parameters.insert(infer_name.to_owned(), infer_type); + if existing.is_some() { + crate::utilities::notify!("Raise error diagnostic"); + } + infer_type + } else { + crate::utilities::notify!("Raise error diagnostic"); + TypeId::UNIMPLEMENTED_ERROR_TYPE + } + } } diff --git a/checker/src/context/information.rs b/checker/src/context/information.rs index e94b6a7d..e84835be 100644 --- a/checker/src/context/information.rs +++ b/checker/src/context/information.rs @@ -82,8 +82,10 @@ impl ReturnState { match self { ReturnState::Continued => *self = new, ReturnState::Rolling { .. } => match new { - ReturnState::Continued => todo!(), - ReturnState::Rolling { .. } => todo!(), + ReturnState::Continued => {} + ReturnState::Rolling { .. } => { + crate::utilities::notify!("Warning not accepting second rolling"); + } new @ ReturnState::Finished(_) => { crate::utilities::notify!("Warning overwriting conditional"); *self = new; @@ -245,12 +247,6 @@ pub trait InformationChain { } } -impl InformationChain for LocalInformation { - fn get_chain_of_info(&self) -> impl Iterator { - std::iter::once(self) - } -} - pub struct ModuleInformation<'a> { pub top: &'a LocalInformation, pub module: &'a LocalInformation, diff --git a/checker/src/context/mod.rs b/checker/src/context/mod.rs index 55a5ddc6..e8b11f28 100644 --- a/checker/src/context/mod.rs +++ b/checker/src/context/mod.rs @@ -14,7 +14,9 @@ use source_map::SpanWithSource; use crate::{ context::environment::ExpectedReturnType, - diagnostics::{CannotRedeclareVariable, TypeCheckError, TypeStringRepresentation, TDZ}, + diagnostics::{ + CannotRedeclareVariable, TypeCheckError, TypeStringRepresentation, VariableUsedInTDZ, + }, events::RootReference, features::{ assignments::Reference, @@ -392,7 +394,7 @@ impl Context { VariableMutability::Mutable { reassignment_constraint: None } | VariableMutability::Constant => { crate::utilities::notify!("TODO get value"); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } VariableMutability::Mutable { reassignment_constraint: Some(value), @@ -401,19 +403,19 @@ impl Context { // TODO VariableOrImport::MutableImport { .. } => { crate::utilities::notify!("TODO MutableImport"); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } // TODO VariableOrImport::ConstantImport { .. } => { crate::utilities::notify!("TODO ConstantImport"); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } } }) } Reference::Property { .. } => { crate::utilities::notify!("TODO get object constraint on object"); - Some(TypeId::ERROR_TYPE) + Some(TypeId::UNIMPLEMENTED_ERROR_TYPE) } } } @@ -864,10 +866,8 @@ impl Context { _ => None, } } else { - crate::utilities::notify!( - "TODO get root this type, returning ERROR_TYPE for now" - ); - Some(TypeId::ERROR_TYPE) + crate::utilities::notify!("TODO get root this type"); + Some(TypeId::UNIMPLEMENTED_ERROR_TYPE) } }) .unwrap() @@ -1008,7 +1008,7 @@ pub enum AssignmentError { value_type: TypeStringRepresentation, assignment_position: SpanWithSource, }, - TDZ(TDZ), + VariableUsedInTDZ(VariableUsedInTDZ), } /// TODO mutable let imports diff --git a/checker/src/context/root.rs b/checker/src/context/root.rs index 81eb951d..69f0ffd7 100644 --- a/checker/src/context/root.rs +++ b/checker/src/context/root.rs @@ -76,12 +76,16 @@ impl RootContext { ("Capitalize".to_owned(), TypeId::STRING_CAPITALIZE), ("Uncapitalize".to_owned(), TypeId::STRING_UNCAPITALIZE), ("NoInfer".to_owned(), TypeId::NO_INFER), - ("LessThan".to_owned(), TypeId::LESS_THAN), - ("GreaterThan".to_owned(), TypeId::GREATER_THAN), + ("InclusiveRange".to_owned(), TypeId::INCLUSIVE_RANGE), + ("ExclusiveRange".to_owned(), TypeId::EXCLUSIVE_RANGE), ("MultipleOf".to_owned(), TypeId::MULTIPLE_OF), - ("NotNotANumber".to_owned(), TypeId::NOT_NOT_A_NUMBER), + ("NotNotANumber".to_owned(), TypeId::NUMBER_BUT_NOT_NOT_A_NUMBER), ("Not".to_owned(), TypeId::NOT_RESTRICTION), ("CaseInsensitive".to_owned(), TypeId::CASE_INSENSITIVE), + ("Infinity".to_owned(), TypeId::INFINITY), + ("NegativeInfinity".to_owned(), TypeId::NEG_INFINITY), + ("MinFloat".to_owned(), TypeId::FLOAT_MIN), + ("MaxFloat".to_owned(), TypeId::FLOAT_MAX), ]); let mut info = LocalInformation::default(); diff --git a/checker/src/diagnostics.rs b/checker/src/diagnostics.rs index 89d98a0a..94db0a0b 100644 --- a/checker/src/diagnostics.rs +++ b/checker/src/diagnostics.rs @@ -2,9 +2,7 @@ use crate::{ context::{environment::Label, AssignmentError, InformationChain}, diagnostics, - features::{ - modules::CouldNotOpenFile, operations::MathematicalAndBitwise, CannotDeleteFromError, - }, + features::{modules::CouldNotOpenFile, CannotDeleteFromError}, types::{ calling::FunctionCallingError, printing::print_type_with_type_arguments, @@ -53,12 +51,12 @@ pub enum Diagnostic { /// Temporary dead zone. Between the variable identifier being hoisted and the value being assigned #[allow(clippy::upper_case_acronyms)] -pub struct TDZ { +pub struct VariableUsedInTDZ { pub variable_name: String, pub position: SpanWithSource, } -pub struct InvalidRegexp { +pub struct InvalidRegExp { pub error: String, pub position: SpanWithSource, } @@ -328,13 +326,8 @@ pub(crate) enum TypeCheckError<'a> { position: SpanWithSource, }, CouldNotFindType(&'a str, Vec<&'a str>, SpanWithSource), - TypeHasNoGenericParameters(String, SpanWithSource), /// For all `=`, including from declarations AssignmentError(AssignmentError), - #[allow(dead_code)] - InvalidComparison(TypeStringRepresentation, TypeStringRepresentation), - #[allow(dead_code)] - InvalidUnaryOperation(crate::features::operations::PureUnary, TypeStringRepresentation), SetPropertyError(SetPropertyError), ReturnedTypeDoesNotMatch { expected_return_type: TypeStringRepresentation, @@ -384,13 +377,18 @@ pub(crate) enum TypeCheckError<'a> { name: String, position: SpanWithSource, }, - /// This is for structure generics - #[allow(dead_code)] + /// This is for structure generics (type annotations) GenericArgumentDoesNotMeetRestriction { parameter_restriction: TypeStringRepresentation, argument: TypeStringRepresentation, position: SpanWithSource, }, + /// This is for structure generics (type annotations) + GenericArgumentCountMismatch { + expected_count: usize, + count: usize, + position: SpanWithSource, + }, #[allow(dead_code)] NotTopLevelImport(SpanWithSource), DuplicateImportName { @@ -423,14 +421,25 @@ pub(crate) enum TypeCheckError<'a> { position: SpanWithSource, }, #[allow(clippy::upper_case_acronyms)] - TDZ(TDZ), - #[allow(dead_code)] + VariableUsedInTDZ(VariableUsedInTDZ), InvalidMathematicalOrBitwiseOperation { - operator: MathematicalAndBitwise, + operator: crate::features::operations::MathematicalAndBitwise, + lhs: TypeStringRepresentation, + rhs: TypeStringRepresentation, + position: SpanWithSource, + }, + // Only for `<` `>` etc + InvalidEqualityOperation { + operator: crate::features::operations::EqualityAndInequality, lhs: TypeStringRepresentation, rhs: TypeStringRepresentation, position: SpanWithSource, }, + InvalidUnaryOperation { + operator: crate::features::operations::UnaryOperation, + operand: TypeStringRepresentation, + position: SpanWithSource, + }, #[allow(dead_code)] InvalidCast { position: SpanWithSource, @@ -457,7 +466,7 @@ pub(crate) enum TypeCheckError<'a> { position: SpanWithSource, }, CannotDeleteProperty(CannotDeleteFromError), - InvalidRegexp(InvalidRegexp), + InvalidRegExp(InvalidRegExp), } #[allow(clippy::useless_format)] @@ -571,7 +580,7 @@ impl From> for Diagnostic { kind, } } - AssignmentError::TDZ(TDZ { variable_name, position }) => { + AssignmentError::VariableUsedInTDZ(VariableUsedInTDZ { variable_name, position }) => { Diagnostic::Position { reason: format!("Cannot assign to '{variable_name}' before declaration"), position, @@ -613,13 +622,6 @@ impl From> for Diagnostic { position: at, kind, }, - TypeCheckError::TypeHasNoGenericParameters(name, position) => { - Diagnostic::Position { - reason: format!("Type '{name}' has no generic parameters"), - position, - kind, - } - } TypeCheckError::NonTopLevelExport(position) => Diagnostic::Position { reason: "Cannot export at not top level".to_owned(), position, @@ -682,6 +684,24 @@ impl From> for Diagnostic { position, kind, }, + TypeCheckError::GenericArgumentCountMismatch { + count, + expected_count, + position, + } => { + let reason = if expected_count == 0 { + "Cannot pass a type argument to a non-generic type".to_owned() + } else if expected_count == 1 { + format!("Expected 1 type argument, but got {count}") + } else { + format!("Expected {expected_count} type arguments, but got {count}") + }; + Diagnostic::Position { + position, + kind, + reason + } + }, TypeCheckError::NotTopLevelImport(position) => Diagnostic::Position { reason: "Import must be in the top of the scope".to_owned(), position, @@ -736,19 +756,35 @@ impl From> for Diagnostic { position, kind, }, - TypeCheckError::TDZ(TDZ { position, variable_name }) => Diagnostic::Position { + TypeCheckError::VariableUsedInTDZ(VariableUsedInTDZ { position, variable_name }) => Diagnostic::Position { reason: format!("Variable '{variable_name}' used before declaration"), position, kind, }, - TypeCheckError::InvalidComparison(_, _) => todo!(), - TypeCheckError::InvalidUnaryOperation(_, _) => todo!(), TypeCheckError::InvalidMathematicalOrBitwiseOperation { operator, lhs, rhs, position } => Diagnostic::Position { // TODO temp reason: format!("Cannot {lhs} {operator:?} {rhs}"), position, kind, }, + TypeCheckError::InvalidUnaryOperation { + operator, + operand, + position, + } => { + Diagnostic::Position { + // TODO temp + reason: format!("Cannot {operator:?} {operand}"), + position, + kind, + } + }, + TypeCheckError::InvalidEqualityOperation { operator, lhs, rhs, position } => Diagnostic::Position { + // TODO temp + reason: format!("Cannot {lhs} {operator:?} {rhs}"), + position, + kind, + }, TypeCheckError::NotInLoopOrCouldNotFindLabel(NotInLoopOrCouldNotFindLabel { label: _, position, @@ -873,7 +909,7 @@ impl From> for Diagnostic { kind, } }, - TypeCheckError::InvalidRegexp(InvalidRegexp { error, position }) => Diagnostic::Position { + TypeCheckError::InvalidRegExp(InvalidRegExp { error, position }) => Diagnostic::Position { reason: format!("Invalid regular expression: {error}"), position, kind, @@ -919,6 +955,11 @@ pub enum TypeCheckWarning { call_site: SpanWithSource, }, Unreachable(SpanWithSource), + DisjointEquality { + lhs: TypeStringRepresentation, + rhs: TypeStringRepresentation, + position: SpanWithSource, + }, } impl From for Diagnostic { @@ -989,6 +1030,11 @@ impl From for Diagnostic { kind, } } + TypeCheckWarning::DisjointEquality { lhs, rhs, position } => Diagnostic::Position { + reason: format!("This equality is always false as {lhs} and {rhs} have no overlap"), + position, + kind, + }, } } } @@ -1131,7 +1177,7 @@ fn function_calling_error_diagnostic( kind, position, }, - FunctionCallingError::TDZ { error: TDZ { position, variable_name }, call_site } => { + FunctionCallingError::VariableUsedInTDZ { error: VariableUsedInTDZ { position, variable_name }, call_site } => { Diagnostic::PositionWithAdditionalLabels { reason: format!("Variable '{variable_name}' used before declaration{context}"), position: call_site, @@ -1174,7 +1220,7 @@ fn function_calling_error_diagnostic( kind, } } - FunctionCallingError::NotConfiguarable { + FunctionCallingError::NotConfigurable { property, call_site, } => { @@ -1187,7 +1233,7 @@ fn function_calling_error_diagnostic( kind, } } - FunctionCallingError::InvalidRegexp(InvalidRegexp { error, position }) => Diagnostic::Position { + FunctionCallingError::InvalidRegExp(InvalidRegExp { error, position }) => Diagnostic::Position { reason: format!("Invalid regular expression: {error}"), position, kind, diff --git a/checker/src/events/application.rs b/checker/src/events/application.rs index 59a93b3d..d3ddba9f 100644 --- a/checker/src/events/application.rs +++ b/checker/src/events/application.rs @@ -6,7 +6,7 @@ use super::{ use crate::{ context::{get_value_of_variable, invocation::InvocationContext, CallCheckingBehavior}, - diagnostics::{TypeStringRepresentation, TDZ}, + diagnostics::{TypeStringRepresentation, VariableUsedInTDZ}, features::{ iteration::{self, IterationKind}, objects::SpecialObject, @@ -93,8 +93,8 @@ pub(crate) fn apply_events( ty } else { diagnostics.errors.push( - crate::types::calling::FunctionCallingError::TDZ { - error: TDZ { + crate::types::calling::FunctionCallingError::VariableUsedInTDZ { + error: VariableUsedInTDZ { variable_name: top_environment .get_variable_name(*id) .to_owned(), @@ -110,6 +110,7 @@ pub(crate) fn apply_events( input.this_value.get(top_environment, types, *position) } }; + crate::utilities::notify!("Set to {:?}", value); type_arguments.set_during_application(*id, value); } } diff --git a/checker/src/features/constant_functions.rs b/checker/src/features/constant_functions.rs index 6f6f3ac8..630f5a51 100644 --- a/checker/src/features/constant_functions.rs +++ b/checker/src/features/constant_functions.rs @@ -29,7 +29,7 @@ pub enum ConstantFunctionError { FunctionCallingError(FunctionCallingError), NoLogicForIdentifier(String), /// This will get picked up by the main calling logic - BadCall, + CannotComputeConstant, } /// From when @@ -60,11 +60,16 @@ pub(crate) fn call_constant_function( "sin" | "cos" | "tan" | "atan" | "acos" | "asin" | "sinh" | "cosh" | "tanh" | "asinh" | "acosh" | "atanh" | "exp" | "expm1" | "log" | "log10" | "log2" | "log1p" | "round" | "floor" | "ceil" | "trunc" | "sqrt" | "cbrt" | "abs" => { - let second_argument_type = - types.get_type_by_id(arguments.last().ok_or(ConstantFunctionError::BadCall)?.value); + if arguments.len() > 1 { + return Err(ConstantFunctionError::CannotComputeConstant); + } + + let first_argument = types.get_type_by_id( + arguments.last().ok_or(ConstantFunctionError::CannotComputeConstant)?.value, + ); - let Type::Constant(Constant::Number(num)) = second_argument_type else { - return Err(ConstantFunctionError::BadCall); + let Type::Constant(Constant::Number(num)) = first_argument else { + return Err(ConstantFunctionError::CannotComputeConstant); }; let result = match id { @@ -96,13 +101,24 @@ pub(crate) fn call_constant_function( _ => unreachable!(), }; - let try_into = result.try_into(); - match try_into { - Ok(try_into) => { - let ty = types.new_constant_type(Constant::Number(try_into)); - Ok(ConstantOutput::Value(ty)) + let Ok(num) = result.try_into() else { return Ok(ConstantOutput::Value(TypeId::NAN)) }; + Ok(ConstantOutput::Value(types.new_constant_type(Constant::Number(num)))) + } + "imul" => { + if let [x, y] = arguments { + if let (Type::Constant(Constant::Number(x)), Type::Constant(Constant::Number(y))) = + (types.get_type_by_id(x.value), types.get_type_by_id(y.value)) + { + // TODO is this correct, what about overflow? + let result = (x.into_inner() as i32) * (y.into_inner() as i32); + Ok(ConstantOutput::Value( + types.new_constant_type(Constant::Number(result.into())), + )) + } else { + Err(ConstantFunctionError::CannotComputeConstant) } - Err(_) => Ok(ConstantOutput::Value(TypeId::NAN)), + } else { + Err(ConstantFunctionError::CannotComputeConstant) } } // String stuff. TODO could this be replaced by intrinsics @@ -116,14 +132,14 @@ pub(crate) fn call_constant_function( "string_length" => Constant::Number( (s.encode_utf16().count() as f64) .try_into() - .map_err(|_| ConstantFunctionError::BadCall)?, + .map_err(|_| ConstantFunctionError::CannotComputeConstant)?, ), _ => unreachable!(), }); Ok(ConstantOutput::Value(result)) } else { // This can occur!! - Err(ConstantFunctionError::BadCall) + Err(ConstantFunctionError::CannotComputeConstant) } } "print_type" | "debug_type" | "print_and_debug_type" | "debug_type_independent" => { @@ -156,7 +172,9 @@ pub(crate) fn call_constant_function( let mut buf = String::from("Types: "); for (not_at_end, arg) in arguments.iter().nendiate() { // crate::utilities::notify!("at end {:?} {:?}", not_at_end, arg); - let arg = arg.non_spread_type().map_err(|()| ConstantFunctionError::BadCall)?; + let arg = arg + .non_spread_type() + .map_err(|()| ConstantFunctionError::CannotComputeConstant)?; buf.push_str(&to_string(print, debug, arg, types, environment)); if not_at_end { buf.push_str(", "); @@ -168,9 +186,9 @@ pub(crate) fn call_constant_function( "print_constraint" => { let ty = arguments .first() - .ok_or(ConstantFunctionError::BadCall)? + .ok_or(ConstantFunctionError::CannotComputeConstant)? .non_spread_type() - .map_err(|()| ConstantFunctionError::BadCall)?; + .map_err(|()| ConstantFunctionError::CannotComputeConstant)?; let constraint = environment .get_chain_of_info() @@ -186,9 +204,9 @@ pub(crate) fn call_constant_function( "debug_type_rust" | "debug_type_rust_independent" => { let id = arguments .first() - .ok_or(ConstantFunctionError::BadCall)? + .ok_or(ConstantFunctionError::CannotComputeConstant)? .non_spread_type() - .map_err(|()| ConstantFunctionError::BadCall)?; + .map_err(|()| ConstantFunctionError::CannotComputeConstant)?; let ty = types.get_type_by_id(id); Ok(ConstantOutput::Diagnostic(format!("Type is: {id:?} = {ty:?}"))) @@ -196,9 +214,9 @@ pub(crate) fn call_constant_function( "debug_effects" | "debug_effects_rust" => { let ty = arguments .first() - .ok_or(ConstantFunctionError::BadCall)? + .ok_or(ConstantFunctionError::CannotComputeConstant)? .non_spread_type() - .map_err(|()| ConstantFunctionError::BadCall)?; + .map_err(|()| ConstantFunctionError::CannotComputeConstant)?; // Unwrap structure generics let ty = @@ -214,8 +232,10 @@ pub(crate) fn call_constant_function( let message = if let Type::SpecialObject(SpecialObject::Function(func, _)) | Type::FunctionReference(func) = get_type_by_id { - let function_type = - types.functions.get(func).ok_or(ConstantFunctionError::BadCall)?; + let function_type = types + .functions + .get(func) + .ok_or(ConstantFunctionError::CannotComputeConstant)?; let effects = &function_type.effect; if id.ends_with("rust") { @@ -253,15 +273,16 @@ pub(crate) fn call_constant_function( Some(this_ty), ) = (on, first_argument) { - let type_id = - this_ty.non_spread_type().map_err(|()| ConstantFunctionError::BadCall)?; + let type_id = this_ty + .non_spread_type() + .map_err(|()| ConstantFunctionError::CannotComputeConstant)?; let value = types.register_type(Type::SpecialObject(SpecialObject::Function( *func, ThisValue::Passed(type_id), ))); Ok(ConstantOutput::Value(value)) } else { - Err(ConstantFunctionError::BadCall) + Err(ConstantFunctionError::CannotComputeConstant) } } "setPrototypeOf" => { @@ -273,7 +294,7 @@ pub(crate) fn call_constant_function( // TODO Ok(ConstantOutput::Value(TypeId::UNDEFINED_TYPE)) } else { - Err(ConstantFunctionError::BadCall) + Err(ConstantFunctionError::CannotComputeConstant) } } "getPrototypeOf" => { @@ -282,7 +303,7 @@ pub(crate) fn call_constant_function( let prototype = environment.get_prototype(on); Ok(ConstantOutput::Value(prototype)) } else { - Err(ConstantFunctionError::BadCall) + Err(ConstantFunctionError::CannotComputeConstant) } } "freeze" => { @@ -292,7 +313,7 @@ pub(crate) fn call_constant_function( environment.info.frozen.insert(on); Ok(ConstantOutput::Value(on)) } else { - Err(ConstantFunctionError::BadCall) + Err(ConstantFunctionError::CannotComputeConstant) } } "isFrozen" => { @@ -303,17 +324,21 @@ pub(crate) fn call_constant_function( environment.get_chain_of_info().any(|info| info.frozen.contains(&on)); Ok(ConstantOutput::Value(if is_frozen { TypeId::TRUE } else { TypeId::FALSE })) } else { - Err(ConstantFunctionError::BadCall) + Err(ConstantFunctionError::CannotComputeConstant) } } "defineProperty" => { // TODO check configurable if let [on, property, descriptor] = arguments { - let on = on.non_spread_type().map_err(|()| ConstantFunctionError::BadCall)?; - let property = - property.non_spread_type().map_err(|()| ConstantFunctionError::BadCall)?; - let descriptor = - descriptor.non_spread_type().map_err(|()| ConstantFunctionError::BadCall)?; + let on = on + .non_spread_type() + .map_err(|()| ConstantFunctionError::CannotComputeConstant)?; + let property = property + .non_spread_type() + .map_err(|()| ConstantFunctionError::CannotComputeConstant)?; + let descriptor = descriptor + .non_spread_type() + .map_err(|()| ConstantFunctionError::CannotComputeConstant)?; let under = PropertyKey::from_type(property, types); // TODO @@ -357,7 +382,7 @@ pub(crate) fn call_constant_function( } else if let Some(setter) = setter { PropertyValue::Setter(Callable::from_type(setter, types)) } else { - return Err(ConstantFunctionError::BadCall); + return Err(ConstantFunctionError::CannotComputeConstant); } }; @@ -388,7 +413,7 @@ pub(crate) fn call_constant_function( }; if !valid { return Err(ConstantFunctionError::FunctionCallingError( - FunctionCallingError::NotConfiguarable { + FunctionCallingError::NotConfigurable { property: crate::diagnostics::PropertyKeyRepresentation::new( &under, environment, @@ -428,14 +453,17 @@ pub(crate) fn call_constant_function( Ok(ConstantOutput::Value(on)) } else { - Err(ConstantFunctionError::BadCall) + Err(ConstantFunctionError::CannotComputeConstant) } } "getOwnPropertyDescriptor" => { if let [on, property] = arguments { - let on = on.non_spread_type().map_err(|()| ConstantFunctionError::BadCall)?; - let property = - property.non_spread_type().map_err(|()| ConstantFunctionError::BadCall)?; + let on = on + .non_spread_type() + .map_err(|()| ConstantFunctionError::CannotComputeConstant)?; + let property = property + .non_spread_type() + .map_err(|()| ConstantFunctionError::CannotComputeConstant)?; let value = crate::types::properties::resolver( (on, None), @@ -504,7 +532,7 @@ pub(crate) fn call_constant_function( match value { PropertyValue::ConditionallyExists { .. } => { crate::utilities::notify!("TODO conditional. Union with undefined"); - return Err(ConstantFunctionError::BadCall); + return Err(ConstantFunctionError::CannotComputeConstant); } PropertyValue::Configured { on: _, descriptor: d } => { descriptor = d; @@ -540,7 +568,7 @@ pub(crate) fn call_constant_function( None => Ok(ConstantOutput::Value(TypeId::UNDEFINED_TYPE)), } } else { - Err(ConstantFunctionError::BadCall) + Err(ConstantFunctionError::CannotComputeConstant) } } "proxy:constructor" => { @@ -555,7 +583,7 @@ pub(crate) fn call_constant_function( )); Ok(ConstantOutput::Value(value)) } else { - Err(ConstantFunctionError::BadCall) + Err(ConstantFunctionError::CannotComputeConstant) } } // "RegExp:constructor" => { @@ -563,17 +591,17 @@ pub(crate) fn call_constant_function( // if let Some(arg) = arguments.first() { // Ok(ConstantOutput::Value(features::regular_expressions::new_regexp(features::regular_expressions::TypeIdOrString::TypeId(arg), types, environment))) // } else { - // Err(ConstantFunctionError::BadCall) + // Err(ConstantFunctionError::CannotComputeConstant) // } // } // TODO "JSON:parse" => { crate::utilities::notify!("TODO JSON:parse"); - Err(ConstantFunctionError::BadCall) + Err(ConstantFunctionError::CannotComputeConstant) } "JSON:stringify" => { crate::utilities::notify!("TODO JSON:stringify"); - Err(ConstantFunctionError::BadCall) + Err(ConstantFunctionError::CannotComputeConstant) } "regexp:constructor" => { let pattern = types @@ -582,12 +610,12 @@ pub(crate) fn call_constant_function( arguments.get(1).map(|a| types.get_type_by_id(a.non_spread_type().expect("flags"))); let Type::Constant(Constant::String(pattern)) = pattern else { - return Err(ConstantFunctionError::BadCall); + return Err(ConstantFunctionError::CannotComputeConstant); }; let flags = match flags { Some(flags) => { let Type::Constant(Constant::String(flags)) = flags else { - return Err(ConstantFunctionError::BadCall); + return Err(ConstantFunctionError::CannotComputeConstant); }; Some(flags.clone()) @@ -600,7 +628,7 @@ pub(crate) fn call_constant_function( match regexp { Ok(regex) => Ok(ConstantOutput::Value(regex)), Err(error) => Err(ConstantFunctionError::FunctionCallingError( - FunctionCallingError::InvalidRegexp(crate::diagnostics::InvalidRegexp { + FunctionCallingError::InvalidRegExp(crate::diagnostics::InvalidRegExp { error, position: call_site, }), @@ -621,21 +649,21 @@ pub(crate) fn call_constant_function( call_site, ))) } else { - Err(ConstantFunctionError::BadCall) + Err(ConstantFunctionError::CannotComputeConstant) } } // "satisfies" => { // let ty = arguments // .first() - // .ok_or(ConstantFunctionError::BadCall)? + // .ok_or(ConstantFunctionError::CannotComputeConstant)? // .non_spread_type() - // .map_err(|()| ConstantFunctionError::BadCall)?; + // .map_err(|()| ConstantFunctionError::CannotComputeConstant)?; // // TODO temp!!! // let arg = call_site_type_args // .iter() // .flatten() // .next() - // .ok_or(ConstantFunctionError::BadCall)? + // .ok_or(ConstantFunctionError::CannotComputeConstant)? // .0; // if check_satisfies(arg, ty, types, environment) { // Ok(ConstantOutput::Value(ty)) @@ -658,7 +686,7 @@ pub(crate) fn call_constant_function( let mut buf = format!("{:?}", environment.context_id); for ctx in environment.parents_iter().skip(1) { write!(&mut buf, " <- {:?}", get_on_ctx!(ctx.context_id)) - .map_err(|_| ConstantFunctionError::BadCall)?; + .map_err(|_| ConstantFunctionError::CannotComputeConstant)?; } buf })), @@ -668,23 +696,23 @@ pub(crate) fn call_constant_function( .get_type_by_id( arguments .first() - .ok_or(ConstantFunctionError::BadCall)? + .ok_or(ConstantFunctionError::CannotComputeConstant)? .non_spread_type() - .map_err(|()| ConstantFunctionError::BadCall)? + .map_err(|()| ConstantFunctionError::CannotComputeConstant)? ) .is_dependent() ))), // "compile_type_to_object" => { // if let Some(value) = call_site_type_args { // let value = crate::types::others::create_object_for_type( - // value.first().ok_or(ConstantFunctionError::BadCall)?.0, + // value.first().ok_or(ConstantFunctionError::CannotComputeConstant)?.0, // environment, // types, // call_site, // ); // Ok(ConstantOutput::Value(value)) // } else { - // Err(ConstantFunctionError::BadCall) + // Err(ConstantFunctionError::CannotComputeConstant) // } // } func => { diff --git a/checker/src/features/functions.rs b/checker/src/features/functions.rs index d04624e5..27542b95 100644 --- a/checker/src/features/functions.rs +++ b/checker/src/features/functions.rs @@ -455,13 +455,13 @@ where FunctionKind { behavior: FunctionBehavior::Constructor { prototype, - this_object_type: TypeId::ERROR_TYPE, + this_object_type: TypeId::IS_ASSIGNED_VALUE_LATER, name, }, scope: FunctionScope::Constructor { extends: super_type.is_some(), type_of_super: super_type, - this_object_type: TypeId::ERROR_TYPE, + this_object_type: TypeId::IS_ASSIGNED_VALUE_LATER, }, internal: internal_marker, constructor: Some((prototype, properties)), @@ -489,7 +489,7 @@ where FunctionKind { behavior: FunctionBehavior::ArrowFunction { is_async }, scope: FunctionScope::ArrowFunction { - free_this_type: TypeId::ERROR_TYPE, + free_this_type: TypeId::IS_ASSIGNED_VALUE_LATER, is_async, expected_return: expected_return.map(ExpectedReturnType::Inferred), }, @@ -520,7 +520,7 @@ where behavior: FunctionBehavior::Function { is_async, is_generator, - this_id: TypeId::ERROR_TYPE, + this_id: TypeId::IS_ASSIGNED_VALUE_LATER, prototype, name, }, @@ -528,7 +528,7 @@ where is_generator, is_async, // to set - this_type: TypeId::ERROR_TYPE, + this_type: TypeId::IS_ASSIGNED_VALUE_LATER, type_of_super: TypeId::ANY_TYPE, expected_return: expected_return.map(ExpectedReturnType::Inferred), location, @@ -556,14 +556,14 @@ where is_async, is_generator, prototype, - this_id: TypeId::ERROR_TYPE, + this_id: TypeId::IS_ASSIGNED_VALUE_LATER, name, }, scope: FunctionScope::Function { is_generator, is_async, - this_type: TypeId::ERROR_TYPE, - type_of_super: TypeId::ERROR_TYPE, + this_type: TypeId::IS_ASSIGNED_VALUE_LATER, + type_of_super: TypeId::IS_ASSIGNED_VALUE_LATER, expected_return: None, location, }, @@ -618,11 +618,11 @@ where behavior: FunctionBehavior::Method { is_async, is_generator, - free_this_id: TypeId::ERROR_TYPE, + free_this_id: TypeId::IS_ASSIGNED_VALUE_LATER, name, }, scope: FunctionScope::MethodFunction { - free_this_type: TypeId::ERROR_TYPE, + free_this_type: TypeId::IS_ASSIGNED_VALUE_LATER, is_async, is_generator, expected_return: expected_return.map(ExpectedReturnType::Inferred), @@ -846,7 +846,7 @@ where already_checked: Default::default(), mode: Default::default(), contributions: Default::default(), - others: crate::subtyping::SubTypingOptions::satisfies(), + others: crate::subtyping::SubTypingOptions::default(), // TODO don't think there is much case in constraining it here object_constraints: None, }; diff --git a/checker/src/features/iteration.rs b/checker/src/features/iteration.rs index 3f69aee0..d7b2c0cf 100644 --- a/checker/src/features/iteration.rs +++ b/checker/src/features/iteration.rs @@ -72,6 +72,17 @@ pub fn synthesise_iteration( checking_data, ); + let values = super::narrowing::narrow_based_on_expression_into_vec( + condition, + false, + environment, + &mut checking_data.types, + ); + + crate::utilities::notify!("{:?}", values); + + environment.info.narrowed_values = values; + // TODO not always needed add_loop_described_break_event( condition, @@ -279,6 +290,15 @@ pub fn synthesise_iteration( // TODO copy value of variables between things, or however it works + let values = super::narrowing::narrow_based_on_expression_into_vec( + condition, + false, + environment, + &mut checking_data.types, + ); + + environment.info.narrowed_values = values; + (condition, events, dependent_variables) }, ); @@ -886,6 +906,7 @@ fn calculate_result_of_loop( lhs: assignment, operator: _, rhs: increments_by, + result: _, }) = value_after_running_expressions_in_loop { debug_assert!( diff --git a/checker/src/features/mod.rs b/checker/src/features/mod.rs index aac3199b..572559fe 100644 --- a/checker/src/features/mod.rs +++ b/checker/src/features/mod.rs @@ -73,10 +73,6 @@ pub fn type_of_operator(on: TypeId, types: &mut TypeStore) -> TypeId { crate::types::TypeOperator::TypeOf(on), ))) } - } else if on == TypeId::UNDEFINED_TYPE { - return types.new_constant_type(crate::Constant::String("undefined".to_owned())); - } else if on == TypeId::NULL_TYPE { - return types.new_constant_type(crate::Constant::String("object".to_owned())); } else { let ty = types.get_type_by_id(on); if let crate::Type::Constant(cst) = ty { @@ -86,16 +82,18 @@ pub fn type_of_operator(on: TypeId, types: &mut TypeStore) -> TypeId { crate::Constant::String(_) => "string", crate::Constant::Boolean(_) => "boolean", crate::Constant::Symbol { key: _ } => "symbol", + crate::Constant::Undefined => "undefined", }; // TODO could Cow or something to not allocate? types.new_constant_type(crate::Constant::String(name.to_owned())) } else if let crate::Type::SpecialObject(SpecialObject::Function(..)) = ty { types.new_constant_type(crate::Constant::String("function".to_owned())) } else if let crate::Type::Object(..) | crate::Type::SpecialObject(..) = ty { + // includes TypeId::NULL_TYPE types.new_constant_type(crate::Constant::String("object".to_owned())) } else { crate::utilities::notify!("Cannot `typeof {:?}`", on); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } } } @@ -200,7 +198,7 @@ pub fn await_expression( } } else { checking_data.raise_unimplemented_error("await on object", position); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } } @@ -455,20 +453,20 @@ pub(crate) fn has_property( Logical::Pure(_) => TypeId::TRUE, Logical::Or { .. } => { crate::utilities::notify!("or or implies `in`"); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } Logical::Implies { .. } => { crate::utilities::notify!("or or implies `in`"); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } Logical::BasedOnKey { .. } => { crate::utilities::notify!("mapped in"); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } }, Ok(LogicalOrValid::NeedsCalculation(result)) => { crate::utilities::notify!("TODO {:?}", result); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } Err(err) => { crate::utilities::notify!("TODO {:?}", err); @@ -478,7 +476,7 @@ pub(crate) fn has_property( } Type::Or(_, _) => { crate::utilities::notify!("Condtionally"); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } Type::RootPolyType(_) | Type::Constructor(_) => { crate::utilities::notify!("Queue event / create dependent"); @@ -506,7 +504,6 @@ pub mod tsc { Type::RootPolyType(_rpt) => true, Type::Constructor(constr) => match constr { Constructor::CanonicalRelationOperator { .. } - | Constructor::UnaryOperator { .. } | Constructor::BinaryOperator { .. } => false, Constructor::TypeOperator(_) => todo!(), Constructor::TypeExtends(_) => todo!(), @@ -556,12 +553,24 @@ pub mod tsc { environment: &mut Environment, checking_data: &mut CheckingData, ) { - if !crate::types::helpers::simple_subtype( - expr_ty, + use crate::types::subtyping; + + let mut state = subtyping::State { + already_checked: Default::default(), + mode: Default::default(), + contributions: Default::default(), + others: subtyping::SubTypingOptions { allow_errors: false }, + object_constraints: None, + }; + + let result = subtyping::type_is_subtype( to_satisfy, + expr_ty, + &mut state, environment, &checking_data.types, - ) { + ); + if result.is_mismatch() { let expected = diagnostics::TypeStringRepresentation::from_type_id( to_satisfy, environment, diff --git a/checker/src/features/modules.rs b/checker/src/features/modules.rs index 33a56dd7..3f876dcb 100644 --- a/checker/src/features/modules.rs +++ b/checker/src/features/modules.rs @@ -324,7 +324,7 @@ pub fn import_items< checking_data.types.register_type(import_object) } else { crate::utilities::notify!("TODO :?"); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE }; environment.register_variable_handle_error( under, diff --git a/checker/src/features/narrowing.rs b/checker/src/features/narrowing.rs index 312c4291..1720ff17 100644 --- a/checker/src/features/narrowing.rs +++ b/checker/src/features/narrowing.rs @@ -1,13 +1,14 @@ use crate::{ context::InformationChain, types::{ - self, as_logical_and, as_logical_or, get_conditional, helpers::get_origin, properties, - Constant, Constructor, PolyNature, TypeOperator, TypeStore, + self, as_logical_and, as_logical_not, as_logical_or, + helpers::{get_conditional, get_origin}, + properties, Constant, Constructor, PolyNature, TypeOperator, TypeStore, }, Map, Type, TypeId, }; -use super::operations::{CanonicalEqualityAndInequality, MathematicalAndBitwise, PureUnary}; +use super::operations::{CanonicalEqualityAndInequality, MathematicalAndBitwise}; pub fn narrow_based_on_expression_into_vec( condition: TypeId, @@ -68,6 +69,7 @@ pub fn narrow_based_on_expression( lhs: operand, operator: MathematicalAndBitwise::Modulo, rhs: modulo, + result: _, }) = types.get_type_by_id(*lhs) { if *rhs == TypeId::ZERO { @@ -77,9 +79,10 @@ pub fn narrow_based_on_expression( if negate { crate::utilities::notify!("TODO do we not divisable by?"); } else { - let narrowed_to = types.new_intrinsic( + let narrowed_to = crate::types::intrinsics::new_intrinsic( &crate::types::intrinsics::Intrinsic::MultipleOf, modulo, + types, ); let narrowed = types.new_narrowed(from, narrowed_to); into.insert(from, narrowed); @@ -98,6 +101,11 @@ pub fn narrow_based_on_expression( ); } + if negate && lhs == rhs { + into.insert(*lhs, types.new_narrowed(*lhs, TypeId::NOT_NOT_A_NUMBER)); + return; + } + let lhs = get_origin(*lhs, types); let result = if negate { @@ -116,7 +124,11 @@ pub fn narrow_based_on_expression( let narrowed_to = types.new_or_type_from_iterator(result); types.new_narrowed(lhs, narrowed_to) } else { - types.new_intrinsic(&crate::types::intrinsics::Intrinsic::Not, *rhs) + crate::types::intrinsics::new_intrinsic( + &crate::types::intrinsics::Intrinsic::Not, + *rhs, + types, + ) }; types.new_narrowed(lhs, narrowed_to) } else { @@ -172,20 +184,23 @@ pub fn narrow_based_on_expression( return; } if types.get_type_by_id(lhs).is_dependent() { - let narrowed_to = - types.new_intrinsic(&crate::types::intrinsics::Intrinsic::LessThan, rhs); + let narrowed_to = crate::types::intrinsics::new_intrinsic( + &crate::types::intrinsics::Intrinsic::LessThan, + rhs, + types, + ); let narrowed = types.new_narrowed(lhs, narrowed_to); into.insert(lhs, narrowed); } else if types.get_type_by_id(rhs).is_dependent() { - let narrowed_to = - types.new_intrinsic(&crate::types::intrinsics::Intrinsic::GreaterThan, lhs); + let narrowed_to = crate::types::intrinsics::new_intrinsic( + &crate::types::intrinsics::Intrinsic::GreaterThan, + lhs, + types, + ); let narrowed = types.new_narrowed(rhs, narrowed_to); into.insert(rhs, narrowed); } } - Constructor::UnaryOperator { operator: PureUnary::LogicalNot, operand } => { - narrow_based_on_expression(*operand, !negate, into, information, types); - } Constructor::TypeOperator(TypeOperator::IsPrototype { lhs, rhs_prototype }) => { let (lhs, rhs_prototype) = (*lhs, *rhs_prototype); let constraint = crate::types::get_constraint(lhs, types).unwrap_or(lhs); @@ -241,7 +256,10 @@ pub fn narrow_based_on_expression( into.insert(on, types.new_narrowed(on, narrowed_to)); } constructor => { - if let Some((lhs, rhs)) = as_logical_and(constructor, types) { + if let Some(condition) = as_logical_not(constructor, types) { + crate::utilities::notify!("Here"); + narrow_based_on_expression(condition, !negate, into, information, types); + } else if let Some((lhs, rhs)) = as_logical_and(constructor, types) { // De Morgan's laws if negate { // OR: Pull assertions from left and right, merge if both branches assert something @@ -310,8 +328,20 @@ pub fn narrow_based_on_expression( if rpt.get_constraint() == TypeId::BOOLEAN_TYPE { let result = if negate { TypeId::FALSE } else { TypeId::TRUE }; into.insert(condition, result); - } else { - crate::utilities::notify!("Set, {:?} as truthy", r#type); + } else if !negate { + let mut result = Vec::new(); + super::narrowing::build_union_from_filter( + condition, + super::narrowing::NOT_FASLY, + &mut result, + information, + types, + ); + let narrowed_to = types.new_or_type_from_iterator(result); + into.insert( + condition, + types.register_type(Type::Narrowed { from: condition, narrowed_to }), + ); } } } diff --git a/checker/src/features/operations.rs b/checker/src/features/operations.rs index f3443a66..78a88f83 100644 --- a/checker/src/features/operations.rs +++ b/checker/src/features/operations.rs @@ -7,8 +7,8 @@ use crate::{ diagnostics::{TypeCheckError, TypeStringRepresentation}, features::conditional::new_conditional_context, types::{ - cast_as_number, cast_as_string, is_type_truthy_falsy, Constructor, - PartiallyAppliedGenerics, TypeStore, + cast_as_number, cast_as_string, helpers::simple_subtype, intrinsics, is_type_truthy_falsy, + Constructor, PartiallyAppliedGenerics, TypeStore, }, CheckingData, Constant, Decidable, Environment, Type, TypeId, }; @@ -61,9 +61,11 @@ pub fn evaluate_pure_binary_operation_handle_errors< lhs, operator, rhs, + environment, &mut checking_data.types, checking_data.options.strict_casts, ); + match result { Ok(result) => result, Err(_err) => { @@ -96,22 +98,79 @@ pub fn evaluate_pure_binary_operation_handle_errors< } PureBinaryOperation::EqualityAndInequality(operator) => { // Cannot error, but can be always true or false - evaluate_equality_inequality_operation( + let result = evaluate_equality_inequality_operation( lhs, &operator, rhs, + environment, &mut checking_data.types, checking_data.options.strict_casts, - ) + ); + + if let Ok((result, warning)) = result { + if let EqualityAndInequalityResultKind::Disjoint = warning { + let position = lhs_pos + .without_source() + .union(rhs_pos.without_source()) + .with_source(environment.get_source()); + + checking_data.diagnostics_container.add_warning( + crate::TypeCheckWarning::DisjointEquality { + lhs: TypeStringRepresentation::from_type_id( + lhs, + environment, + &checking_data.types, + false, + ), + rhs: TypeStringRepresentation::from_type_id( + rhs, + environment, + &checking_data.types, + false, + ), + position, + }, + ); + } + + result + } else { + let position = lhs_pos + .without_source() + .union(rhs_pos.without_source()) + .with_source(environment.get_source()); + + checking_data.diagnostics_container.add_error( + crate::TypeCheckError::InvalidEqualityOperation { + operator, + lhs: TypeStringRepresentation::from_type_id( + lhs, + environment, + &checking_data.types, + false, + ), + rhs: TypeStringRepresentation::from_type_id( + rhs, + environment, + &checking_data.types, + false, + ), + position, + }, + ); + + TypeId::ERROR_TYPE + } } } } -/// TODO proper err +/// TODO proper error type pub fn evaluate_mathematical_operation( lhs: TypeId, operator: MathematicalAndBitwise, rhs: TypeId, + info: &impl crate::context::InformationChain, types: &mut TypeStore, strict_casts: bool, ) -> Result { @@ -187,9 +246,63 @@ pub fn evaluate_mathematical_operation( let is_dependent = types.get_type_by_id(lhs).is_dependent() || types.get_type_by_id(rhs).is_dependent(); - // TODO check sides if is_dependent { - let constructor = crate::types::Constructor::BinaryOperator { lhs, operator, rhs }; + let can_be_string = if let MathematicalAndBitwise::Add = operator { + let left_is_string = simple_subtype(lhs, TypeId::STRING_TYPE, info, types); + let right_is_string = simple_subtype(lhs, TypeId::STRING_TYPE, info, types); + let left_is_string_or_number = + left_is_string || simple_subtype(lhs, TypeId::NUMBER_TYPE, info, types); + let right_is_string_or_number = + right_is_string || simple_subtype(rhs, TypeId::NUMBER_TYPE, info, types); + if !left_is_string_or_number || !right_is_string_or_number { + return Err(()); + } + left_is_string || right_is_string + } else { + let left_is_number = simple_subtype(lhs, TypeId::NUMBER_TYPE, info, types); + if !left_is_number || !simple_subtype(rhs, TypeId::NUMBER_TYPE, info, types) { + return Err(()); + } + false + }; + + // :) + if let (MathematicalAndBitwise::Exponent, TypeId::ONE, true) = + (operator, rhs, intrinsics::is_not_not_a_number(lhs, types)) + { + return Ok(lhs); + } else if let (MathematicalAndBitwise::Add, TypeId::ZERO) + | (MathematicalAndBitwise::Multiply, TypeId::ONE) = (operator, rhs) + { + return Ok(lhs); + } else if let (MathematicalAndBitwise::Add, TypeId::ZERO) + | (MathematicalAndBitwise::Multiply, TypeId::ONE) = (operator, lhs) + { + return Ok(rhs); + } + + let result = if can_be_string { + TypeId::STRING_TYPE + } else if let ( + MathematicalAndBitwise::Add | MathematicalAndBitwise::Multiply, + Some(lhs_range), + Some(rhs_range), + ) = (operator, intrinsics::get_range(lhs, types), intrinsics::get_range(rhs, types)) + { + match operator { + MathematicalAndBitwise::Add => { + intrinsics::range_to_type(lhs_range.space_addition(rhs_range), types) + } + MathematicalAndBitwise::Multiply => { + intrinsics::range_to_type(lhs_range.space_multiplication(rhs_range), types) + } + _ => unreachable!(), + } + } else { + TypeId::NUMBER_TYPE + }; + + let constructor = crate::types::Constructor::BinaryOperator { lhs, operator, rhs, result }; Ok(types.register_type(crate::Type::Constructor(constructor))) } else { attempt_constant_math_operator(lhs, operator, rhs, types, strict_casts) @@ -217,16 +330,23 @@ pub enum CanonicalEqualityAndInequality { LessThan, } +pub enum EqualityAndInequalityResultKind { + Constant, + Disjoint, + Condition, +} + pub fn evaluate_equality_inequality_operation( mut lhs: TypeId, operator: &EqualityAndInequality, mut rhs: TypeId, + info: &impl crate::context::InformationChain, types: &mut TypeStore, strict_casts: bool, -) -> TypeId { +) -> Result<(TypeId, EqualityAndInequalityResultKind), ()> { // `NaN == t` is always true if lhs == TypeId::NAN || rhs == TypeId::NAN { - return TypeId::FALSE; + return Ok((TypeId::FALSE, EqualityAndInequalityResultKind::Constant)); } match operator { @@ -236,8 +356,26 @@ pub fn evaluate_equality_inequality_operation( let left_dependent = types.get_type_by_id(lhs).is_dependent(); let is_dependent = left_dependent || types.get_type_by_id(rhs).is_dependent(); - // TODO check lhs and rhs type to see if they overlap if is_dependent { + if lhs == rhs + && intrinsics::is_not_not_a_number(lhs, types) + && intrinsics::is_not_not_a_number(rhs, types) + { + // I think this is okay + return Ok((TypeId::TRUE, EqualityAndInequalityResultKind::Constant)); + } + + // Checks lhs and rhs type to see if they overlap + if crate::types::disjoint::types_are_disjoint( + lhs, + rhs, + &mut Vec::new(), + info, + types, + ) { + return Ok((TypeId::FALSE, EqualityAndInequalityResultKind::Disjoint)); + } + // Sort if `*constant* == ...`. Ideally want constant type on the RHS let (lhs, rhs) = if left_dependent { (lhs, rhs) } else { (rhs, rhs) }; let constructor = crate::types::Constructor::CanonicalRelationOperator { @@ -246,10 +384,16 @@ pub fn evaluate_equality_inequality_operation( rhs, }; - types.register_type(crate::Type::Constructor(constructor)) + Ok(( + types.register_type(crate::Type::Constructor(constructor)), + EqualityAndInequalityResultKind::Condition, + )) } else { match attempt_constant_equality(lhs, rhs, types) { - Ok(ty) => ty, + Ok(ty) => Ok(( + if ty { TypeId::TRUE } else { TypeId::FALSE }, + EqualityAndInequalityResultKind::Constant, + )), Err(()) => { unreachable!( "should have been caught `is_dependent` above, {:?} === {:?}", @@ -266,22 +410,25 @@ pub fn evaluate_equality_inequality_operation( rhs: TypeId, types: &mut TypeStore, strict_casts: bool, - ) -> Result { + ) -> Result { // Similar but reversed semantics to add match (types.get_type_by_id(lhs), types.get_type_by_id(rhs)) { - (Type::Constant(Constant::String(a)), Type::Constant(Constant::String(b))) => { + ( + Type::Constant(Constant::String(string1)), + Type::Constant(Constant::String(string2)), + ) => { // Yah rust includes string alphanumerical equivalence of strings - Ok(types.new_constant_type(Constant::Boolean(a < b))) + Ok(string1 < string2) } (Type::Constant(c1), Type::Constant(c2)) => { let lhs = cast_as_number(c1, strict_casts)?; let rhs = cast_as_number(c2, strict_casts)?; - Ok(types.new_constant_type(Constant::Boolean(lhs < rhs))) + Ok(lhs < rhs) } (lhs, rhs) => { crate::utilities::notify!("{:?}", (lhs, rhs)); - Ok(TypeId::OPEN_BOOLEAN_TYPE) - // Err(()) + // Ok(TypeId::OPEN_BOOLEAN_TYPE) + Err(()) } } } @@ -290,12 +437,12 @@ pub fn evaluate_equality_inequality_operation( || types.get_type_by_id(rhs).is_dependent(); if is_dependent { - // Tidies some things for counting loop iterations { if let Type::Constructor(Constructor::BinaryOperator { lhs: op_lhs, operator, rhs: op_rhs, + result: _, }) = types.get_type_by_id(lhs) { if let ( @@ -311,95 +458,137 @@ pub fn evaluate_equality_inequality_operation( } } + { + // let lhs = get_constraint(lhs, types).unwrap_or(lhs); + // let rhs = get_constraint(rhs, types).unwrap_or(rhs); + + if !simple_subtype(lhs, TypeId::NUMBER_TYPE, info, types) + || !simple_subtype(rhs, TypeId::NUMBER_TYPE, info, types) + { + return Err(()); + } + + // Tidies some things for counting loop iterations + + // Checking disjoint-ness for inequalities (TODO under option) via distribution + if let (Some(lhs_range), Some(rhs_range)) = + (intrinsics::get_range(lhs, types), intrinsics::get_range(rhs, types)) + { + if lhs_range.below(rhs_range) { + return Ok((TypeId::TRUE, EqualityAndInequalityResultKind::Constant)); + } + if lhs_range.above(rhs_range) { + return Ok((TypeId::FALSE, EqualityAndInequalityResultKind::Disjoint)); + } + } + } + let constructor = Constructor::CanonicalRelationOperator { lhs, operator: CanonicalEqualityAndInequality::LessThan, rhs, }; - types.register_type(crate::Type::Constructor(constructor)) + Ok(( + types.register_type(crate::Type::Constructor(constructor)), + EqualityAndInequalityResultKind::Condition, + )) } else { - let less_than_result = attempt_less_than(lhs, rhs, types, strict_casts); - if let Ok(result) = less_than_result { - result - } else { - crate::utilities::notify!( - "Less than unreachable {:?}", - (types.get_type_by_id(lhs), types.get_type_by_id(rhs)) - ); - TypeId::ERROR_TYPE - } + attempt_less_than(lhs, rhs, types, strict_casts).map(|value| { + ( + if value { TypeId::TRUE } else { TypeId::FALSE }, + EqualityAndInequalityResultKind::Constant, + ) + }) } } // equal OR less than EqualityAndInequality::LessThanOrEqual => { - let equality_result = evaluate_equality_inequality_operation( + let (equality_result, warning) = evaluate_equality_inequality_operation( lhs, &EqualityAndInequality::StrictEqual, rhs, + info, types, strict_casts, - ); + )?; if equality_result == TypeId::TRUE { - equality_result + Ok((equality_result, warning)) } else if equality_result == TypeId::FALSE { evaluate_equality_inequality_operation( lhs, &EqualityAndInequality::LessThan, rhs, + info, types, strict_casts, ) } else { - let less_than_result = evaluate_equality_inequality_operation( + let (less_than_result, warning) = evaluate_equality_inequality_operation( lhs, &EqualityAndInequality::LessThan, rhs, + info, types, strict_casts, - ); - types.new_logical_or_type(equality_result, less_than_result) + )?; + Ok((types.new_logical_or_type(equality_result, less_than_result), warning)) } } EqualityAndInequality::StrictNotEqual => { - let equality_result = evaluate_equality_inequality_operation( + let (equality_result, kind) = evaluate_equality_inequality_operation( lhs, &EqualityAndInequality::StrictEqual, rhs, + info, types, strict_casts, - ); - evaluate_pure_unary_operator( - PureUnary::LogicalNot, - equality_result, - types, - strict_casts, - ) + )?; + if let EqualityAndInequalityResultKind::Condition = kind { + Ok((types.new_logical_negation_type(equality_result), kind)) + } else { + let negated = if let TypeId::TRUE = equality_result { + TypeId::FALSE + } else if let TypeId::FALSE = equality_result { + TypeId::TRUE + } else { + todo!() + }; + Ok((negated, kind)) + } } EqualityAndInequality::Equal => { crate::utilities::notify!("TODO equal operator"); - TypeId::OPEN_BOOLEAN_TYPE + Ok((TypeId::OPEN_BOOLEAN_TYPE, EqualityAndInequalityResultKind::Condition)) } EqualityAndInequality::NotEqual => { - let equality_result = evaluate_equality_inequality_operation( + let (equality_result, kind) = evaluate_equality_inequality_operation( lhs, &EqualityAndInequality::Equal, rhs, + info, types, strict_casts, - ); - evaluate_pure_unary_operator( - PureUnary::LogicalNot, - equality_result, - types, - strict_casts, - ) + )?; + if let EqualityAndInequalityResultKind::Condition = kind { + Ok((types.new_logical_negation_type(equality_result), kind)) + } else { + let negated = if let TypeId::TRUE = equality_result { + TypeId::FALSE + } else if let TypeId::FALSE = equality_result { + TypeId::TRUE + } else { + todo!() + }; + Ok((negated, kind)) + } } // Swapping operands! EqualityAndInequality::GreaterThan => evaluate_equality_inequality_operation( rhs, &EqualityAndInequality::LessThan, lhs, + info, types, strict_casts, ), @@ -408,6 +597,7 @@ pub fn evaluate_equality_inequality_operation( rhs, &EqualityAndInequality::LessThanOrEqual, lhs, + info, types, strict_casts, ), @@ -415,25 +605,40 @@ pub fn evaluate_equality_inequality_operation( } #[allow(clippy::let_and_return)] -pub fn is_null_or_undefined(ty: TypeId, types: &mut TypeStore) -> TypeId { +pub fn is_null_or_undefined( + ty: TypeId, + info: &impl crate::context::InformationChain, + types: &mut TypeStore, +) -> TypeId { let is_null = evaluate_equality_inequality_operation( ty, &EqualityAndInequality::StrictEqual, TypeId::NULL_TYPE, + info, types, false, - ); - // TODO temp to fix narrowing - // let is_undefined = evaluate_equality_inequality_operation( - // ty, - // &EqualityAndInequality::StrictEqual, - // TypeId::UNDEFINED_TYPE, - // types, - // false, - // ); - - // types.new_logical_or_type(is_null, is_undefined) - is_null + ) + .map_or(TypeId::ERROR_TYPE, |(left, _)| left); + + if let TypeId::TRUE = is_null { + is_null + } else { + let is_undefined = evaluate_equality_inequality_operation( + ty, + &EqualityAndInequality::StrictEqual, + TypeId::UNDEFINED_TYPE, + info, + types, + false, + ) + .map_or(TypeId::ERROR_TYPE, |(left, _)| left); + + if let TypeId::FALSE = is_null { + is_undefined + } else { + types.new_logical_or_type(is_null, is_undefined) + } + } } #[derive(Copy, Clone, Debug)] @@ -493,7 +698,8 @@ pub fn evaluate_logical_operation_with_expression< checking_data, )), LogicalOperator::NullCoalescing => { - let is_lhs_null_or_undefined = is_null_or_undefined(lhs.0, &mut checking_data.types); + let is_lhs_null_or_undefined = + is_null_or_undefined(lhs.0, environment, &mut checking_data.types); // Equivalent to: `(lhs is null or undefined) ? lhs : rhs` Ok(new_conditional_context( environment, @@ -529,52 +735,77 @@ pub fn evaluate_logical_operation_with_expression< /// `typeof` and some others done elsewhere #[derive(Clone, Copy, Debug, binary_serialize_derive::BinarySerializable)] -pub enum PureUnary { +pub enum UnaryOperation { + /// Treated as `(value ? false : true)` LogicalNot, + /// Treated as `0 - value` (could also do -1 * value?) Negation, + /// Treated as `value ^ 0xFFFF_FFFF` BitwiseNot, } -pub fn evaluate_pure_unary_operator( - operator: PureUnary, +/// Tries to evaluate unary operation for constant terms. Else delegates to binary operations that handle equivalent thing +pub fn evaluate_unary_operator( + operator: UnaryOperation, operand: TypeId, + info: &impl crate::context::InformationChain, types: &mut TypeStore, strict_casts: bool, -) -> TypeId { +) -> Result { if operand == TypeId::ERROR_TYPE { - return operand; + return Ok(operand); } match operator { - PureUnary::LogicalNot => { + UnaryOperation::LogicalNot => { if let Decidable::Known(value) = is_type_truthy_falsy(operand, types) { if value { - TypeId::FALSE + Ok(TypeId::FALSE) } else { - TypeId::TRUE + Ok(TypeId::TRUE) } } else { - types.new_logical_negation_type(operand) + let is_boolean = simple_subtype(operand, TypeId::BOOLEAN_TYPE, info, types); + if is_boolean { + Ok(types.new_logical_negation_type(operand)) + } else { + Err(()) + } } } - PureUnary::Negation | PureUnary::BitwiseNot => { + UnaryOperation::Negation | UnaryOperation::BitwiseNot => { if let Type::Constant(cst) = types.get_type_by_id(operand) { let value = cast_as_number(cst, strict_casts).expect("hmm"); let value = match operator { - PureUnary::LogicalNot => unreachable!(), - PureUnary::Negation => -value, - PureUnary::BitwiseNot => f64::from(!(value as i32)), + UnaryOperation::BitwiseNot => f64::from(!(value as i32)), + UnaryOperation::Negation => -value, + UnaryOperation::LogicalNot => unreachable!(), }; let value = ordered_float::NotNan::try_from(value); - match value { + Ok(match value { Ok(value) => types.new_constant_type(Constant::Number(value)), Err(_) => TypeId::NAN, - } + }) } else { - types.register_type(Type::Constructor(crate::types::Constructor::UnaryOperator { - operator, - operand, - })) + match operator { + UnaryOperation::BitwiseNot => evaluate_mathematical_operation( + TypeId::MAX_U32, + MathematicalAndBitwise::BitwiseXOr, + operand, + info, + types, + strict_casts, + ), + UnaryOperation::Negation => evaluate_mathematical_operation( + TypeId::ZERO, + MathematicalAndBitwise::Subtract, + operand, + info, + types, + strict_casts, + ), + UnaryOperation::LogicalNot => unreachable!("handled above"), + } } } } @@ -583,28 +814,24 @@ pub fn evaluate_pure_unary_operator( /// Returns whether lhs and rhs are always equal or never equal. TODO more /// /// TODO return decidable. -fn attempt_constant_equality( - lhs: TypeId, - rhs: TypeId, - types: &mut TypeStore, -) -> Result { - let are_equal = if lhs == rhs { - true +fn attempt_constant_equality(lhs: TypeId, rhs: TypeId, types: &mut TypeStore) -> Result { + if lhs == rhs { + Ok(true) } else if matches!(lhs, TypeId::NULL_TYPE | TypeId::UNDEFINED_TYPE) || matches!(rhs, TypeId::NULL_TYPE | TypeId::UNDEFINED_TYPE) { // If above `==`` failed => false (as always have same `TypeId`) - false + Ok(false) } else { let lhs = types.get_type_by_id(lhs); let rhs = types.get_type_by_id(rhs); if let (Type::Constant(cst1), Type::Constant(cst2)) = (lhs, rhs) { - cst1 == cst2 + Ok(cst1 == cst2) } else if let (Type::Object(..) | Type::SpecialObject(SpecialObject::Function(..)), _) | (_, Type::Object(..) | Type::SpecialObject(SpecialObject::Function(..))) = (lhs, rhs) { // Same objects and functions always have same type id. Poly case doesn't occur here - false + Ok(false) } // Temp fix for closures else if let ( @@ -613,12 +840,10 @@ fn attempt_constant_equality( ) = (lhs, rhs) { // TODO does this work? - return attempt_constant_equality(*on_lhs, *on_rhs, types); + attempt_constant_equality(*on_lhs, *on_rhs, types) } else { crate::utilities::notify!("{:?} === {:?} is apparently false", lhs, rhs); - return Err(()); + Err(()) } - }; - - Ok(types.new_constant_type(Constant::Boolean(are_equal))) + } } diff --git a/checker/src/features/template_literal.rs b/checker/src/features/template_literal.rs index d606e45d..747877b2 100644 --- a/checker/src/features/template_literal.rs +++ b/checker/src/features/template_literal.rs @@ -8,7 +8,7 @@ use crate::{ application_result_to_return_type, Callable, CallingContext, CallingInput, SynthesisedArgument, }, - cast_as_string, TypeStore, + cast_as_string, }, CheckingData, Constant, Environment, Type, TypeId, }; @@ -146,6 +146,7 @@ where acc, crate::features::operations::MathematicalAndBitwise::Add, lhs, + environment, &mut checking_data.types, checking_data.options.strict_casts, ); @@ -153,7 +154,7 @@ where acc = result; } else { crate::utilities::notify!("Invalid template literal concatenation"); - return TypeId::ERROR_TYPE; + return TypeId::UNIMPLEMENTED_ERROR_TYPE; } let rhs = A::synthesise_multiple_expression( dynamic_part, @@ -165,6 +166,7 @@ where acc, crate::features::operations::MathematicalAndBitwise::Add, rhs, + environment, &mut checking_data.types, checking_data.options.strict_casts, ); @@ -172,7 +174,7 @@ where acc = result; } else { crate::utilities::notify!("Invalid template literal concatenation"); - return TypeId::ERROR_TYPE; + return TypeId::UNIMPLEMENTED_ERROR_TYPE; } } if final_part.is_empty() { @@ -184,6 +186,7 @@ where acc, crate::features::operations::MathematicalAndBitwise::Add, value, + environment, &mut checking_data.types, checking_data.options.strict_casts, ); @@ -191,47 +194,8 @@ where result } else { crate::utilities::notify!("Invalid template literal concatenation"); - TypeId::ERROR_TYPE - } - } - } -} - -/// **Expects static part first** -/// -/// TODO API is different to the `synthesise_template_literal_expression` above -pub fn synthesize_template_literal_type(parts: Vec, types: &mut TypeStore) -> TypeId { - let mut parts_iter = parts.into_iter(); - if let Some(first) = parts_iter.next() { - let mut acc = first; - for other in parts_iter { - // TODO unfold_alias function - let other = if let Type::AliasTo { to, .. } = types.get_type_by_id(other) { - *to - } else { - other - }; - let result = super::operations::evaluate_mathematical_operation( - acc, - crate::features::operations::MathematicalAndBitwise::Add, - other, - types, - true, - ); - match result { - Ok(result) => acc = result, - Err(()) => { - // crate::utilities::notify!( - // "acc is {:?}, other is {:?}", - // types.get_type_by_id(acc), - // types.get_type_by_id(other) - // ); - crate::utilities::notify!("Invalid type template literal concatenation"); - } + TypeId::UNIMPLEMENTED_ERROR_TYPE } } - acc - } else { - types.new_constant_type(Constant::String(String::new())) } } diff --git a/checker/src/features/variables.rs b/checker/src/features/variables.rs index a8ea9068..48c50501 100644 --- a/checker/src/features/variables.rs +++ b/checker/src/features/variables.rs @@ -157,7 +157,7 @@ pub fn get_new_register_argument_under todo!(), } } else { - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } } else { let keys; diff --git a/checker/src/synthesis/classes.rs b/checker/src/synthesis/classes.rs index b73ed230..5495dc09 100644 --- a/checker/src/synthesis/classes.rs +++ b/checker/src/synthesis/classes.rs @@ -191,6 +191,8 @@ fn synthesise_class_declaration_extends_and_members< } }; + crate::utilities::notify!("{:?}", (getter_setter, is_async, is_generator)); + let internal_marker = if let (true, ParserPropertyKey::Identifier(name, _, _)) = (is_declare, method.name.get_ast_ref()) { @@ -360,6 +362,10 @@ fn synthesise_class_declaration_extends_and_members< // Adds event environment.register_constructable_function(class_variable_type, function_id); + if let Some(variable) = class.name.get_variable_id(environment.get_source()) { + environment.info.variable_current_value.insert(variable, class_variable_type); + } + crate::utilities::notify!("At end {:?}", environment.context_type.free_variables); { @@ -451,7 +457,7 @@ fn synthesise_class_declaration_extends_and_members< synthesise_type_annotation(type_annotation, environment, checking_data) } else { crate::utilities::notify!("Declare without type annotation"); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } } else { TypeId::UNDEFINED_TYPE @@ -480,10 +486,6 @@ fn synthesise_class_declaration_extends_and_members< } } - if let Some(variable) = class.name.get_variable_id(environment.get_source()) { - environment.info.variable_current_value.insert(variable, class_variable_type); - } - class_variable_type } @@ -609,12 +611,20 @@ fn register_extends_and_member( environment, ); + let (getter_setter, is_async, is_generator) = match &method.header { + MethodHeader::Get => (Some(GetterSetter::Getter), false, false), + MethodHeader::Set => (Some(GetterSetter::Setter), false, false), + MethodHeader::Regular { is_async, generator } => { + (None, *is_async, generator.is_some()) + } + }; + let value = build_overloaded_function( FunctionId(environment.get_source(), method.position.start), crate::types::functions::FunctionBehavior::Method { free_this_id: TypeId::ANY_TYPE, - is_async: method.header.is_async(), - is_generator: method.header.is_generator(), + is_async, + is_generator, // TODO name: TypeId::ANY_TYPE, }, @@ -640,12 +650,17 @@ fn register_extends_and_member( if *initial_is_static { crate::utilities::notify!("TODO static item?"); } else { - environment.info.register_property_on_type( - class_type, - publicity, - under, - PropertyValue::Value(value), - ); + use crate::types::calling::Callable; + let value = match getter_setter { + Some(GetterSetter::Getter) => { + PropertyValue::Getter(Callable::from_type(value, &checking_data.types)) + } + Some(GetterSetter::Setter) => { + PropertyValue::Setter(Callable::from_type(value, &checking_data.types)) + } + None => PropertyValue::Value(value), + }; + environment.info.register_property_on_type(class_type, publicity, under, value); } } ClassMember::Property(is_static, property) => { diff --git a/checker/src/synthesis/expressions.rs b/checker/src/synthesis/expressions.rs index 619017f2..837f99c5 100644 --- a/checker/src/synthesis/expressions.rs +++ b/checker/src/synthesis/expressions.rs @@ -33,8 +33,8 @@ use crate::{ operations::is_null_or_undefined, operations::{ evaluate_logical_operation_with_expression, - evaluate_pure_binary_operation_handle_errors, evaluate_pure_unary_operator, - EqualityAndInequality, MathematicalAndBitwise, PureUnary, + evaluate_pure_binary_operation_handle_errors, evaluate_unary_operator, + EqualityAndInequality, MathematicalAndBitwise, UnaryOperation, }, template_literal::synthesise_template_literal_expression, variables::VariableWithValue, @@ -100,8 +100,8 @@ pub(super) fn synthesise_expression( Ok(regexp) => Instance::RValue(regexp), Err(error) => { checking_data.diagnostics_container.add_error( - crate::diagnostics::TypeCheckError::InvalidRegexp( - crate::diagnostics::InvalidRegexp { + crate::diagnostics::TypeCheckError::InvalidRegExp( + crate::diagnostics::InvalidRegExp { error, position: position.with_source(environment.get_source()), }, @@ -117,7 +117,7 @@ pub(super) fn synthesise_expression( v.try_into().unwrap() } else { crate::utilities::notify!("TODO big int"); - return TypeId::ERROR_TYPE; + return TypeId::UNIMPLEMENTED_ERROR_TYPE; }; // if not_nan == 6. { // crate::utilities::notify!("{:?}", environment.get_all_named_types()); @@ -170,7 +170,7 @@ pub(super) fn synthesise_expression( PropertyKey::Type(TypeId::NUMBER_TYPE) } }; - Some((property, TypeId::ERROR_TYPE)) + Some((property, TypeId::UNIMPLEMENTED_ERROR_TYPE)) } FunctionArgument::Comment { .. } => None, }) @@ -313,7 +313,7 @@ pub(super) fn synthesise_expression( "special operations", position.with_source(environment.get_source()), ); - return TypeId::ERROR_TYPE; + return TypeId::UNIMPLEMENTED_ERROR_TYPE; } }; Instance::RValue(evaluate_pure_binary_operation_handle_errors( @@ -343,28 +343,47 @@ pub(super) fn synthesise_expression( "Unary plus operator", position.with_source(environment.get_source()), ); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE }; } UnaryOperator::Negation | UnaryOperator::BitwiseNot | UnaryOperator::LogicalNot => { - let operand_type = synthesise_expression( + let operand = synthesise_expression( operand, environment, checking_data, TypeId::ANY_TYPE, ); let operator = match operator { - UnaryOperator::Negation => PureUnary::Negation, - UnaryOperator::BitwiseNot => PureUnary::BitwiseNot, - UnaryOperator::LogicalNot => PureUnary::LogicalNot, + UnaryOperator::Negation => UnaryOperation::Negation, + UnaryOperator::BitwiseNot => UnaryOperation::BitwiseNot, + UnaryOperator::LogicalNot => UnaryOperation::LogicalNot, _ => unreachable!(), }; - Instance::RValue(evaluate_pure_unary_operator( + // TODO abstract handling? + let result = evaluate_unary_operator( operator, - operand_type, + operand, + environment, &mut checking_data.types, checking_data.options.strict_casts, - )) + ); + if let Ok(result) = result { + Instance::RValue(result) + } else { + checking_data.diagnostics_container.add_error( + TypeCheckError::InvalidUnaryOperation { + operator, + operand: TypeStringRepresentation::from_type_id( + operand, + environment, + &checking_data.types, + false, + ), + position: position.with_source(environment.get_source()), + }, + ); + Instance::RValue(TypeId::ERROR_TYPE) + } } UnaryOperator::Await => { // TODO get promise T @@ -496,7 +515,7 @@ pub(super) fn synthesise_expression( "yield expression", position.with_source(environment.get_source()), ); - return TypeId::ERROR_TYPE; + return TypeId::UNIMPLEMENTED_ERROR_TYPE; } } } @@ -537,7 +556,7 @@ pub(super) fn synthesise_expression( "Invert operator", position.with_source(environment.get_source()), ); - return TypeId::ERROR_TYPE; + return TypeId::UNIMPLEMENTED_ERROR_TYPE; } UnaryPrefixAssignmentOperator::IncrementOrDecrement(direction) => { return environment.assign_handle_errors( @@ -614,7 +633,11 @@ pub(super) fn synthesise_expression( let site = position.with_source(environment.get_source()); if *is_optional { - let null_or_undefined = is_null_or_undefined(on, &mut checking_data.types); + let null_or_undefined = + is_null_or_undefined(on, environment, &mut checking_data.types); + + // crate::utilities::notify!("{:?}", null_or_undefined); + Instance::RValue(new_conditional_context( environment, (null_or_undefined, parent.get_position()), @@ -622,6 +645,7 @@ pub(super) fn synthesise_expression( TypeId::UNDEFINED_TYPE }, Some(|env: &mut Environment, data: &mut CheckingData| { + let on = env.info.narrowed_values.get(&on).copied().unwrap_or(on); let result = env.get_property_handle_errors( on, publicity, @@ -661,7 +685,7 @@ pub(super) fn synthesise_expression( if *is_optional { let null_or_undefined = - is_null_or_undefined(being_indexed, &mut checking_data.types); + is_null_or_undefined(being_indexed, environment, &mut checking_data.types); Instance::RValue(new_conditional_context( environment, (null_or_undefined, indexee.get_position()), @@ -756,19 +780,19 @@ pub(super) fn synthesise_expression( "Property access on super", position.with_source(environment.get_source()), ); - return TypeId::ERROR_TYPE; + return TypeId::UNIMPLEMENTED_ERROR_TYPE; } SuperReference::Index { indexer: _ } => { checking_data.raise_unimplemented_error( "Index on super", position.with_source(environment.get_source()), ); - return TypeId::ERROR_TYPE; + return TypeId::UNIMPLEMENTED_ERROR_TYPE; } } } else { crate::utilities::notify!("TODO error"); - Instance::RValue(TypeId::ERROR_TYPE) + Instance::RValue(TypeId::UNIMPLEMENTED_ERROR_TYPE) } } Expression::NewTarget(..) => { @@ -1001,7 +1025,7 @@ pub(super) fn synthesise_expression( "dynamic import", position.with_source(environment.get_source()), ); - return TypeId::ERROR_TYPE; + return TypeId::UNIMPLEMENTED_ERROR_TYPE; } Expression::IsExpression(is_expr) => { Instance::RValue(synthesise_is_expression(is_expr, environment, checking_data)) @@ -1285,7 +1309,7 @@ pub(super) fn synthesise_object_literal( pos.with_source(environment.get_source()), ); - return TypeId::ERROR_TYPE; + return TypeId::UNIMPLEMENTED_ERROR_TYPE; } } } @@ -1299,13 +1323,7 @@ pub(super) fn synthesise_object_literal( let value = match get_variable { Ok(VariableWithValue(_variable, value)) => value, Err(_err) => { - // checking_data.diagnostics_container.add_error( - // TypeCheckError::CouldNotFindVariable { - // variable: err.name, - // possibles: err.possibles, - // position: position.clone(), - // }, - // ); + // missing handled above TypeId::ERROR_TYPE } }; diff --git a/checker/src/synthesis/extensions/jsx.rs b/checker/src/synthesis/extensions/jsx.rs index 94bd2d8b..c8c3f3f3 100644 --- a/checker/src/synthesis/extensions/jsx.rs +++ b/checker/src/synthesis/extensions/jsx.rs @@ -31,7 +31,7 @@ pub(crate) fn synthesise_jsx_root( "JSX fragment", fragment.get_position().with_source(environment.get_source()), ); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } } } @@ -502,14 +502,14 @@ fn synthesise_attribute( "spread JSX attribute", pos.with_source(environment.get_source()), ); - return (PropertyKey::String(Cow::Borrowed("err")), TypeId::ERROR_TYPE); + return (PropertyKey::String(Cow::Borrowed("err")), TypeId::UNIMPLEMENTED_ERROR_TYPE); } JSXAttribute::Shorthand(expr) => { checking_data.raise_unimplemented_error( "shorthand JSX attribute", expr.get_position().with_source(environment.get_source()), ); - return (PropertyKey::String(Cow::Borrowed("err")), TypeId::ERROR_TYPE); + return (PropertyKey::String(Cow::Borrowed("err")), TypeId::UNIMPLEMENTED_ERROR_TYPE); } }; diff --git a/checker/src/synthesis/hoisting.rs b/checker/src/synthesis/hoisting.rs index e42a6d65..12f4d047 100644 --- a/checker/src/synthesis/hoisting.rs +++ b/checker/src/synthesis/hoisting.rs @@ -618,11 +618,11 @@ pub(crate) fn hoist_statements( let value = super::functions::build_overloaded_function( crate::FunctionId(environment.get_source(), function.position.start), crate::types::functions::FunctionBehavior::Function { - this_id: TypeId::ERROR_TYPE, - prototype: TypeId::ERROR_TYPE, + this_id: TypeId::IS_ASSIGNED_VALUE_LATER, + prototype: TypeId::IS_ASSIGNED_VALUE_LATER, is_async: function.header.is_async(), is_generator: function.header.is_generator(), - name: TypeId::ERROR_TYPE, + name: TypeId::IS_ASSIGNED_VALUE_LATER, }, overloads, actual, diff --git a/checker/src/synthesis/interfaces.rs b/checker/src/synthesis/interfaces.rs index 7d019f71..12863cd4 100644 --- a/checker/src/synthesis/interfaces.rs +++ b/checker/src/synthesis/interfaces.rs @@ -9,8 +9,9 @@ use crate::{ synthesis::parser_property_key_to_checker_property_key, types::{ calling::Callable, + helpers::references_key_of, properties::{Descriptor, PropertyKey, PropertyValue, Publicity}, - references_key_of, FunctionType, Type, + FunctionType, Type, }, CheckingData, Scope, TypeId, }; @@ -206,7 +207,7 @@ pub(super) fn synthesise_signatures { diff --git a/checker/src/synthesis/type_annotations.rs b/checker/src/synthesis/type_annotations.rs index a683302c..bcbb40c8 100644 --- a/checker/src/synthesis/type_annotations.rs +++ b/checker/src/synthesis/type_annotations.rs @@ -12,7 +12,7 @@ use crate::{ types::{ generics::generic_type_arguments::GenericArguments, properties::{PropertyKey, PropertyValue, Publicity}, - Constant, Constructor, PartiallyAppliedGenerics, Type, TypeId, + Constant, Constructor, PartiallyAppliedGenerics, PolyNature, Type, TypeId, }, types::{ generics::ExplicitTypeArguments, @@ -111,56 +111,7 @@ pub fn synthesise_type_annotation( } } } - TypeAnnotation::Union(type_annotations, _) => { - let iterator = type_annotations - .iter() - .map(|type_annotation| { - synthesise_type_annotation(type_annotation, environment, checking_data) - }) - .collect::>() - .into_iter(); - - iterator - .reduce(|acc, right| checking_data.types.new_or_type(acc, right)) - .expect("Empty union") - } - TypeAnnotation::Intersection(type_annotations, position) => { - let mut iterator = type_annotations - .iter() - .map(|type_annotation| { - synthesise_type_annotation(type_annotation, environment, checking_data) - }) - .collect::>() - .into_iter(); - - let mut acc = iterator.next().expect("Empty intersection"); - for right in iterator { - if let Ok(new_ty) = checking_data.types.new_and_type(acc, right) { - acc = new_ty; - } else { - checking_data.diagnostics_container.add_error( - TypeCheckWarning::TypesDoNotIntersect { - left: TypeStringRepresentation::from_type_id( - acc, - environment, - &checking_data.types, - checking_data.options.debug_types, - ), - right: TypeStringRepresentation::from_type_id( - right, - environment, - &checking_data.types, - checking_data.options.debug_types, - ), - position: position.with_source(environment.get_source()), - }, - ); - return TypeId::ERROR_TYPE; - } - } - acc - } - // This will take the found type and generate a `StructureGeneric` based on the type arguments + // This will take the found type and generate a `PartiallyAppliedGeneric` based on the type arguments TypeAnnotation::NameWithGenericArguments(name, arguments, position) => { match name { TypeName::Name(name) => { @@ -183,71 +134,114 @@ pub fn synthesise_type_annotation( let mut type_arguments: crate::Map = crate::Map::default(); - for (parameter, argument_type_annotation) in - parameters.clone().into_iter().zip(arguments.iter()) - { - let argument = synthesise_type_annotation( - argument_type_annotation, - environment, - checking_data, + // TODO better diagnostic + if parameters.len() != arguments.len() { + checking_data.diagnostics_container.add_error( + TypeCheckError::GenericArgumentCountMismatch { + expected_count: parameters.len(), + count: arguments.len(), + position: position.with_source(environment.get_source()), + }, ); + // Continue is fine + } + let mut argument_type_annotations = arguments.iter(); + for parameter in parameters.iter().copied() { + let parameter_restriction = + if let Type::RootPolyType(PolyNature::StructureGeneric { + extends, + .. + }) = checking_data.types.get_type_by_id(parameter) + { + *extends + } else { + crate::utilities::notify!("Shouldn't be here"); + parameter + }; + + let (argument, position) = if let Some(argument_type_annotation) = + argument_type_annotations.next() { - // TODO check restriction on parameter - // let mut basic_equality = BasicEquality { - // add_property_restrictions: true, - // position: argument_type_annotation - // .get_position() - // .with_source(environment.get_source()), - // // TODO not needed - // object_constraints: Default::default(), - // allow_errors: true, - // }; - - // let Type::RootPolyType(PolyNature::InterfaceGeneric { name: _ }) = - // checking_data.types.get_type_by_id(parameter) - // else { - // unreachable!() - // }; - - // // TODO it is a bit weird with the arguments, maybe should get their restriction directly here? - // // Definition files don't necessary need to check ... - // let result = type_is_subtype( - // *parameter_restriction, - // argument, - // &mut basic_equality, - // environment, - // &checking_data.types, - // ); - - // if let SubTypeResult::IsNotSubType(_matches) = result { - // let error = TypeCheckError::GenericArgumentDoesNotMeetRestriction { - // parameter_restriction: TypeStringRepresentation::from_type_id( - // *parameter_restriction, - // environment, - // &checking_data.types, - // checking_data.options.debug_types, - // ), - // argument: TypeStringRepresentation::from_type_id( - // argument, - // environment, - // &checking_data.types, - // checking_data.options.debug_types, - // ), - // position: argument_type_annotation - // .get_position() - // .with_source(environment.get_source()), - // }; - - // checking_data.diagnostics_container.add_error(error); - // } - } - - let with_source = argument_type_annotation - .get_position() - .with_source(environment.get_source()); - - type_arguments.insert(parameter, (argument, with_source)); + let argument = if let TypeAnnotation::Infer { + name, + extends: None, + position: _, + } = argument_type_annotation + { + environment.new_infer_type( + parameter_restriction, + name, + &mut checking_data.types, + ) + } else { + synthesise_type_annotation( + argument_type_annotation, + environment, + checking_data, + ) + }; + + { + use crate::types::subtyping; + + let mut state = subtyping::State { + already_checked: Default::default(), + mode: Default::default(), + contributions: Default::default(), + others: subtyping::SubTypingOptions { + allow_errors: true, + }, + object_constraints: None, + }; + + let result = subtyping::type_is_subtype( + parameter_restriction, + argument, + &mut state, + environment, + &checking_data.types, + ); + + if let subtyping::SubTypeResult::IsNotSubType(_matches) = + result + { + let error = + TypeCheckError::GenericArgumentDoesNotMeetRestriction { + parameter_restriction: + TypeStringRepresentation::from_type_id( + parameter_restriction, + environment, + &checking_data.types, + checking_data.options.debug_types, + ), + argument: TypeStringRepresentation::from_type_id( + argument, + environment, + &checking_data.types, + checking_data.options.debug_types, + ), + position: argument_type_annotation + .get_position() + .with_source(environment.get_source()), + }; + + checking_data.diagnostics_container.add_error(error); + } + } + let position = argument_type_annotation + .get_position() + .with_source(environment.get_source()); + + (argument, position) + } else { + ( + TypeId::ERROR_TYPE, + ::NULL, + ) + }; + + type_arguments.insert(parameter, (argument, position)); } // Inline alias with arguments unless intrinsic @@ -301,10 +295,11 @@ pub fn synthesise_type_annotation( } } else { checking_data.diagnostics_container.add_error( - TypeCheckError::TypeHasNoGenericParameters( - name.clone(), - position.with_source(environment.get_source()), - ), + TypeCheckError::GenericArgumentCountMismatch { + expected_count: 0, + count: arguments.len(), + position: position.with_source(environment.get_source()), + }, ); TypeId::ERROR_TYPE } @@ -331,10 +326,59 @@ pub fn synthesise_type_annotation( "namespace item", position.with_source(environment.get_source()), ); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } } } + TypeAnnotation::Union(type_annotations, _) => { + let iterator = type_annotations + .iter() + .map(|type_annotation| { + synthesise_type_annotation(type_annotation, environment, checking_data) + }) + .collect::>() + .into_iter(); + + iterator + .reduce(|acc, right| checking_data.types.new_or_type(acc, right)) + .expect("Empty union") + } + TypeAnnotation::Intersection(type_annotations, position) => { + let mut iterator = type_annotations + .iter() + .map(|type_annotation| { + synthesise_type_annotation(type_annotation, environment, checking_data) + }) + .collect::>() + .into_iter(); + + let mut acc = iterator.next().expect("Empty intersection"); + for right in iterator { + if let Ok(new_ty) = checking_data.types.new_and_type(acc, right) { + acc = new_ty; + } else { + checking_data.diagnostics_container.add_error( + TypeCheckWarning::TypesDoNotIntersect { + left: TypeStringRepresentation::from_type_id( + acc, + environment, + &checking_data.types, + checking_data.options.debug_types, + ), + right: TypeStringRepresentation::from_type_id( + right, + environment, + &checking_data.types, + checking_data.options.debug_types, + ), + position: position.with_source(environment.get_source()), + }, + ); + return TypeId::ERROR_TYPE; + } + } + acc + } TypeAnnotation::FunctionLiteral { type_parameters, parameters, @@ -366,7 +410,7 @@ pub fn synthesise_type_annotation( "abstact type annotation", position.with_source(environment.get_source()), ); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } TypeAnnotation::Readonly(type_annotation, position) => { let underlying_type = @@ -400,7 +444,7 @@ pub fn synthesise_type_annotation( "constructor literal", position.with_source(environment.get_source()), ); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } // Object literals are first turned into types as if they were interface declarations and then // returns reference to object literal @@ -482,9 +526,10 @@ pub fn synthesise_type_annotation( crate::utilities::notify!("found wildcard"); let after = idx.into_type(&mut checking_data.types); - let key = checking_data.types.new_intrinsic( + let key = crate::types::intrinsics::new_intrinsic( &crate::types::intrinsics::Intrinsic::GreaterThan, after, + &mut checking_data.types, ); let item_type = checking_data.types.register_type(Type::Constructor( @@ -557,7 +602,7 @@ pub fn synthesise_type_annotation( "throw error for annotation", position.with_source(environment.get_source()), ); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } } TypeAnnotation::Conditional { condition, resolve_true, resolve_false, position: _ } => { @@ -673,18 +718,24 @@ pub fn synthesise_type_annotation( lhs: acc, operator: crate::features::operations::MathematicalAndBitwise::Add, rhs: lhs, + result: TypeId::STRING_TYPE, }, )) }; - let rhs = synthesise_type_annotation( - dynamic_part.get_inner_ref(), - environment, - checking_data, - ); + // WIP fix correcting `infer T` to `infer T extends string` so that string addition works + let dynamic_part = dynamic_part.get_inner_ref(); + let rhs = if let TypeAnnotation::Infer { name, extends: None, position: _ } = + dynamic_part + { + environment.new_infer_type(TypeId::STRING_TYPE, name, &mut checking_data.types) + } else { + synthesise_type_annotation(dynamic_part, environment, checking_data) + }; let constructor = crate::types::Constructor::BinaryOperator { lhs: acc, operator: crate::features::operations::MathematicalAndBitwise::Add, rhs, + result: TypeId::STRING_TYPE, }; acc = checking_data.types.register_type(Type::Constructor(constructor)); } @@ -700,6 +751,7 @@ pub fn synthesise_type_annotation( lhs: acc, operator: crate::features::operations::MathematicalAndBitwise::Add, rhs: lhs, + result: TypeId::STRING_TYPE, }, )) } @@ -716,7 +768,7 @@ pub fn synthesise_type_annotation( environment.context_type.scope { let infer_type = checking_data.types.register_type(Type::RootPolyType( - crate::types::PolyNature::InferGeneric { name: name.clone(), extends }, + PolyNature::InferGeneric { name: name.clone(), extends }, )); let existing = infer_parameters.insert(name.clone(), infer_type); @@ -726,7 +778,7 @@ pub fn synthesise_type_annotation( infer_type } else { crate::utilities::notify!("Raise error diagnostic"); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } } TypeAnnotation::Extends { item, extends, position: _ } => { @@ -752,8 +804,8 @@ pub fn synthesise_type_annotation( .copied(), parser::type_annotations::IsItem::This => { // TODO - let based_on = TypeId::ERROR_TYPE; - let ty = Type::RootPolyType(crate::types::PolyNature::FreeVariable { + let based_on = TypeId::UNIMPLEMENTED_ERROR_TYPE; + let ty = Type::RootPolyType(PolyNature::FreeVariable { reference: crate::events::RootReference::This, based_on, }); @@ -799,7 +851,7 @@ pub fn synthesise_type_annotation( "`this` annotation", position.with_source(environment.get_source()), ); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } }; diff --git a/checker/src/types/calling.rs b/checker/src/types/calling.rs index c7ae9e68..3ea2723a 100644 --- a/checker/src/types/calling.rs +++ b/checker/src/types/calling.rs @@ -3,7 +3,8 @@ use source_map::{BaseSpan, Nullable, SpanWithSource}; use crate::{ context::{invocation::CheckThings, CallCheckingBehavior, Environment, InformationChain}, diagnostics::{ - InfoDiagnostic, TypeCheckError, TypeCheckWarning, TypeStringRepresentation, TDZ, + InfoDiagnostic, TypeCheckError, TypeCheckWarning, TypeStringRepresentation, + VariableUsedInTDZ, }, events::{ application::ApplicationInput, apply_events, ApplicationResult, Event, RootReference, @@ -278,15 +279,15 @@ pub fn call_type_handle_errors match l { NeedsCalculation::Infer { on } => { if on == TypeId::ERROR_TYPE { - (TypeId::ERROR_TYPE, None) + (TypeId::UNIMPLEMENTED_ERROR_TYPE, None) } else { crate::utilities::notify!("TODO function calling inference on {:?}", on); - (TypeId::ERROR_TYPE, None) + (TypeId::UNIMPLEMENTED_ERROR_TYPE, None) } } NeedsCalculation::Proxy(..) => { crate::utilities::notify!("TODO calling proxy"); - (TypeId::ERROR_TYPE, None) + (TypeId::UNIMPLEMENTED_ERROR_TYPE, None) } }, Err(Invalid(ty)) => { @@ -379,7 +380,7 @@ impl Callable { types.get_function_from_id(*id).return_type } else { crate::utilities::notify!("Cannot get return type"); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } } } @@ -710,7 +711,7 @@ fn call_logical( returned_type: types.new_error_type(function_type.return_type), }); } - Err(ConstantFunctionError::BadCall) => { + Err(ConstantFunctionError::CannotComputeConstant) => { crate::utilities::notify!( "Constant function calling failed, non constant params" ); @@ -865,11 +866,13 @@ fn call_logical( Ok(result) } else { - panic!() + panic!("no function") } } - Logical::Or { condition, left, right } => { - todo!("{:?}", (condition, left, right)); + Logical::Or { .. } => { + crate::utilities::notify!("Calling OR"); + Err(BadCallOutput { returned_type: TypeId::UNIMPLEMENTED_ERROR_TYPE }) + // todo!("{:?}", (condition, left, right)); // if let (Ok(_left), Ok(_right)) = (*left, *right) { // let (truthy_result, otherwise_result) = behavior.evaluate_conditionally( // top_environment, @@ -933,7 +936,10 @@ fn call_logical( (behavior, diagnostics), ) } - Logical::BasedOnKey { .. } => todo!(), + Logical::BasedOnKey { .. } => { + crate::utilities::notify!("Calling based on key?"); + Err(BadCallOutput { returned_type: TypeId::UNIMPLEMENTED_ERROR_TYPE }) + } } } @@ -989,7 +995,6 @@ pub enum FunctionCallingError { count: usize, position: SpanWithSource, }, - ExcessTypeArguments { expected_count: usize, count: usize, @@ -1007,12 +1012,12 @@ pub enum FunctionCallingError { CyclicRecursion(FunctionId, SpanWithSource), NoLogicForIdentifier(String, SpanWithSource), NeedsToBeCalledWithNewKeyword(SpanWithSource), - TDZ { - error: TDZ, + VariableUsedInTDZ { + error: VariableUsedInTDZ, /// Should be set by parent call_site: SpanWithSource, }, - InvalidRegexp(crate::diagnostics::InvalidRegexp), + InvalidRegExp(crate::diagnostics::InvalidRegExp), /// For #18 SetPropertyConstraint { property_type: TypeStringRepresentation, @@ -1028,7 +1033,7 @@ pub enum FunctionCallingError { /// Should be set by parent call_site: SpanWithSource, }, - NotConfiguarable { + NotConfigurable { property: crate::diagnostics::PropertyKeyRepresentation, /// Should be set by parent call_site: SpanWithSource, @@ -1192,7 +1197,10 @@ impl FunctionType { // Adjust call sites. (because they aren't currently passed down) for d in &mut diagnostics.errors[current_errors..] { - if let FunctionCallingError::TDZ { call_site: ref mut c, .. } + if let FunctionCallingError::VariableUsedInTDZ { + call_site: ref mut c, + .. + } | FunctionCallingError::SetPropertyConstraint { call_site: ref mut c, .. @@ -1509,7 +1517,7 @@ impl FunctionType { CalledWithNew::New { on } => on, CalledWithNew::Super { .. } => { crate::utilities::notify!("Get this type for super new.target"); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE // let ty = this_value.0; // let on = crate::types::printing::print_type( // ty, diff --git a/checker/src/types/casts.rs b/checker/src/types/casts.rs index 13b8ba5e..449ba3a7 100644 --- a/checker/src/types/casts.rs +++ b/checker/src/types/casts.rs @@ -17,7 +17,7 @@ pub(crate) fn cast_as_number(cst: &Constant, strict_casts: bool) -> Result Ok(if *val { 1f64 } else { 0f64 }), - Constant::NaN => Ok(f64::NAN), + Constant::Undefined | Constant::NaN => Ok(f64::NAN), Constant::Symbol { key: _ } => todo!(), } } @@ -41,7 +41,7 @@ pub(crate) fn cast_as_boolean(cst: &Constant, strict_casts: bool) -> Result number.into_inner() != 0., Constant::String(value) => !value.is_empty(), Constant::Boolean(value) => *value, - Constant::NaN => false, + Constant::Undefined | Constant::NaN => false, Constant::Symbol { key: _ } => todo!(), }) } diff --git a/checker/src/types/classes.rs b/checker/src/types/classes.rs index d181caa5..fcdd1a22 100644 --- a/checker/src/types/classes.rs +++ b/checker/src/types/classes.rs @@ -57,9 +57,9 @@ fn _register_class_properties_for_later_application< ) { let scope = crate::Scope::Function(crate::context::environment::FunctionScope::Constructor { extends: false, - type_of_super: Some(TypeId::ERROR_TYPE), + type_of_super: Some(TypeId::UNIMPLEMENTED_ERROR_TYPE), // TODO get from above - this_object_type: TypeId::ERROR_TYPE, + this_object_type: TypeId::UNIMPLEMENTED_ERROR_TYPE, }); let ((), result, _) = environment.new_lexical_environment_fold_into_parent( diff --git a/checker/src/types/disjoint.rs b/checker/src/types/disjoint.rs index db70f788..a49c5f59 100644 --- a/checker/src/types/disjoint.rs +++ b/checker/src/types/disjoint.rs @@ -1,5 +1,5 @@ -use super::PartiallyAppliedGenerics; -use crate::{context::InformationChain, types::TypeStore, Type, TypeId}; +use super::{Constant, PartiallyAppliedGenerics, Type, TypeId, TypeStore}; +use crate::context::InformationChain; /// For equality + [`crate::intrinsics::Intrinsics::Not`] /// @@ -9,56 +9,48 @@ use crate::{context::InformationChain, types::TypeStore, Type, TypeId}; /// /// Could shrink some logic here but is more readable verbose pub fn types_are_disjoint( - left: TypeId, - right: TypeId, + lhs: TypeId, + rhs: TypeId, already_checked: &mut Vec<(TypeId, TypeId)>, information: &impl InformationChain, types: &TypeStore, ) -> bool { - if left == right { + // crate::utilities::notify!("are disjoint? {:?}", (lhs, rhs)); + + if lhs == rhs || lhs == TypeId::ANY_TYPE || rhs == TypeId::ANY_TYPE { false - } else if already_checked.iter().any(|pair| *pair == (left, right)) { + } else if already_checked.iter().any(|pair| *pair == (lhs, rhs)) { // TODO explain why `true` true } else { - let left_ty = types.get_type_by_id(left); - let right_ty = types.get_type_by_id(right); + let lhs_ty = types.get_type_by_id(lhs); + let rhs_ty = types.get_type_by_id(rhs); - // if let Type::Constructor(Constructor::KeyOf(_)) = left_ty { - // todo!("get property != ") - // } else - if let Type::Constant(left_cst) = left_ty { - if let Type::Constant(right_cst) = right_ty { - left_cst != right_cst - } else { - left_cst.get_backing_type_id() != right - } - } else if let Type::Constant(right_cst) = right_ty { - right_cst.get_backing_type_id() != left - } else if let Type::Or(left_left, left_right) = left_ty { - types_are_disjoint(*left_left, right, already_checked, information, types) - && types_are_disjoint(*left_right, right, already_checked, information, types) - } else if let Type::And(left_left, left_right) = left_ty { - types_are_disjoint(*left_left, right, already_checked, information, types) - || types_are_disjoint(*left_right, right, already_checked, information, types) - } else if let Type::Or(right_left, right_right) = right_ty { - types_are_disjoint(left, *right_left, already_checked, information, types) - && types_are_disjoint(left, *right_right, already_checked, information, types) - } else if let Type::And(right_left, right_right) = right_ty { - types_are_disjoint(left, *right_left, already_checked, information, types) - || types_are_disjoint(left, *right_right, already_checked, information, types) - } else if let Type::AliasTo { to, parameters: None, name: _ } = left_ty { + // Order of these branches matter + if let Type::Or(lhs_lhs, lhs_rhs) = lhs_ty { + types_are_disjoint(*lhs_lhs, rhs, already_checked, information, types) + && types_are_disjoint(*lhs_rhs, rhs, already_checked, information, types) + } else if let Type::And(lhs_lhs, lhs_rhs) = lhs_ty { + types_are_disjoint(*lhs_lhs, rhs, already_checked, information, types) + || types_are_disjoint(*lhs_rhs, rhs, already_checked, information, types) + } else if let Type::Or(rhs_lhs, rhs_rhs) = rhs_ty { + types_are_disjoint(lhs, *rhs_lhs, already_checked, information, types) + && types_are_disjoint(lhs, *rhs_rhs, already_checked, information, types) + } else if let Type::And(rhs_lhs, rhs_rhs) = rhs_ty { + types_are_disjoint(lhs, *rhs_lhs, already_checked, information, types) + || types_are_disjoint(lhs, *rhs_rhs, already_checked, information, types) + } else if let Type::AliasTo { to, parameters: None, name: _ } = lhs_ty { // TODO temp fix, need infer ANY if matches!(*to, TypeId::ANY_TYPE) { true } else { - types_are_disjoint(*to, right, already_checked, information, types) + types_are_disjoint(*to, rhs, already_checked, information, types) } - } else if let Type::AliasTo { to, parameters: None, name: _ } = right_ty { + } else if let Type::AliasTo { to, parameters: None, name: _ } = rhs_ty { if matches!(*to, TypeId::ANY_TYPE) { true } else { - types_are_disjoint(left, *to, already_checked, information, types) + types_are_disjoint(lhs, *to, already_checked, information, types) } } else if let ( Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { @@ -66,26 +58,145 @@ pub fn types_are_disjoint( arguments: _arguments, }), Type::Object(super::ObjectNature::RealDeal), - ) = (left_ty, right_ty) + ) = (lhs_ty, rhs_ty) { - let rhs_prototype = information - .get_chain_of_info() - .find_map(|info| info.prototypes.get(&right).copied()); + let rhs_prototype = + information.get_chain_of_info().find_map(|info| info.prototypes.get(&rhs).copied()); // { // if let Some(lhs_prototype) = info.prototypes.get(&lhs).copied() { - // let rhs_prototype = information.get_prototype_of(right); + // let rhs_prototype = information.get_prototype_of(rhs); // TODO leaving arguments out of picture for now rhs_prototype != Some(TypeId::ARRAY_TYPE) - } else if let (Type::Object(super::ObjectNature::RealDeal), _) - | (_, Type::Object(super::ObjectNature::RealDeal)) = (left_ty, right_ty) + } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::NOT_RESTRICTION, + arguments, + }) = lhs_ty { - true + use super::subtyping; + let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); + let mut state = subtyping::State { + // TODO + already_checked: already_checked.clone(), + mode: Default::default(), + contributions: None, + others: subtyping::SubTypingOptions { allow_errors: true }, + object_constraints: None, + }; + + crate::utilities::notify!("{:?}", (lhs, inner)); + + subtyping::type_is_subtype(rhs, inner, &mut state, information, types).is_subtype() + } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::NOT_RESTRICTION, + arguments, + }) = rhs_ty + { + use super::subtyping; + let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); + let mut state = subtyping::State { + // TODO + already_checked: already_checked.clone(), + mode: Default::default(), + contributions: None, + others: subtyping::SubTypingOptions { allow_errors: true }, + object_constraints: None, + }; + + crate::utilities::notify!("{:?}", (lhs, inner)); + + subtyping::type_is_subtype(lhs, inner, &mut state, information, types).is_subtype() + } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE, + arguments: _, + }) = lhs_ty + { + let range = super::intrinsics::get_range(lhs, types).unwrap(); + if let Some(rhs_range) = super::intrinsics::get_range(rhs, types) { + let overlap = range.overlaps(rhs_range); + crate::utilities::notify!("{:?}", overlap); + !overlap + } else { + crate::utilities::notify!("Here"); + true + } + } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE, + arguments: _, + }) = rhs_ty + { + let range = super::intrinsics::get_range(rhs, types).unwrap(); + if let Some(lhs_range) = super::intrinsics::get_range(lhs, types) { + let overlap = range.overlaps(lhs_range); + crate::utilities::notify!("{:?}", overlap); + !overlap + } else { + crate::utilities::notify!("Here"); + true + } + } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::MULTIPLE_OF, + arguments, + }) = lhs_ty + { + // Little bit complex here because dealing with decimal types, not integers + if let (Type::Constant(Constant::Number(lhs)), Type::Constant(Constant::Number(rhs))) = ( + types.get_type_by_id( + arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC).unwrap(), + ), + types.get_type_by_id(rhs), + ) { + let result = rhs % lhs != 0.; + crate::utilities::notify!("{:?} {:?}", rhs, lhs); + result + } else { + crate::utilities::notify!("Here"); + false + } + } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::MULTIPLE_OF, + arguments, + }) = rhs_ty + { + // Little bit complex here because dealing with decimal types, not integers + if let (Type::Constant(Constant::Number(lhs)), Type::Constant(Constant::Number(rhs))) = ( + types.get_type_by_id(lhs), + types.get_type_by_id( + arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC).unwrap(), + ), + ) { + let result = lhs % rhs != 0.; + crate::utilities::notify!("{:?} {:?}", lhs, rhs); + result + } else { + crate::utilities::notify!("Here"); + false + } + } else if let Some(lhs) = super::get_constraint(lhs, types) { + // TODO not sure whether these should be here? + types_are_disjoint(lhs, rhs, already_checked, information, types) + } else if let Some(rhs) = super::get_constraint(rhs, types) { + // TODO not sure whether these should be here? + types_are_disjoint(lhs, rhs, already_checked, information, types) + } else if let Type::Constant(lhs_cst) = lhs_ty { + if let Type::Constant(rhs_cst) = rhs_ty { + lhs_cst != rhs_cst + } else { + types_are_disjoint( + lhs_cst.get_backing_type(), + rhs, + already_checked, + information, + types, + ) + } + } else if let Type::Constant(rhs_cst) = rhs_ty { + types_are_disjoint(rhs_cst.get_backing_type(), lhs, already_checked, information, types) } else { crate::utilities::notify!( "{:?} cap {:?} == empty ? cases. Might be missing, calling disjoint", - left_ty, - right_ty + lhs_ty, + rhs_ty ); true } diff --git a/checker/src/types/functions.rs b/checker/src/types/functions.rs index d8f21bcf..351c512e 100644 --- a/checker/src/types/functions.rs +++ b/checker/src/types/functions.rs @@ -94,7 +94,7 @@ impl FunctionType { extends: false, type_of_super: None, // Set later - this_object_type: TypeId::ERROR_TYPE, + this_object_type: TypeId::IS_ASSIGNED_VALUE_LATER, }); let (on, env_data, _) = environment.new_lexical_environment_fold_into_parent( diff --git a/checker/src/types/generics/substitution.rs b/checker/src/types/generics/substitution.rs index 5d28487d..dacfe81a 100644 --- a/checker/src/types/generics/substitution.rs +++ b/checker/src/types/generics/substitution.rs @@ -7,10 +7,7 @@ use crate::{ features::{ functions::{ClosureChain, ClosureId}, objects::{Proxy, SpecialObject}, - operations::{ - evaluate_equality_inequality_operation, evaluate_mathematical_operation, - evaluate_pure_unary_operator, - }, + operations::{evaluate_equality_inequality_operation, evaluate_mathematical_operation}, }, subtyping::{State, SubTypingOptions}, types::{ @@ -210,11 +207,13 @@ pub(crate) fn substitute( Type::RootPolyType(nature) => { if let PolyNature::Open(_) | PolyNature::Error(_) = nature { id - } else if let PolyNature::FunctionGeneric { .. } - | PolyNature::StructureGeneric { .. } - | PolyNature::InferGeneric { .. } = nature - { + } else if let PolyNature::InferGeneric { .. } = nature { // Infer generic is fine for `type Index = T extends Array ? I : never`; + crate::utilities::notify!("No argument for infer generic (sometimes fine)"); + id + } else if let PolyNature::FunctionGeneric { .. } | PolyNature::StructureGeneric { .. } = + nature + { crate::utilities::notify!( "Could not find argument for explicit generic {:?} (nature={:?})", id, @@ -238,7 +237,8 @@ pub(crate) fn substitute( let lhs = substitute(lhs, arguments, environment, types); let rhs = substitute(rhs, arguments, environment, types); - match evaluate_mathematical_operation(lhs, operator, rhs, types, false) { + match evaluate_mathematical_operation(lhs, operator, rhs, environment, types, false) + { Ok(result) => result, Err(()) => { unreachable!( @@ -249,14 +249,6 @@ pub(crate) fn substitute( } } } - Constructor::UnaryOperator { operand, operator, .. } => { - let operand = substitute(operand, arguments, environment, types); - evaluate_pure_unary_operator( - operator, operand, types, - // Restrictions should have been made ahead of time - false, - ) - } Constructor::ConditionalResult { condition, truthy_result, @@ -415,11 +407,11 @@ pub(crate) fn substitute( } Ok(value) => { crate::utilities::notify!("TODO {:?}", value); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } Err(err) => { crate::utilities::notify!("{:?}", err); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } } } else { @@ -428,7 +420,7 @@ pub(crate) fn substitute( on_type, under ); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } } Constructor::Image { .. } => { @@ -475,7 +467,21 @@ pub(crate) fn substitute( let lhs = substitute(lhs, arguments, environment, types); let rhs = substitute(rhs, arguments, environment, types); - evaluate_equality_inequality_operation(lhs, &operator, rhs, types, false) + let result = evaluate_equality_inequality_operation( + lhs, + &operator, + rhs, + environment, + types, + false, + ); + + if let Ok((left, _warning)) = result { + left + } else { + crate::utilities::notify!("Error here"); + TypeId::OPEN_BOOLEAN_TYPE + } } Constructor::TypeOperator(op) => match op { crate::types::TypeOperator::TypeOf(ty) => { @@ -487,6 +493,7 @@ pub(crate) fn substitute( } crate::types::TypeOperator::IsPrototype { lhs, rhs_prototype } => { let lhs = substitute(lhs, arguments, environment, types); + let rhs_prototype = substitute(rhs_prototype, arguments, environment, types); crate::features::instance_of_operator_rhs_prototype( lhs, rhs_prototype, diff --git a/checker/src/types/intrinsics.rs b/checker/src/types/intrinsics.rs index 64359b8a..17581d27 100644 --- a/checker/src/types/intrinsics.rs +++ b/checker/src/types/intrinsics.rs @@ -10,6 +10,8 @@ use crate::{ use super::Type; +pub use crate::utilities::float_range::FloatRange; + /// These are special marker types (using [`Type::Alias`]) /// /// Some are from TSC, others are added by me! @@ -118,43 +120,108 @@ pub fn is_intrinsic(id: TypeId) -> bool { #[must_use] pub fn ezno_number_intrinsic(id: TypeId) -> bool { - matches!(id, TypeId::LESS_THAN | TypeId::GREATER_THAN | TypeId::MULTIPLE_OF) + matches!(id, TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE | TypeId::MULTIPLE_OF) } #[must_use] -pub fn get_greater_than(on: TypeId, types: &TypeStore) -> Option { +pub fn get_greater_than(on: TypeId, types: &TypeStore) -> Option<(bool, TypeId)> { let on = get_constraint(on, types).unwrap_or(on); let ty = types.get_type_by_id(on); if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: TypeId::GREATER_THAN, + on: on @ (TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE), arguments, }) = ty { - arguments.get_structure_restriction(TypeId::NUMBER_GENERIC) + let inclusive = *on == TypeId::INCLUSIVE_RANGE; + let floor = arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC).unwrap(); + Some((inclusive, floor)) } else if let Type::And(lhs, rhs) = ty { get_greater_than(*lhs, types).or_else(|| get_greater_than(*rhs, types)) + } else if let Type::Constant(crate::Constant::Number(..)) = ty { + Some((true, on)) } else { None } } #[must_use] -pub fn get_less_than(on: TypeId, types: &TypeStore) -> Option { +pub fn get_less_than(on: TypeId, types: &TypeStore) -> Option<(bool, TypeId)> { let on = get_constraint(on, types).unwrap_or(on); let ty = types.get_type_by_id(on); if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: TypeId::LESS_THAN, + on: on @ (TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE), arguments, }) = ty { - arguments.get_structure_restriction(TypeId::NUMBER_GENERIC) + let inclusive = *on == TypeId::INCLUSIVE_RANGE; + Some(( + inclusive, + arguments.get_structure_restriction(TypeId::NUMBER_CEILING_GENERIC).unwrap(), + )) } else if let Type::And(lhs, rhs) = ty { get_less_than(*lhs, types).or_else(|| get_less_than(*rhs, types)) + } else if let Type::Constant(crate::Constant::Number(..)) = ty { + Some((true, on)) + } else { + None + } +} + +#[must_use] +pub fn get_range(on: TypeId, types: &TypeStore) -> Option { + let on = get_constraint(on, types).unwrap_or(on); + let ty = types.get_type_by_id(on); + if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: on @ (TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE), + arguments, + }) = ty + { + let inclusive = *on == TypeId::INCLUSIVE_RANGE; + crate::utilities::notify!("{:?} {:?}", on, arguments); + let floor = arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC).unwrap(); + let ceiling = arguments.get_structure_restriction(TypeId::NUMBER_CEILING_GENERIC).unwrap(); + if let ( + Type::Constant(crate::Constant::Number(floor)), + Type::Constant(crate::Constant::Number(ceiling)), + ) = (types.get_type_by_id(floor), types.get_type_by_id(ceiling)) + { + let (floor, ceiling) = (*floor, *ceiling); + Some(if inclusive { + FloatRange::Inclusive { floor, ceiling } + } else { + FloatRange::Exclusive { floor, ceiling } + }) + } else { + crate::utilities::notify!("Not bottom top number"); + None + } + } else if let Type::Constant(crate::Constant::Number(number)) = ty { + Some(FloatRange::single(*number)) } else { None } } +#[must_use] +pub fn range_to_type(range: FloatRange, types: &mut TypeStore) -> TypeId { + use source_map::Nullable; + + let on = if let FloatRange::Inclusive { .. } = range { + TypeId::INCLUSIVE_RANGE + } else { + TypeId::EXCLUSIVE_RANGE + }; + let (FloatRange::Inclusive { floor, ceiling } | FloatRange::Exclusive { floor, ceiling }) = + range; + let floor = types.new_constant_type(crate::Constant::Number(floor)); + let ceiling = types.new_constant_type(crate::Constant::Number(ceiling)); + let arguments = GenericArguments::ExplicitRestrictions(crate::Map::from_iter([ + (TypeId::NUMBER_FLOOR_GENERIC, (floor, SpanWithSource::NULL)), + (TypeId::NUMBER_CEILING_GENERIC, (ceiling, SpanWithSource::NULL)), + ])); + types.register_type(Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on, arguments })) +} + #[must_use] pub fn get_multiple(on: TypeId, types: &TypeStore) -> Option { let on = get_constraint(on, types).unwrap_or(on); @@ -164,9 +231,11 @@ pub fn get_multiple(on: TypeId, types: &TypeStore) -> Option { arguments, }) = ty { - arguments.get_structure_restriction(TypeId::NUMBER_GENERIC) + arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC) } else if let Type::And(lhs, rhs) = ty { get_multiple(*lhs, types).or_else(|| get_multiple(*rhs, types)) + } else if let Type::Constant(crate::Constant::Number(..)) = ty { + Some(on) } else { None } @@ -174,22 +243,83 @@ pub fn get_multiple(on: TypeId, types: &TypeStore) -> Option { #[allow(clippy::match_like_matches_macro)] #[must_use] -pub fn can_be_not_a_number(on: TypeId, types: &TypeStore) -> bool { - let on = get_constraint(on, types).unwrap_or(on); +pub fn is_not_not_a_number(on: TypeId, types: &TypeStore) -> bool { if on == TypeId::NOT_NOT_A_NUMBER { true + } else if on == TypeId::NUMBER_TYPE || on == TypeId::ANY_TYPE { + false } else { let ty = types.get_type_by_id(on); if let Type::And(lhs, rhs) = ty { - can_be_not_a_number(*lhs, types) || can_be_not_a_number(*rhs, types) + is_not_not_a_number(*lhs, types) || is_not_not_a_number(*rhs, types) + } else if let Type::Or(lhs, rhs) = ty { + is_not_not_a_number(*lhs, types) && is_not_not_a_number(*rhs, types) } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: TypeId::MULTIPLE_OF | TypeId::LESS_THAN | TypeId::GREATER_THAN, + on: TypeId::MULTIPLE_OF | TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE, arguments: _, }) = ty { true + } else if let Type::AliasTo { to, .. } = ty { + is_not_not_a_number(*to, types) + } else if let Some(constraint) = get_constraint(on, types) { + is_not_not_a_number(constraint, types) } else { - false + true } } } + +pub fn new_intrinsic(intrinsic: &Intrinsic, argument: TypeId, types: &mut TypeStore) -> TypeId { + use source_map::Nullable; + let (on, to_pair) = + match intrinsic { + Intrinsic::Uppercase => (TypeId::STRING_UPPERCASE, TypeId::STRING_GENERIC), + Intrinsic::Lowercase => (TypeId::STRING_LOWERCASE, TypeId::STRING_GENERIC), + Intrinsic::Capitalize => (TypeId::STRING_CAPITALIZE, TypeId::STRING_GENERIC), + Intrinsic::Uncapitalize => (TypeId::STRING_UNCAPITALIZE, TypeId::STRING_GENERIC), + Intrinsic::NoInfer => (TypeId::NO_INFER, TypeId::T_TYPE), + Intrinsic::Literal => (TypeId::LITERAL_RESTRICTION, TypeId::T_TYPE), + Intrinsic::LessThan => { + let arguments = GenericArguments::ExplicitRestrictions(crate::Map::from_iter([ + (TypeId::NUMBER_FLOOR_GENERIC, (TypeId::NEG_INFINITY, SpanWithSource::NULL)), + (TypeId::NUMBER_CEILING_GENERIC, (argument, SpanWithSource::NULL)), + ])); + + return types.register_type(Type::PartiallyAppliedGenerics( + PartiallyAppliedGenerics { on: TypeId::EXCLUSIVE_RANGE, arguments }, + )); + } + Intrinsic::GreaterThan => { + let arguments = GenericArguments::ExplicitRestrictions(crate::Map::from_iter([ + (TypeId::NUMBER_FLOOR_GENERIC, (argument, SpanWithSource::NULL)), + (TypeId::NUMBER_CEILING_GENERIC, (TypeId::INFINITY, SpanWithSource::NULL)), + ])); + + return types.register_type(Type::PartiallyAppliedGenerics( + PartiallyAppliedGenerics { on: TypeId::EXCLUSIVE_RANGE, arguments }, + )); + } + Intrinsic::MultipleOf => (TypeId::MULTIPLE_OF, TypeId::NUMBER_FLOOR_GENERIC), + Intrinsic::Exclusive => (TypeId::EXCLUSIVE_RESTRICTION, TypeId::T_TYPE), + Intrinsic::Not => { + // Double negation + if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::NOT_RESTRICTION, + arguments: GenericArguments::ExplicitRestrictions(args), + }) = types.get_type_by_id(argument) + { + return args.get(&TypeId::T_TYPE).unwrap().0; + } + + (TypeId::NOT_RESTRICTION, TypeId::T_TYPE) + } + Intrinsic::CaseInsensitive => (TypeId::CASE_INSENSITIVE, TypeId::STRING_GENERIC), + }; + let arguments = GenericArguments::ExplicitRestrictions(crate::Map::from_iter([( + to_pair, + (argument, SpanWithSource::NULL), + )])); + + types.register_type(Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on, arguments })) +} diff --git a/checker/src/types/logical.rs b/checker/src/types/logical.rs index 24d377d9..cc641db0 100644 --- a/checker/src/types/logical.rs +++ b/checker/src/types/logical.rs @@ -51,7 +51,7 @@ impl From for LogicalOrValid { // pub enum MissingOrToCalculate { // /// Doesn't contain request // Missing, -// /// From [`TypeId::ERROR_TYPE`] +// /// From [`TypeId::UNIMPLEMENTED_ERROR_TYPE`] // Error, // /// From [`TypeId::ANY_TYPE`] // Infer { on: TypeId }, diff --git a/checker/src/types/mod.rs b/checker/src/types/mod.rs index 8d3dab29..a4bd6d90 100644 --- a/checker/src/types/mod.rs +++ b/checker/src/types/mod.rs @@ -33,7 +33,7 @@ pub use crate::features::objects::SpecialObject; use crate::{ context::InformationChain, events::RootReference, - features::operations::{CanonicalEqualityAndInequality, MathematicalAndBitwise, PureUnary}, + features::operations::{CanonicalEqualityAndInequality, MathematicalAndBitwise}, subtyping::SliceArguments, Decidable, FunctionId, }; @@ -61,6 +61,7 @@ impl TypeId { /// Not to be confused with [`TypeId::NEVER_TYPE`] pub const ERROR_TYPE: Self = Self(0); pub const UNIMPLEMENTED_ERROR_TYPE: Self = Self::ERROR_TYPE; + pub const IS_ASSIGNED_VALUE_LATER: Self = Self::ERROR_TYPE; pub const NEVER_TYPE: Self = Self(1); @@ -90,72 +91,84 @@ impl TypeId { /// This points to the ??? pub const SYMBOL_TYPE: Self = Self(15); - /// For more direct stuff and the rules + /// For direct constant and the rules pub const TRUE: Self = Self(16); pub const FALSE: Self = Self(17); pub const ZERO: Self = Self(18); pub const ONE: Self = Self(19); pub const NAN: Self = Self(20); - pub const EMPTY_STRING: Self = Self(21); + pub const NEG_INFINITY: Self = Self(21); + pub const INFINITY: Self = Self(22); + pub const FLOAT_MIN: Self = Self(23); + pub const FLOAT_MAX: Self = Self(24); + pub const FLOAT_EPSILON: Self = Self(25); + /// For bitwise negation + pub const MAX_U32: Self = Self(26); + /// "" + pub const EMPTY_STRING: Self = Self(27); /// Shortcut for inferred this /// TODO remove - pub const ANY_INFERRED_FREE_THIS: Self = Self(22); + pub const ANY_INFERRED_FREE_THIS: Self = Self(28); /// - pub const NEW_TARGET_ARG: Self = Self(23); + pub const NEW_TARGET_ARG: Self = Self(29); - pub const IMPORT_META: Self = Self(24); + pub const IMPORT_META: Self = Self(30); // known symbols /// - pub const SYMBOL_ITERATOR: Self = Self(25); + pub const SYMBOL_ITERATOR: Self = Self(31); /// - pub const SYMBOL_ASYNC_ITERATOR: Self = Self(26); + pub const SYMBOL_ASYNC_ITERATOR: Self = Self(32); /// - pub const SYMBOL_HAS_INSTANCE: Self = Self(27); + pub const SYMBOL_HAS_INSTANCE: Self = Self(33); /// - pub const SYMBOL_TO_PRIMITIVE: Self = Self(28); + pub const SYMBOL_TO_PRIMITIVE: Self = Self(34); // TSC intrinsics - pub const STRING_GENERIC: Self = Self(29); - pub const STRING_UPPERCASE: Self = Self(30); - pub const STRING_LOWERCASE: Self = Self(31); - pub const STRING_CAPITALIZE: Self = Self(32); - pub const STRING_UNCAPITALIZE: Self = Self(33); - pub const NO_INFER: Self = Self(34); + pub const STRING_GENERIC: Self = Self(35); + pub const STRING_UPPERCASE: Self = Self(36); + pub const STRING_LOWERCASE: Self = Self(37); + pub const STRING_CAPITALIZE: Self = Self(38); + pub const STRING_UNCAPITALIZE: Self = Self(39); + pub const NO_INFER: Self = Self(40); /// Might be a special type in TSC - pub const READONLY_RESTRICTION: Self = Self(35); + pub const READONLY_RESTRICTION: Self = Self(41); /// For mapped types - pub const NON_OPTIONAL_KEY_ARGUMENT: Self = Self(36); + pub const NON_OPTIONAL_KEY_ARGUMENT: Self = Self(42); /// For mapped types - pub const WRITABLE_KEY_ARGUMENT: Self = Self(37); + pub const WRITABLE_KEY_ARGUMENT: Self = Self(43); // Ezno intrinsics - /// Used in [`Self::LESS_THAN`], [`Self::LESS_THAN`] and [`Self::MULTIPLE_OF`] - pub const NUMBER_GENERIC: Self = Self(38); - pub const LESS_THAN: Self = Self(39); - pub const GREATER_THAN: Self = Self(40); - pub const MULTIPLE_OF: Self = Self(41); - pub const NOT_NOT_A_NUMBER: Self = Self(42); - pub const NUMBER_BUT_NOT_NOT_A_NUMBER: Self = Self(43); + /// Used in [`Self::INCLUSIVE_RANGE`], [`Self::EXCLUSIVE_RANGE`] and [`Self::MULTIPLE_OF`] + pub const NUMBER_FLOOR_GENERIC: Self = Self(44); + pub const NUMBER_CEILING_GENERIC: Self = Self(45); + pub const INCLUSIVE_RANGE: Self = Self(46); + pub const EXCLUSIVE_RANGE: Self = Self(47); + pub const MULTIPLE_OF: Self = Self(48); + pub const NOT_NOT_A_NUMBER: Self = Self(49); + pub const NUMBER_BUT_NOT_NOT_A_NUMBER: Self = Self(50); - pub const LITERAL_RESTRICTION: Self = Self(44); - pub const EXCLUSIVE_RESTRICTION: Self = Self(45); - pub const NOT_RESTRICTION: Self = Self(46); + pub const LITERAL_RESTRICTION: Self = Self(51); + pub const EXCLUSIVE_RESTRICTION: Self = Self(52); + pub const NOT_RESTRICTION: Self = Self(53); /// This is needed for the TSC string intrinsics - pub const CASE_INSENSITIVE: Self = Self(47); + pub const CASE_INSENSITIVE: Self = Self(54); /// WIP - pub const OPEN_BOOLEAN_TYPE: Self = Self(48); - pub const OPEN_NUMBER_TYPE: Self = Self(49); + pub const OPEN_BOOLEAN_TYPE: Self = Self(55); + pub const OPEN_NUMBER_TYPE: Self = Self(56); + + /// For `+` operator + pub const STRING_OR_NUMBER: Self = Self(57); /// Above add one (because [`TypeId`] starts at zero). Used to assert that the above is all correct - pub(crate) const INTERNAL_TYPE_COUNT: usize = 50; + pub(crate) const INTERNAL_TYPE_COUNT: usize = 58; } #[derive(Debug, binary_serialize_derive::BinarySerializable)] @@ -356,23 +369,21 @@ impl Type { /// - Some of these can be specialised, others are only created via event specialisation /// - Note that no || and && etc. This is handled using [`Constructor::ConditionalResult`] +/// - Unary operations are encoded via [`Constructor::BinaryOperator`] equivalents #[derive(Clone, Debug, binary_serialize_derive::BinarySerializable)] pub enum Constructor { - // TODO separate add? BinaryOperator { lhs: TypeId, operator: MathematicalAndBitwise, rhs: TypeId, + /// for add + number intrinsics + result: TypeId, }, CanonicalRelationOperator { lhs: TypeId, operator: CanonicalEqualityAndInequality, rhs: TypeId, }, - UnaryOperator { - operator: PureUnary, - operand: TypeId, - }, /// JS type based operations TypeOperator(TypeOperator), /// TS operation @@ -417,7 +428,6 @@ impl Constructor { | Constructor::Image { result, .. } => Some(*result), Constructor::BinaryOperator { .. } | Constructor::CanonicalRelationOperator { .. } - | Constructor::UnaryOperator { .. } | Constructor::TypeExtends(_) | Constructor::TypeOperator(_) => None, // TODO or symbol @@ -426,6 +436,22 @@ impl Constructor { } } +#[must_use] +pub fn as_logical_not(constructor: &Constructor, types: &TypeStore) -> Option { + // TODO technically any falsy, truthy reverse pair is okay + if let Constructor::ConditionalResult { + condition, + truthy_result: TypeId::FALSE, + otherwise_result: TypeId::TRUE, + result_union: _, + } = constructor + { + Some(helpers::get_origin(*condition, types)) + } else { + None + } +} + #[must_use] pub fn as_logical_and(constructor: &Constructor, types: &TypeStore) -> Option<(TypeId, TypeId)> { if let Constructor::ConditionalResult { @@ -535,6 +561,7 @@ impl TypeExtends { } } + #[must_use] pub fn equal_to_rhs(&self, rhs: TypeId, types: &TypeStore) -> bool { if let Ok(TypeExtends { item, extends }) = Self::from_type(rhs, types) { // TODO get origin on item @@ -645,45 +672,8 @@ pub(crate) fn get_constraint(on: TypeId, types: &TypeStore) -> Option { match types.get_type_by_id(on) { Type::RootPolyType(nature) => Some(nature.get_constraint()), Type::Constructor(constructor) => match constructor.clone() { - Constructor::BinaryOperator { lhs, operator, rhs } => { - if let MathematicalAndBitwise::Add = operator { - let lhs = get_larger_type(lhs, types); - let rhs = get_larger_type(rhs, types); - // TODO these need to be generated - if let (TypeId::NUMBER_TYPE, TypeId::NUMBER_TYPE) = (lhs, rhs) { - Some(TypeId::NUMBER_TYPE) - } else if let (TypeId::STRING_TYPE, _) | (_, TypeId::STRING_TYPE) = (lhs, rhs) { - Some(TypeId::STRING_TYPE) - } else { - crate::utilities::notify!("lhs = {:?}", types.get_type_by_id(lhs)); - crate::utilities::notify!("TODO use existing conditional"); - Some(TypeId::NUMBER_TYPE) - } - } else { - Some(TypeId::NUMBER_TYPE) - } - } - Constructor::UnaryOperator { operand: _, operator } => { - Some(match operator { - PureUnary::LogicalNot => TypeId::BOOLEAN_TYPE, - PureUnary::Negation | PureUnary::BitwiseNot => TypeId::NUMBER_TYPE, - }) - // if *constraint == TypeId::ANY_TYPE && mutable_context { - // let (operand, operator) = (operand.clone(), operator.clone()); - // let constraint = to(self, data); - // self.modify_type( - // on, - // Some(Type::Constructor(Constructor::UnaryOperator { - // operator, - // operand, - // // })), - // ); - // Some(constraint) - // } else { - // Some(*constraint) - // } - } - Constructor::Awaited { on: _, result } + Constructor::BinaryOperator { result, .. } + | Constructor::Awaited { on: _, result } | Constructor::Image { on: _, with: _, result } => Some(result), Constructor::Property { on: _, under: _, result, mode: _ } => { // crate::utilities::notify!("Here, result of a property get"); @@ -722,7 +712,7 @@ pub fn get_larger_type(on: TypeId, types: &TypeStore) -> TypeId { if let Some(poly_base) = get_constraint(on, types) { poly_base } else if let Type::Constant(cst) = types.get_type_by_id(on) { - cst.get_backing_type_id() + cst.get_backing_type() } else { on } @@ -931,6 +921,8 @@ impl Counter { lhs: *value, operator: MathematicalAndBitwise::Add, rhs: TypeId::ONE, + // TODO could be greater than + result: TypeId::NUMBER_TYPE, })); } } @@ -942,6 +934,8 @@ impl Counter { lhs: ty, operator: MathematicalAndBitwise::Add, rhs: current, + // TODO could be greater than + result: TypeId::NUMBER_TYPE, })); *self = Counter::AddTo(new); } @@ -963,108 +957,113 @@ impl Counter { } } -/// To fill in for TSC behavior for mapped types -#[must_use] -pub fn references_key_of(id: TypeId, types: &TypeStore) -> bool { - match types.get_type_by_id(id) { - Type::AliasTo { to, .. } => references_key_of(*to, types), - Type::Or(lhs, rhs) | Type::And(lhs, rhs) => { - references_key_of(*lhs, types) || references_key_of(*rhs, types) - } - Type::RootPolyType(c) => references_key_of(c.get_constraint(), types), - Type::Constructor(c) => { - if let Constructor::KeyOf(..) = c { - true - } else if let Constructor::BinaryOperator { lhs, rhs, operator: _ } = c { +pub(crate) mod helpers { + use super::{ + get_constraint, subtyping, Constructor, GenericArguments, GenericChain, InformationChain, + PartiallyAppliedGenerics, PolyNature, Type, TypeId, TypeStore, + }; + + /// To fill in for TSC behavior for mapped types + #[must_use] + pub fn references_key_of(id: TypeId, types: &TypeStore) -> bool { + match types.get_type_by_id(id) { + Type::AliasTo { to, .. } => references_key_of(*to, types), + Type::Or(lhs, rhs) | Type::And(lhs, rhs) => { references_key_of(*lhs, types) || references_key_of(*rhs, types) - } else { - // TODO might have missed something here - false } - } - Type::PartiallyAppliedGenerics(a) => { - if let GenericArguments::ExplicitRestrictions(ref e) = a.arguments { - e.0.iter().any(|(_, (lhs, _))| references_key_of(*lhs, types)) - } else { - false + Type::RootPolyType(c) => references_key_of(c.get_constraint(), types), + Type::Constructor(c) => { + if let Constructor::KeyOf(..) = c { + true + } else if let Constructor::BinaryOperator { lhs, rhs, operator: _, result: _ } = c { + references_key_of(*lhs, types) || references_key_of(*rhs, types) + } else { + crate::utilities::notify!("TODO might have missed keyof {:?}", c); + false + } } + Type::PartiallyAppliedGenerics(a) => { + if let GenericArguments::ExplicitRestrictions(ref e) = a.arguments { + e.0.iter().any(|(_, (lhs, _))| references_key_of(*lhs, types)) + } else { + false + } + } + Type::Interface { .. } + | Type::Class { .. } + | Type::Constant(_) + | Type::Narrowed { .. } + | Type::FunctionReference(_) + | Type::Object(_) + | Type::SpecialObject(_) => false, } - Type::Interface { .. } - | Type::Class { .. } - | Type::Constant(_) - | Type::Narrowed { .. } - | Type::FunctionReference(_) - | Type::Object(_) - | Type::SpecialObject(_) => false, } -} -#[allow(clippy::match_like_matches_macro)] -#[must_use] -pub fn type_is_error(ty: TypeId, types: &TypeStore) -> bool { - if ty == TypeId::ERROR_TYPE { - true - } else if let Type::RootPolyType(PolyNature::Error(_)) = types.get_type_by_id(ty) { - true - } else { - false + #[allow(clippy::match_like_matches_macro)] + #[must_use] + pub fn _type_is_error(ty: TypeId, types: &TypeStore) -> bool { + if ty == TypeId::UNIMPLEMENTED_ERROR_TYPE { + true + } else if let Type::RootPolyType(PolyNature::Error(_)) = types.get_type_by_id(ty) { + true + } else { + false + } } -} -/// TODO want to skip mapped generics because that would break subtyping -#[must_use] -pub fn get_conditional(ty: TypeId, types: &TypeStore) -> Option<(TypeId, TypeId, TypeId)> { - match types.get_type_by_id(ty) { - Type::Constructor(crate::types::Constructor::ConditionalResult { - condition, - truthy_result, - otherwise_result, - result_union: _, - }) => Some((*condition, *truthy_result, *otherwise_result)), - Type::Or(left, right) => Some((TypeId::OPEN_BOOLEAN_TYPE, *left, *right)), - // For reasons ! - Type::RootPolyType(PolyNature::MappedGeneric { .. }) => None, - _ => { - if let Some(constraint) = get_constraint(ty, types) { - get_conditional(constraint, types) - } else { - None + /// TODO want to skip mapped generics because that would break subtyping + #[must_use] + pub fn get_conditional(ty: TypeId, types: &TypeStore) -> Option<(TypeId, TypeId, TypeId)> { + match types.get_type_by_id(ty) { + Type::Constructor(crate::types::Constructor::ConditionalResult { + condition, + truthy_result, + otherwise_result, + result_union: _, + }) => Some((*condition, *truthy_result, *otherwise_result)), + Type::Or(left, right) => Some((TypeId::OPEN_BOOLEAN_TYPE, *left, *right)), + // For reasons ! + Type::RootPolyType(PolyNature::MappedGeneric { .. }) => None, + _ => { + if let Some(constraint) = get_constraint(ty, types) { + get_conditional(constraint, types) + } else { + None + } } } } -} -/// TODO wip -#[must_use] -pub fn is_pseudo_continous((ty, generics): (TypeId, GenericChain), types: &TypeStore) -> bool { - if let TypeId::NUMBER_TYPE | TypeId::STRING_TYPE = ty { - true - } else if let Some(arg) = generics.as_ref().and_then(|args| args.get_single_argument(ty)) { - is_pseudo_continous((arg, generics), types) - } else { - let ty = types.get_type_by_id(ty); - if let Type::Or(left, right) = ty { - is_pseudo_continous((*left, generics), types) - || is_pseudo_continous((*right, generics), types) - } else if let Type::And(left, right) = ty { - is_pseudo_continous((*left, generics), types) - && is_pseudo_continous((*right, generics), types) - } else if let Type::RootPolyType(PolyNature::MappedGeneric { extends, .. }) = ty { - is_pseudo_continous((*extends, generics), types) + /// TODO wip + #[must_use] + pub fn is_pseudo_continous((ty, generics): (TypeId, GenericChain), types: &TypeStore) -> bool { + if let TypeId::NUMBER_TYPE | TypeId::STRING_TYPE = ty { + true + } else if let Some(arg) = generics.as_ref().and_then(|args| args.get_single_argument(ty)) { + is_pseudo_continous((arg, generics), types) } else { - false + let ty = types.get_type_by_id(ty); + if let Type::Or(left, right) = ty { + is_pseudo_continous((*left, generics), types) + || is_pseudo_continous((*right, generics), types) + } else if let Type::And(left, right) = ty { + is_pseudo_continous((*left, generics), types) + && is_pseudo_continous((*right, generics), types) + } else if let Type::RootPolyType(PolyNature::MappedGeneric { extends, .. }) = ty { + is_pseudo_continous((*extends, generics), types) + } else { + false + } } } -} -#[must_use] -pub fn is_inferrable_type(ty: TypeId) -> bool { - matches!(ty, TypeId::ANY_TO_INFER_TYPE | TypeId::OBJECT_TYPE) -} - -pub(crate) mod helpers { - use super::{subtyping, InformationChain, Type, TypeId, TypeStore}; + #[must_use] + pub fn is_inferrable_type(ty: TypeId) -> bool { + matches!(ty, TypeId::ANY_TO_INFER_TYPE | TypeId::OBJECT_TYPE) + } + /// For quick checking + #[must_use] pub fn simple_subtype( expr_ty: TypeId, to_satisfy: TypeId, @@ -1075,7 +1074,7 @@ pub(crate) mod helpers { already_checked: Default::default(), mode: Default::default(), contributions: Default::default(), - others: subtyping::SubTypingOptions { allow_errors: false }, + others: subtyping::SubTypingOptions { allow_errors: true }, object_constraints: None, }; @@ -1091,4 +1090,18 @@ pub(crate) mod helpers { ty } } + + /// Temp fix for equality of narrowing stuff + pub fn is_not_of_constant(ty: TypeId, types: &TypeStore) -> bool { + if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::NOT_RESTRICTION, + arguments, + }) = types.get_type_by_id(ty) + { + let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); + types.get_type_by_id(inner).is_constant() + } else { + false + } + } } diff --git a/checker/src/types/printing.rs b/checker/src/types/printing.rs index 0490fae6..a9688428 100644 --- a/checker/src/types/printing.rs +++ b/checker/src/types/printing.rs @@ -87,9 +87,11 @@ pub fn print_type_into_buf( buf.push_str(" | "); print_type_into_buf(*b, buf, cycles, args, types, info, debug); } - Type::Narrowed { narrowed_to, .. } => { + Type::Narrowed { narrowed_to, from } => { if debug { - buf.push_str("(narrowed) "); + buf.push_str("(narrowed from "); + print_type_into_buf(*from, buf, cycles, args, types, info, debug); + buf.push_str(") "); } print_type_into_buf(*narrowed_to, buf, cycles, args, types, info, debug); } @@ -146,6 +148,9 @@ pub fn print_type_into_buf( } } PolyNature::InferGeneric { name, extends } => { + if debug { + write!(buf, "[IG {}] @ ", ty.0).unwrap(); + } buf.push_str("infer "); buf.push_str(name); if *extends != TypeId::ANY_TYPE { @@ -195,6 +200,26 @@ pub fn print_type_into_buf( } }, Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on, arguments }) => { + // TypeId::INCLUSIVE_RANGE | + if let TypeId::EXCLUSIVE_RANGE = *on { + // let inclusive = *on == TypeId::INCLUSIVE_RANGE; + let floor = + arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC).unwrap(); + let ceiling = + arguments.get_structure_restriction(TypeId::NUMBER_CEILING_GENERIC).unwrap(); + if let TypeId::NEG_INFINITY = floor { + buf.push_str("LessThan<"); + print_type_into_buf(ceiling, buf, cycles, args, types, info, debug); + buf.push('>'); + return; + } else if let TypeId::INFINITY = ceiling { + buf.push_str("GreaterThan<"); + print_type_into_buf(floor, buf, cycles, args, types, info, debug); + buf.push('>'); + return; + } + } + if debug { write!(buf, "SG({:?})(", ty.0).unwrap(); print_type_into_buf(*on, buf, cycles, args, types, info, debug); @@ -262,7 +287,7 @@ pub fn print_type_into_buf( result_union: _, } => { if let (TypeId::NEVER_TYPE, Ok(crate::types::TypeExtends { item, extends })) = - (TypeId::NEVER_TYPE, crate::types::TypeExtends::from_type(*condition, types)) + (*otherwise_result, crate::types::TypeExtends::from_type(*condition, types)) { buf.push_str("asserts "); print_type_into_buf(item, buf, cycles, args, types, info, debug); @@ -386,7 +411,7 @@ pub fn print_type_into_buf( } } constructor if debug => match constructor { - Constructor::BinaryOperator { lhs, operator, rhs } => { + Constructor::BinaryOperator { lhs, operator, rhs, result: _ } => { print_type_into_buf(*lhs, buf, cycles, args, types, info, debug); write!(buf, " {operator:?} ").unwrap(); print_type_into_buf(*rhs, buf, cycles, args, types, info, debug); @@ -403,10 +428,6 @@ pub fn print_type_into_buf( } print_type_into_buf(*rhs, buf, cycles, args, types, info, debug); } - Constructor::UnaryOperator { operator, operand } => { - write!(buf, "{operator:?} ").unwrap(); - print_type_into_buf(*operand, buf, cycles, args, types, info, debug); - } Constructor::TypeOperator(to) => { write!(buf, "TypeOperator.{to:?}").unwrap(); } diff --git a/checker/src/types/properties/access.rs b/checker/src/types/properties/access.rs index d5bf2db7..84d52df1 100644 --- a/checker/src/types/properties/access.rs +++ b/checker/src/types/properties/access.rs @@ -9,7 +9,8 @@ use crate::{ generics::{ contributions::CovariantContribution, generic_type_arguments::GenericArguments, }, - get_conditional, get_constraint, is_inferrable_type, is_pseudo_continous, + get_constraint, + helpers::{get_conditional, is_inferrable_type, is_pseudo_continous}, logical::{ BasedOnKey, Invalid, Logical, LogicalOrValid, NeedsCalculation, PossibleLogical, PropertyOn, @@ -542,12 +543,14 @@ pub(crate) fn get_property_unbound( } } Type::Constant(Constant::String(s)) if under.is_equal_to("length") => { - // TODO temp TypeId::NUMBER_GENERIC for slice member + // TODO temp TypeId::NUMBER_FLOOR_GENERIC for slice member let count = s.chars().count(); Ok(Logical::BasedOnKey(BasedOnKey::Left { - value: Box::new(Logical::Pure(PropertyValue::Value(TypeId::NUMBER_GENERIC))), + value: Box::new(Logical::Pure(PropertyValue::Value( + TypeId::NUMBER_FLOOR_GENERIC, + ))), key_arguments: crate::Map::from_iter([( - TypeId::NUMBER_GENERIC, + TypeId::NUMBER_FLOOR_GENERIC, (CovariantContribution::Number(count as f64), 0), )]), }) @@ -563,7 +566,7 @@ pub(crate) fn get_property_unbound( .map(LogicalOrValid::Logical) .ok_or(Invalid(on)) .or_else(|_| { - let backing_type = cst.get_backing_type_id(); + let backing_type = cst.get_backing_type(); get_property_on_type_unbound( (backing_type, on_type_arguments), (publicity, under, under_type_arguments), @@ -734,7 +737,7 @@ pub(crate) fn get_property( ); { - crate::utilities::notify!("Access result {:?}", result); + crate::utilities::notify!("Access {:?} result {:?}", under, result); } match result { @@ -938,13 +941,18 @@ fn resolve_property_on_logical( }; let result = getter.call(Vec::new(), input, environment, (behavior, diagnostics), types); + if let Ok(res) = result { + crate::utilities::notify!("{:?}", res.result); + let application_result = application_result_to_return_type(res.result, environment, types); + + crate::utilities::notify!("{:?}", application_result); Some((PropertyKind::Getter, application_result)) } else { - crate::utilities::notify!("TODO merge calling"); - Some((PropertyKind::Getter, TypeId::ERROR_TYPE)) + crate::utilities::notify!("Here"); + Some((PropertyKind::Getter, TypeId::UNIMPLEMENTED_ERROR_TYPE)) } } PropertyValue::Setter(_) => { @@ -1172,7 +1180,7 @@ pub(crate) fn proxy_access( Some((PropertyKind::Getter, application_result)) } else { crate::utilities::notify!("TODO merge calling"); - Some((PropertyKind::Getter, TypeId::ERROR_TYPE)) + Some((PropertyKind::Getter, TypeId::UNIMPLEMENTED_ERROR_TYPE)) } } else { get_property( diff --git a/checker/src/types/properties/list.rs b/checker/src/types/properties/list.rs index 29fe0b12..9e97c437 100644 --- a/checker/src/types/properties/list.rs +++ b/checker/src/types/properties/list.rs @@ -109,7 +109,7 @@ pub fn get_properties_on_single_type( get_properties_on_single_type(*to, types, info, filter_enumerable, filter_type) } Type::Constant(c) => get_properties_on_single_type( - c.get_backing_type_id(), + c.get_backing_type(), types, info, filter_enumerable, @@ -273,15 +273,15 @@ pub fn get_properties_on_single_type2( // info, // types, // ); - crate::utilities::notify!("Cannot get all properties on {:?}", base); - Default::default() + crate::utilities::notify!("Getting on generic, losing generic {:?}", base); + get_properties_on_single_type2((*on, base_arguments), types, info, filter_type) } } Type::Narrowed { narrowed_to: to, .. } | Type::AliasTo { to, .. } => { get_properties_on_single_type2((*to, base_arguments), types, info, filter_type) } Type::Constant(c) => get_properties_on_single_type2( - (c.get_backing_type_id(), base_arguments), + (c.get_backing_type(), base_arguments), types, info, filter_type, @@ -300,29 +300,48 @@ pub fn get_properties_on_single_type2( } } +/// Just for diagnostic printing +#[must_use] pub fn get_property_key_names_on_a_single_type( base: TypeId, types: &TypeStore, - environment: &mut crate::Environment, + environment: &crate::Environment, ) -> Vec { - let is_special = matches!( - types.get_type_by_id(base), - Type::SpecialObject(_) - | Type::Constructor(_) - | Type::RootPolyType(_) - | Type::Or(..) - | Type::PartiallyAppliedGenerics(_) - | Type::Constant(_) - | Type::AliasTo { .. } - | Type::FunctionReference(_) - | Type::And(_, _) - ); - if is_special { - return vec![]; + match types.get_type_by_id(base) { + Type::Object(ObjectNature::AnonymousTypeAnnotation(properties)) => properties + .iter() + .map(|(_, property, _)| super::get_property_as_string(property, types, environment)) + .collect(), + Type::Interface { .. } | Type::Class { .. } | Type::Object(_) => environment + .get_chain_of_info() + .filter_map(|info| info.current_properties.get(&base).map(|v| v.iter())) + .flatten() + .map(|(_, property, _)| super::get_property_as_string(property, types, environment)) + .collect(), + Type::Constant(r) => { + get_property_key_names_on_a_single_type(r.get_backing_type(), types, environment) + } + Type::SpecialObject(crate::types::SpecialObject::Function(..)) + | Type::FunctionReference(_) => { + get_property_key_names_on_a_single_type(TypeId::FUNCTION_TYPE, types, environment) + } + Type::Narrowed { narrowed_to: to, .. } | Type::AliasTo { to, .. } => { + get_property_key_names_on_a_single_type(*to, types, environment) + } + Type::PartiallyAppliedGenerics(crate::types::PartiallyAppliedGenerics { + on, + arguments: _, + }) => { + // TODO mapped types + get_property_key_names_on_a_single_type(*on, types, environment) + } + Type::Constructor(_) | Type::RootPolyType(_) => { + let backing = crate::types::get_constraint(base, types).unwrap(); + get_property_key_names_on_a_single_type(backing, types, environment) + } + t @ (Type::SpecialObject(_) | Type::Or(..) | Type::And(_, _)) => { + crate::utilities::notify!("Cannot get all propertie keys on {:?}", t); + Default::default() + } } - - get_properties_on_single_type(base, types, environment, false, TypeId::ANY_TYPE) - .into_iter() - .map(|property| super::get_property_as_string(&property.1, types, environment)) - .collect() } diff --git a/checker/src/types/properties/mod.rs b/checker/src/types/properties/mod.rs index c1a0b75c..18e34c69 100644 --- a/checker/src/types/properties/mod.rs +++ b/checker/src/types/properties/mod.rs @@ -101,7 +101,8 @@ impl<'a> PropertyKey<'a> { // Okay I think? PropertyKey::Type(ty) } - Constant::NaN => todo!(), + Constant::NaN => PropertyKey::String(Cow::Borrowed("NaN")), + Constant::Undefined => PropertyKey::String(Cow::Borrowed("undefined")), } } else { PropertyKey::Type(ty) diff --git a/checker/src/types/store.rs b/checker/src/types/store.rs index a72698fc..f6bcb3bb 100644 --- a/checker/src/types/store.rs +++ b/checker/src/types/store.rs @@ -1,21 +1,20 @@ use std::collections::{HashMap, HashSet}; -use crate::{features::regexp::RegExp, types::intrinsics::Intrinsic, Constant, Map as SmallMap}; use source_map::{Nullable, Span, SpanWithSource}; use crate::{ - features::{functions::ClosureId, objects::SpecialObject}, - types::{ - functions::{FunctionBehavior, FunctionType}, - logical::{Logical, LogicalOrValid}, - PolyNature, Type, - }, - Environment, FunctionId, TypeId, + features::{functions::ClosureId, objects::SpecialObject, regexp::RegExp}, + Constant, Environment, FunctionId, Map as SmallMap, TypeId, }; use super::{ - generics::generic_type_arguments::GenericArguments, get_constraint, properties::PropertyKey, - Constructor, LookUpGeneric, LookUpGenericMap, PartiallyAppliedGenerics, TypeExtends, + functions::{FunctionBehavior, FunctionType}, + generics::generic_type_arguments::GenericArguments, + get_constraint, + logical::{Logical, LogicalOrValid}, + properties::PropertyKey, + Constructor, LookUpGeneric, LookUpGenericMap, PartiallyAppliedGenerics, PolyNature, Type, + TypeExtends, }; /// Holds all the types. Eventually may be split across modules @@ -41,9 +40,9 @@ pub struct TypeStore { impl Default for TypeStore { fn default() -> Self { - // These have to be in the order of TypeId + // These have to be in the order of the `impl TypeId` let types = vec![ - // TODO will `TypeId::ANY_TYPE` cause any problems + // TODO will `TypeId::ANY_TYPE` cause any problems? Type::RootPolyType(PolyNature::Error(TypeId::ANY_TYPE)), Type::Interface { name: "never".to_owned(), parameters: None, extends: None }, Type::Interface { name: "any".to_owned(), parameters: None, extends: None }, @@ -51,7 +50,7 @@ impl Default for TypeStore { Type::Class { name: "number".to_owned(), type_parameters: None }, Type::Class { name: "string".to_owned(), type_parameters: None }, // sure? - Type::Interface { name: "undefined".to_owned(), parameters: None, extends: None }, + Type::Constant(Constant::Undefined), Type::SpecialObject(SpecialObject::Null), // `void` type. Has special subtyping in returns Type::AliasTo { to: TypeId::UNDEFINED_TYPE, name: "void".into(), parameters: None }, @@ -65,7 +64,7 @@ impl Default for TypeStore { Type::Interface { name: "object".to_owned(), parameters: None, extends: None }, Type::Class { name: "Function".to_owned(), type_parameters: None }, Type::Class { name: "RegExp".to_owned(), type_parameters: None }, - Type::Or(TypeId::STRING_TYPE, TypeId::NUMBER_TYPE), + Type::Class { name: "Symbol".to_owned(), type_parameters: None }, // true Type::Constant(Constant::Boolean(true)), // false @@ -76,6 +75,15 @@ impl Default for TypeStore { Type::Constant(Constant::Number(1.into())), // NaN Type::Constant(Constant::NaN), + Type::Constant(Constant::Number(f64::NEG_INFINITY.try_into().unwrap())), + Type::Constant(Constant::Number(f64::INFINITY.try_into().unwrap())), + Type::Constant(Constant::Number(f64::MIN.try_into().unwrap())), + Type::Constant(Constant::Number(f64::MAX.try_into().unwrap())), + Type::Constant(Constant::Number(f64::EPSILON.try_into().unwrap())), + Type::Constant(Constant::Number({ + const THIRTY_TWO_ONE_BITS: i32 = -1i32; + THIRTY_TWO_ONE_BITS.into() + })), // "" Type::Constant(Constant::String(String::new())), // inferred this free variable shortcut @@ -140,20 +148,30 @@ impl Default for TypeStore { name: "T".into(), extends: TypeId::NUMBER_TYPE, }), + Type::RootPolyType(PolyNature::StructureGeneric { + name: "U".into(), + extends: TypeId::NUMBER_TYPE, + }), Type::AliasTo { to: TypeId::NUMBER_TYPE, - name: "LessThan".into(), - parameters: Some(vec![TypeId::NUMBER_GENERIC]), + name: "InclusiveRange".into(), + parameters: Some(vec![ + TypeId::NUMBER_FLOOR_GENERIC, + TypeId::NUMBER_CEILING_GENERIC, + ]), }, Type::AliasTo { to: TypeId::NUMBER_TYPE, - name: "GreaterThan".into(), - parameters: Some(vec![TypeId::NUMBER_GENERIC]), + name: "ExclusiveRange".into(), + parameters: Some(vec![ + TypeId::NUMBER_FLOOR_GENERIC, + TypeId::NUMBER_CEILING_GENERIC, + ]), }, Type::AliasTo { to: TypeId::NUMBER_TYPE, name: "MultipleOf".into(), - parameters: Some(vec![TypeId::NUMBER_GENERIC]), + parameters: Some(vec![TypeId::NUMBER_FLOOR_GENERIC]), }, // Intermediate for the below Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { @@ -187,6 +205,7 @@ impl Default for TypeStore { }, Type::RootPolyType(PolyNature::Open(TypeId::BOOLEAN_TYPE)), Type::RootPolyType(PolyNature::Open(TypeId::NUMBER_TYPE)), + Type::Or(TypeId::STRING_TYPE, TypeId::NUMBER_TYPE), ]; // Check that above is correct, TODO eventually a macro @@ -219,13 +238,10 @@ impl TypeStore { Constant::String(s) if s.is_empty() => TypeId::EMPTY_STRING, Constant::Number(number) if number == 0f64 => TypeId::ZERO, Constant::Number(number) if number == 1f64 => TypeId::ONE, - Constant::Boolean(value) => { - if value { - TypeId::TRUE - } else { - TypeId::FALSE - } - } + Constant::Number(number) if number == f64::NEG_INFINITY => TypeId::NEG_INFINITY, + Constant::Number(number) if number == f64::INFINITY => TypeId::INFINITY, + Constant::Boolean(true) => TypeId::TRUE, + Constant::Boolean(false) => TypeId::FALSE, Constant::NaN => TypeId::NAN, _ => { let ty = Type::Constant(constant); @@ -381,12 +397,16 @@ impl TypeStore { otherwise_result } else if truthy_result == TypeId::TRUE && otherwise_result == TypeId::FALSE { condition - } else if let Type::Constructor(Constructor::UnaryOperator { - operator: crate::features::operations::PureUnary::LogicalNot, - operand: reversed_condition, + } else if let Type::Constructor(Constructor::ConditionalResult { + condition, + // TODO technically any falsy, truthy reverse pair is okay + truthy_result: TypeId::FALSE, + otherwise_result: TypeId::TRUE, + result_union: _, }) = self.get_type_by_id(condition) { - self.new_conditional_type(*reversed_condition, otherwise_result, truthy_result) + // Revese the condition + self.new_conditional_type(*condition, otherwise_result, truthy_result) } else { let ty = Type::Constructor(super::Constructor::ConditionalResult { condition, @@ -408,12 +428,13 @@ impl TypeStore { } /// Doesn't do constant compilation - pub(crate) fn new_logical_negation_type(&mut self, operand: TypeId) -> TypeId { - let ty = Type::Constructor(Constructor::UnaryOperator { - operator: crate::features::operations::PureUnary::LogicalNot, - operand, - }); - self.register_type(ty) + pub(crate) fn new_logical_negation_type(&mut self, condition: TypeId) -> TypeId { + self.register_type(Type::Constructor(super::Constructor::ConditionalResult { + condition, + truthy_result: TypeId::FALSE, + otherwise_result: TypeId::TRUE, + result_union: TypeId::BOOLEAN_TYPE, + })) } /// Doesn't evaluate events @@ -479,14 +500,14 @@ impl TypeStore { LogicalOrValid::Logical(Logical::Pure(ty)) => ty.as_get_type(self), value => { crate::utilities::notify!("value={:?}", value); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } // Logical::Or { .. } => todo!(), // Logical::Implies { .. } => todo!(), // Logical::BasedOnKey { .. } => todo!(), } } else { crate::utilities::notify!("Error: no index on type annotation"); - TypeId::ERROR_TYPE + TypeId::UNIMPLEMENTED_ERROR_TYPE } } } @@ -612,41 +633,4 @@ impl TypeStore { pub(crate) fn new_key_of(&mut self, of: TypeId) -> TypeId { self.register_type(Type::Constructor(Constructor::KeyOf(of))) } - - pub(crate) fn new_intrinsic(&mut self, intrinsic: &Intrinsic, argument: TypeId) -> TypeId { - let (on, to_pair) = match intrinsic { - Intrinsic::Uppercase => (TypeId::STRING_UPPERCASE, TypeId::STRING_GENERIC), - Intrinsic::Lowercase => (TypeId::STRING_LOWERCASE, TypeId::STRING_GENERIC), - Intrinsic::Capitalize => (TypeId::STRING_CAPITALIZE, TypeId::STRING_GENERIC), - Intrinsic::Uncapitalize => (TypeId::STRING_UNCAPITALIZE, TypeId::STRING_GENERIC), - Intrinsic::NoInfer => (TypeId::NO_INFER, TypeId::T_TYPE), - Intrinsic::Literal => (TypeId::LITERAL_RESTRICTION, TypeId::T_TYPE), - Intrinsic::LessThan => (TypeId::LESS_THAN, TypeId::NUMBER_GENERIC), - Intrinsic::GreaterThan => (TypeId::GREATER_THAN, TypeId::NUMBER_GENERIC), - Intrinsic::MultipleOf => (TypeId::MULTIPLE_OF, TypeId::NUMBER_GENERIC), - Intrinsic::Exclusive => (TypeId::EXCLUSIVE_RESTRICTION, TypeId::T_TYPE), - Intrinsic::Not => { - // Double negation - if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: TypeId::NOT_RESTRICTION, - arguments: GenericArguments::ExplicitRestrictions(args), - }) = self.get_type_by_id(argument) - { - return args.get(&TypeId::T_TYPE).unwrap().0; - } - - (TypeId::NOT_RESTRICTION, TypeId::T_TYPE) - } - Intrinsic::CaseInsensitive => (TypeId::CASE_INSENSITIVE, TypeId::STRING_GENERIC), - }; - let arguments = GenericArguments::ExplicitRestrictions(crate::Map::from_iter([( - to_pair, - (argument, ::NULL), - )])); - - self.register_type(Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on, - arguments, - })) - } } diff --git a/checker/src/types/subtyping.rs b/checker/src/types/subtyping.rs index f845791e..eb274d06 100644 --- a/checker/src/types/subtyping.rs +++ b/checker/src/types/subtyping.rs @@ -8,23 +8,22 @@ use crate::{ objects::{self, SpecialObject}, operations::MathematicalAndBitwise, }, - types::{ - generics::{ - chain::{GenericChain, GenericChainLink, SpecialGenericChainLink}, - contributions::{ContributionDepth, Contributions, CovariantContribution, TriMap}, - generic_type_arguments::GenericArguments, - }, - intrinsics::apply_string_intrinsic, - logical::{BasedOnKey, Logical, LogicalOrValid, NeedsCalculation, PropertyOn}, - printing::print_type, - properties::{get_properties_on_single_type2, get_property_unbound, Publicity}, - ObjectNature, Type, TypeStore, - }, Constant, Environment, PropertyValue, TypeId, }; use super::{ - get_constraint, properties::PropertyKey, Constructor, PartiallyAppliedGenerics, PolyNature, + generics::{ + chain::{GenericChain, GenericChainLink, SpecialGenericChainLink}, + contributions::{ContributionDepth, Contributions, CovariantContribution, TriMap}, + generic_type_arguments::GenericArguments, + }, + get_constraint, + intrinsics::{self, apply_string_intrinsic}, + logical::{BasedOnKey, Logical, LogicalOrValid, NeedsCalculation, PropertyOn}, + printing::print_type, + properties::PropertyKey, + properties::{get_properties_on_single_type2, get_property_unbound, Publicity}, + Constructor, ObjectNature, PartiallyAppliedGenerics, PolyNature, Type, TypeStore, }; pub use super::{NonEqualityReason, PropertyError}; @@ -254,19 +253,36 @@ pub(crate) fn type_is_subtype_with_generics( state.already_checked.push((base_type, ty)); } - let left_ty = types.get_type_by_id(base_type); - let right_ty = types.get_type_by_id(ty); + let supertype = types.get_type_by_id(base_type); + let subtype = types.get_type_by_id(ty); // Eager things - match right_ty { + match subtype { Type::Narrowed { narrowed_to: right, .. } | Type::AliasTo { to: right, .. } => { - return type_is_subtype_with_generics( + let result = type_is_subtype_with_generics( (base_type, base_type_arguments), (*right, ty_structure_arguments), state, information, types, ); + // Temp fix for narrowing constants + crate::utilities::notify!("{:?}", super::helpers::is_not_of_constant(*right, types)); + // SubTypeResult::IsNotSubType(_) + return if let (Type::Narrowed { from, .. }, _, true) = + (subtype, &result, super::helpers::is_not_of_constant(*right, types)) + { + crate::utilities::notify!("Here"); + type_is_subtype_with_generics( + (base_type, base_type_arguments), + (*from, ty_structure_arguments), + state, + information, + types, + ) + } else { + result + }; } Type::Or(left, right) => { let right = *right; @@ -294,7 +310,7 @@ pub(crate) fn type_is_subtype_with_generics( } // Type::And(left, right) => { // let left_is_operator_right_is_not = - // left_ty.is_operator(); + // supertype.is_operator(); // // edge cases on edge cases // // If any of these are true. Then do not perform constraint argument lookup @@ -334,10 +350,9 @@ pub(crate) fn type_is_subtype_with_generics( let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); // https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Set/Basic.html#Set.subset_compl_comm -> https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Set/Basic.html#Set.subset_compl_iff_disjoint_left - // Swapped let result = super::disjoint::types_are_disjoint( + base_type, inner, - ty, &mut state.already_checked, information, types, @@ -355,6 +370,7 @@ pub(crate) fn type_is_subtype_with_generics( t @ (Type::RootPolyType(..) | Type::Constructor(..)) => { if let Type::RootPolyType(PolyNature::Error(to)) = t { // (unless specified) treat as subtype as error would have already been thrown + crate::utilities::notify!("Here {:?}", state.others.allow_errors); return if state.others.allow_errors && *to == TypeId::ANY_TYPE { SubTypeResult::IsSubType } else { @@ -435,16 +451,16 @@ pub(crate) fn type_is_subtype_with_generics( // This is important that LHS is not operator let left_is_operator_right_is_not = - left_ty.is_operator() && !types.get_type_by_id(right_arg).is_operator(); + supertype.is_operator() && !types.get_type_by_id(right_arg).is_operator(); // edge cases on edge cases // If any of these are true. Then do not perform constraint argument lookup let edge_case = left_is_operator_right_is_not || matches!( - left_ty, + supertype, Type::RootPolyType(rpt) if rpt.is_substitutable() - ) || matches!(left_ty, Type::Constructor(..)); + ) || matches!(supertype, Type::Constructor(..)); if !edge_case { let result = type_is_subtype_with_generics( @@ -457,7 +473,7 @@ pub(crate) fn type_is_subtype_with_generics( // TODO is the above event needed or constructor with constraint == TypeId::ANY_TYPE return if result.is_mismatch() - && matches!(right_ty, Type::RootPolyType(root) if root.is_inferrable()) + && matches!(subtype, Type::RootPolyType(root) if root.is_inferrable()) { crate::utilities::notify!("Setting inferred request"); // state.add_request(ty, base_type); @@ -471,17 +487,17 @@ pub(crate) fn type_is_subtype_with_generics( _ => (), } - match left_ty { + match supertype { Type::FunctionReference(left_func) | Type::SpecialObject(SpecialObject::Function(left_func, _)) => subtype_function( (*left_func, base_type_arguments), - (right_ty, ty, ty_structure_arguments), + (subtype, ty, ty_structure_arguments), state, information, types, ), Type::Constant(lhs) => { - if let Type::Constant(rhs) = right_ty { + if let Type::Constant(rhs) = subtype { if lhs == rhs { SubTypeResult::IsSubType } else { @@ -489,7 +505,7 @@ pub(crate) fn type_is_subtype_with_generics( } } else { // TODO what about if LHS has inferred constraint - crate::utilities::notify!("Constant {:?} against RHS {:#?}", lhs, right_ty); + crate::utilities::notify!("Constant {:?} against RHS {:#?}", lhs, subtype); SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } } @@ -594,7 +610,7 @@ pub(crate) fn type_is_subtype_with_generics( types, ), CovariantContribution::String(left_string) => { - if let Type::Constant(Constant::String(right_string)) = right_ty { + if let Type::Constant(Constant::String(right_string)) = subtype { if &left_string == right_string { SubTypeResult::IsSubType } else { @@ -609,7 +625,7 @@ pub(crate) fn type_is_subtype_with_generics( CovariantContribution::Number(n) => { unreachable!("{:?}", n) // crate::utilities::notify!("Here?"); - // if let Type::Constant(Constant::String(right_string)) = right_ty { + // if let Type::Constant(Constant::String(right_string)) = subtype { // if left_string == right_string { // SubTypeResult::IsSubType // } else { @@ -674,7 +690,7 @@ pub(crate) fn type_is_subtype_with_generics( } else { // TODO what does this do // TODO temp fix - if let Type::Constructor(c) = right_ty { + if let Type::Constructor(c) = subtype { crate::utilities::notify!("TODO right hand side maybe okay"); if let Some(to) = c.get_base() { if to == base_type { @@ -692,7 +708,7 @@ pub(crate) fn type_is_subtype_with_generics( } } - return if check_and_includes(base_type, right_ty) { + return if check_and_includes(base_type, subtype) { SubTypeResult::IsSubType } else { // Already eliminated equal == case above, so always invalid here @@ -738,7 +754,7 @@ pub(crate) fn type_is_subtype_with_generics( return if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on: TypeId::READONLY_RESTRICTION, arguments, - }) = right_ty + }) = subtype { let ty = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); type_is_subtype_with_generics( @@ -749,7 +765,7 @@ pub(crate) fn type_is_subtype_with_generics( types, ) } else if information.get_chain_of_info().any(|info| info.frozen.contains(&ty)) - || matches!(right_ty, Type::Constant(_)) + || matches!(subtype, Type::Constant(_)) || matches!( ty, TypeId::STRING_TYPE | TypeId::BOOLEAN_TYPE | TypeId::NUMBER_TYPE @@ -805,7 +821,7 @@ pub(crate) fn type_is_subtype_with_generics( | TypeId::STRING_UNCAPITALIZE | TypeId::STRING_LOWERCASE | TypeId::STRING_UPPERCASE => { - if let Type::Constant(Constant::String(rs)) = right_ty { + if let Type::Constant(Constant::String(rs)) = subtype { let contributions = state.contributions.as_mut().map(|n| &mut n.staging_contravariant); let matches = slice_matches_type( @@ -827,10 +843,10 @@ pub(crate) fn type_is_subtype_with_generics( // Ezno intrinsic TypeId::LITERAL_RESTRICTION => { let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); - return if let Type::Constant(rhs_constant) = right_ty { + return if let Type::Constant(rhs_constant) = subtype { type_is_subtype_with_generics( (inner, base_type_arguments), - (rhs_constant.get_backing_type_id(), ty_structure_arguments), + (rhs_constant.get_backing_type(), ty_structure_arguments), state, information, types, @@ -861,35 +877,45 @@ pub(crate) fn type_is_subtype_with_generics( } return result; } - TypeId::LESS_THAN | TypeId::GREATER_THAN | TypeId::MULTIPLE_OF => { + TypeId::MULTIPLE_OF => { let argument = - arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap(); + arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC).unwrap(); + + let right_multiple = crate::types::intrinsics::get_multiple(ty, types); return if let ( Type::Constant(Constant::Number(argument)), - Type::Constant(Constant::Number(value)), - ) = (types.get_type_by_id(argument), right_ty) - { - let result = match *on { - TypeId::LESS_THAN => value < argument, - TypeId::GREATER_THAN => value > argument, - TypeId::MULTIPLE_OF => value % argument == 0f64, - _ => unreachable!(), - }; - if result { + Some(Type::Constant(Constant::Number(right_multiple))), + ) = ( + types.get_type_by_id(argument), + right_multiple.map(|right_multiple| types.get_type_by_id(right_multiple)), + ) { + if (right_multiple % argument) == 0. { SubTypeResult::IsSubType } else { SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } } else { - crate::utilities::notify!( - "Returning NonEqualityReason::Mismatch {:?}", - right_ty - ); + crate::utilities::notify!("TODO multiple of {:?}", (argument, ty, subtype)); + SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) + }; + } + TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE => { + return if let (super_range, Some(sub_range)) = ( + intrinsics::get_range(base_type, types).unwrap(), + intrinsics::get_range(ty, types), + ) { + if sub_range.contained_in(super_range) { + SubTypeResult::IsSubType + } else { + SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) + } + } else { + crate::utilities::notify!("TODO"); SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) }; } TypeId::CASE_INSENSITIVE => { - if let Type::Constant(Constant::String(rs)) = right_ty { + if let Type::Constant(Constant::String(rs)) = subtype { let contributions = state.contributions.as_mut().map(|n| &mut n.staging_contravariant); // Slice matches handles this @@ -932,7 +958,7 @@ pub(crate) fn type_is_subtype_with_generics( } // TODO a bit of a mess - return if let Some(sgs) = get_structure_generics_on(right_ty, *on) { + return if let Some(sgs) = get_structure_generics_on(subtype, *on) { match (arguments, sgs) { ( GenericArguments::ExplicitRestrictions(left), @@ -955,7 +981,7 @@ pub(crate) fn type_is_subtype_with_generics( } pair => todo!("{:?}", pair), } - } else if let Type::Object(super::ObjectNature::RealDeal) = right_ty { + } else if let Type::Object(super::ObjectNature::RealDeal) = subtype { let prototype = information.get_chain_of_info().find_map(|info| info.prototypes.get(&ty)); @@ -994,7 +1020,7 @@ pub(crate) fn type_is_subtype_with_generics( ); // TODO temp fix for general parameters - if let Type::Object(_) = right_ty { + if let Type::Object(_) = subtype { // let Some(lookup_restriction) = // types.get_look_up_generic_from_prototype(TypeId::ARRAY_TYPE, ty) // else { @@ -1015,7 +1041,7 @@ pub(crate) fn type_is_subtype_with_generics( } else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on: TypeId::ARRAY_TYPE, arguments: right_arguments, - }) = right_ty + }) = subtype { let left_arg = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); let right_arg = @@ -1032,7 +1058,7 @@ pub(crate) fn type_is_subtype_with_generics( types, ) } else { - crate::utilities::notify!("Not array-ish {:?}", right_ty); + crate::utilities::notify!("Not array-ish {:?}", subtype); SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } } else { @@ -1088,7 +1114,7 @@ pub(crate) fn type_is_subtype_with_generics( operator: crate::types::MathematicalAndBitwise::Add, .. } => { - if let Type::Constant(Constant::String(rs)) = right_ty { + if let Type::Constant(Constant::String(rs)) = subtype { let matches = slice_matches_type( (base_type, base_type_arguments), rs, @@ -1114,9 +1140,9 @@ pub(crate) fn type_is_subtype_with_generics( ) } } - Constructor::BinaryOperator { .. } - | Constructor::CanonicalRelationOperator { .. } - | Constructor::UnaryOperator { .. } => unreachable!("invalid constructor on LHS"), + Constructor::BinaryOperator { .. } | Constructor::CanonicalRelationOperator { .. } => { + unreachable!("invalid constructor on LHS") + } Constructor::TypeOperator(_) => todo!(), Constructor::TypeExtends(_) => todo!(), Constructor::Image { on: _, with: _, result } => { @@ -1145,7 +1171,7 @@ pub(crate) fn type_is_subtype_with_generics( otherwise_result: TypeId::NEVER_TYPE, result_union: _, }), - ) = (types.get_type_by_id(*condition), *otherwise_result, right_ty) + ) = (types.get_type_by_id(*condition), *otherwise_result, subtype) { if extends.equal_to_rhs(*rhs_condition, types) { SubTypeResult::IsSubType @@ -1157,7 +1183,7 @@ pub(crate) fn type_is_subtype_with_generics( SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } } else { - crate::utilities::notify!("Here {:?}", right_ty); + crate::utilities::notify!("Here {:?}", subtype); type_is_subtype_with_generics( (*result, base_type_arguments), @@ -1176,7 +1202,7 @@ pub(crate) fn type_is_subtype_with_generics( under: r_under, result: _, mode: _, - }) = right_ty + }) = subtype { if on == r_on && under == r_under { return SubTypeResult::IsSubType; @@ -1331,7 +1357,7 @@ pub(crate) fn type_is_subtype_with_generics( } Constructor::Awaited { .. } => todo!(), Constructor::KeyOf(on) => { - if let Type::Constant(crate::Constant::String(s)) = right_ty { + if let Type::Constant(crate::Constant::String(s)) = subtype { let get_property_unbound = get_property_unbound( (*on, base_type_arguments), ( @@ -1371,9 +1397,9 @@ pub(crate) fn type_is_subtype_with_generics( ) } // TODO WIP nominal mechanism - Type::Class { .. } => match right_ty { + Type::Class { .. } => match subtype { Type::Constant(constant) => { - if constant.get_backing_type_id() == base_type { + if constant.get_backing_type() == base_type { SubTypeResult::IsSubType } else { SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) @@ -1433,9 +1459,9 @@ pub(crate) fn type_is_subtype_with_generics( } // TODO a bit messy - match right_ty { + match subtype { Type::Constant(constant) => { - if constant.get_backing_type_id() == base_type { + if constant.get_backing_type() == base_type { SubTypeResult::IsSubType } else { SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) @@ -1481,7 +1507,7 @@ pub(crate) fn type_is_subtype_with_generics( | Type::Class { .. } | Type::AliasTo { .. } | Type::Interface { .. } => { - crate::utilities::notify!("lhs={:?} rhs={:?}", left_ty, right_ty); + crate::utilities::notify!("supertype={:?}, subtype={:?}", supertype, subtype); // TODO SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } @@ -1526,7 +1552,7 @@ pub(crate) fn type_is_subtype_with_generics( } } Type::SpecialObject(SpecialObject::Null) => { - crate::utilities::notify!("rhs={:?}", right_ty); + crate::utilities::notify!("rhs={:?}", subtype); SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch) } Type::SpecialObject(_) => todo!(), @@ -1535,28 +1561,28 @@ pub(crate) fn type_is_subtype_with_generics( fn subtype_function( (left_func, base_type_arguments): (crate::FunctionId, GenericChain), - (right_ty, ty, right_type_arguments): (&Type, TypeId, GenericChain), + (subtype, ty, subtypepe_arguments): (&Type, TypeId, GenericChain), state: &mut State, information: &impl InformationChain, types: &TypeStore, ) -> SubTypeResult { let right_func = if let Type::FunctionReference(right_func) - | Type::SpecialObject(SpecialObject::Function(right_func, _)) = right_ty + | Type::SpecialObject(SpecialObject::Function(right_func, _)) = subtype { right_func } else if let Some(constraint) = get_constraint(ty, types) { // TODO explain why get_constraint early breaks a bunch of tests - let right_ty = types.get_type_by_id(constraint); + let subtype = types.get_type_by_id(constraint); if let Type::FunctionReference(right_func) - | Type::SpecialObject(SpecialObject::Function(right_func, _)) = right_ty + | Type::SpecialObject(SpecialObject::Function(right_func, _)) = subtype { right_func } else { - crate::utilities::notify!("Not function after constraint!! {:?}", right_ty); + crate::utilities::notify!("Not function after constraint!! {:?}", subtype); return SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch); } } else { - crate::utilities::notify!("Not function!! {:?}", right_ty); + crate::utilities::notify!("Not function!! {:?}", subtype); return SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch); }; @@ -1572,7 +1598,7 @@ fn subtype_function( // Reverse is important let result = type_is_subtype_with_generics( - (right_param_ty, right_type_arguments), + (right_param_ty, subtypepe_arguments), (lhs_param.ty, base_type_arguments), state, information, @@ -1585,7 +1611,7 @@ fn subtype_function( crate::utilities::notify!( "Parameter invalid rhs ({:?} {:?}) <- lhs ({:?} {:?})", rhs, - right_type_arguments, + subtypepe_arguments, lhs, base_type_arguments ); @@ -1611,7 +1637,7 @@ fn subtype_function( } else { let type_is_subtype_with_generics = type_is_subtype_with_generics( (left_func.return_type, base_type_arguments), - (right_func.return_type, right_type_arguments), + (right_func.return_type, subtypepe_arguments), state, information, types, @@ -1627,7 +1653,7 @@ fn subtype_function( fn subtype_floating_properties( (base_type, base_type_arguments): (TypeId, GenericChain), - (ty, right_type_arguments): (TypeId, GenericChain), + (ty, subtypepe_arguments): (TypeId, GenericChain), state: &mut State, information: &impl InformationChain, types: &TypeStore, @@ -1639,7 +1665,7 @@ fn subtype_floating_properties( subtype_properties( (base_type, reversed_flattened_properties_on_base, base_type_arguments), - (ty, right_type_arguments), + (ty, subtypepe_arguments), state, information, types, @@ -1648,7 +1674,7 @@ fn subtype_floating_properties( fn subtype_properties<'a, T>( (base_type, base_properties, base_type_arguments): (TypeId, T, GenericChain), - (ty, right_type_arguments): (TypeId, GenericChain), + (ty, subtypepe_arguments): (TypeId, GenericChain), state: &mut State, information: &impl InformationChain, types: &TypeStore, @@ -1685,7 +1711,7 @@ where let result = check_lhs_property_is_super_type_of_rhs( (*publicity, key), (lhs_property, base_type_arguments, false), - (ty, right_type_arguments), + (ty, subtypepe_arguments), state, information, types, @@ -1702,7 +1728,7 @@ where if let Type::Interface { extends: Some(extends), .. } = types.get_type_by_id(base_type) { let extends_result = type_is_subtype_with_generics( (*extends, base_type_arguments), - (ty, right_type_arguments), + (ty, subtypepe_arguments), state, information, types, @@ -1771,7 +1797,7 @@ where fn check_lhs_property_is_super_type_of_rhs( (publicity, key): (Publicity, &PropertyKey<'_>), (lhs_property, base_type_arguments, optional): (&PropertyValue, GenericChain, bool), - (ty, right_type_arguments): (TypeId, GenericChain), + (ty, subtypepe_arguments): (TypeId, GenericChain), state: &mut State, information: &impl InformationChain, types: &TypeStore, @@ -1779,7 +1805,7 @@ fn check_lhs_property_is_super_type_of_rhs( match lhs_property { PropertyValue::Value(lhs_value) => { let right_result = get_property_unbound( - (ty, right_type_arguments), + (ty, subtypepe_arguments), (publicity, key, base_type_arguments), false, information, @@ -1799,7 +1825,7 @@ fn check_lhs_property_is_super_type_of_rhs( Ok(LogicalOrValid::Logical(res)) => { let res = check_logical_property( (*lhs_value, base_type_arguments, optional), - (res, right_type_arguments), + (res, subtypepe_arguments), state, information, types, @@ -1821,7 +1847,7 @@ fn check_lhs_property_is_super_type_of_rhs( ))) => { crate::utilities::notify!("TODO set as well?"); let get_handler = get_property_unbound( - (handler, right_type_arguments), + (handler, subtypepe_arguments), ( Publicity::Public, &PropertyKey::String(std::borrow::Cow::Borrowed("get")), @@ -1859,14 +1885,14 @@ fn check_lhs_property_is_super_type_of_rhs( map.insert(third, (CovariantContribution::TypeId(handler), 0)); } - let right_type_arguments = Some(GenericChainLink::MappedPropertyLink { - parent_link: right_type_arguments.as_ref(), + let subtypepe_arguments = Some(GenericChainLink::MappedPropertyLink { + parent_link: subtypepe_arguments.as_ref(), value: &map, }); let result = type_is_subtype_with_generics( (*lhs_value, base_type_arguments), - (function.return_type, right_type_arguments), + (function.return_type, subtypepe_arguments), state, information, types, @@ -1883,7 +1909,7 @@ fn check_lhs_property_is_super_type_of_rhs( check_lhs_property_is_super_type_of_rhs( (publicity, key), (lhs_property, base_type_arguments, optional), - (handler, right_type_arguments), + (handler, subtypepe_arguments), state, information, types, @@ -1893,7 +1919,7 @@ fn check_lhs_property_is_super_type_of_rhs( check_lhs_property_is_super_type_of_rhs( (publicity, key), (lhs_property, base_type_arguments, optional), - (handler, right_type_arguments), + (handler, subtypepe_arguments), state, information, types, @@ -1967,14 +1993,14 @@ fn check_lhs_property_is_super_type_of_rhs( check_lhs_property_is_super_type_of_rhs( (publicity, key), (truthy, base_type_arguments, is_optional), - (ty, right_type_arguments), + (ty, subtypepe_arguments), state, information, types, ) // if let PropertyValue::Value(lhs_value) = &**truthy { // let property = get_property_unbound( - // (ty, right_type_arguments), + // (ty, subtypepe_arguments), // (publicity, key, base_type_arguments), // information, // types, @@ -1986,14 +2012,14 @@ fn check_lhs_property_is_super_type_of_rhs( // let found = if let Logical::Pure(PropertyValue::Value(ref found)) = property { // *found // } else { - // TypeId::ERROR_TYPE + // TypeId::UNIMPLEMENTED_ERROR_TYPE // }; // crate::utilities::notify!("{:?}", property); // let res = check_logical_property( // (*lhs_value, base_type_arguments), - // (property, right_type_arguments), + // (property, subtypepe_arguments), // state, // information, // types, @@ -2024,7 +2050,7 @@ fn check_lhs_property_is_super_type_of_rhs( check_lhs_property_is_super_type_of_rhs( (publicity, key), (on, base_type_arguments, optional), - (ty, right_type_arguments), + (ty, subtypepe_arguments), state, information, types, @@ -2035,7 +2061,7 @@ fn check_lhs_property_is_super_type_of_rhs( fn check_logical_property( (lhs_property_value, lhs_property_value_type_arguments, optional): (TypeId, GenericChain, bool), - (rhs_property, right_type_arguments): (Logical, GenericChain), + (rhs_property, subtypepe_arguments): (Logical, GenericChain), state: &mut State, information: &impl InformationChain, types: &TypeStore, @@ -2053,7 +2079,7 @@ fn check_logical_property( type_is_subtype_with_generics( (lhs_property_value, lhs_property_value_type_arguments), - (rhs_type, right_type_arguments), + (rhs_type, subtypepe_arguments), state, information, types, @@ -2066,7 +2092,7 @@ fn check_logical_property( { let left_result = check_logical_property( (lhs_property_value, lhs_property_value_type_arguments, optional), - (left, right_type_arguments), + (left, subtypepe_arguments), state, information, types, @@ -2075,7 +2101,7 @@ fn check_logical_property( if let SubTypeResult::IsSubType = left_result { check_logical_property( (lhs_property_value, lhs_property_value_type_arguments, optional), - (right, right_type_arguments), + (right, subtypepe_arguments), state, information, types, @@ -2098,8 +2124,8 @@ fn check_logical_property( ( *on, GenericChainLink::append( - TypeId::ERROR_TYPE, - right_type_arguments.as_ref(), + TypeId::UNIMPLEMENTED_ERROR_TYPE, + subtypepe_arguments.as_ref(), &antecedent, ), ), @@ -2111,7 +2137,7 @@ fn check_logical_property( Logical::BasedOnKey(kind) => match kind { BasedOnKey::Left { value, key_arguments } => { let property_generics = Some(GenericChainLink::MappedPropertyLink { - parent_link: right_type_arguments.as_ref(), + parent_link: subtypepe_arguments.as_ref(), value: &key_arguments, }); check_logical_property( @@ -2129,7 +2155,7 @@ fn check_logical_property( type_is_subtype_of_property_mapped_key( MappedKey { value: (*extends).into(), key }, (lhs_property_value, lhs_property_value_type_arguments, optional), - (on, right_type_arguments), + (on, subtypepe_arguments), state, information, types, @@ -2138,7 +2164,7 @@ fn check_logical_property( let filter = get_constraint(key, types).unwrap_or(key); let properties = get_properties_on_single_type2( - (on, right_type_arguments), + (on, subtypepe_arguments), types, information, filter, @@ -2146,7 +2172,7 @@ fn check_logical_property( for (_key, rhs_property, _args) in properties { let result = check_logical_property( (lhs_property_value, lhs_property_value_type_arguments, optional), - (Logical::Pure(rhs_property), right_type_arguments), + (Logical::Pure(rhs_property), subtypepe_arguments), state, information, types, @@ -2172,7 +2198,7 @@ pub struct MappedKey { pub fn type_is_subtype_of_property_mapped_key( mapped_key: MappedKey, (base, property_generics, optional): (TypeId, GenericChain, bool), - (ty, right_type_arguments): (TypeId, GenericChain), + (ty, subtypepe_arguments): (TypeId, GenericChain), state: &mut State, information: &impl InformationChain, types: &TypeStore, @@ -2185,11 +2211,11 @@ pub fn type_is_subtype_of_property_mapped_key( "Reading {:?}, with {:?} {:?}", types.get_type_by_id(ty), s, - (property_generics.as_ref(), right_type_arguments.as_ref()) + (property_generics.as_ref(), subtypepe_arguments.as_ref()) ); } let right_property = get_property_unbound( - (ty, right_type_arguments), + (ty, subtypepe_arguments), ( Publicity::Public, &PropertyKey::String(std::borrow::Cow::Owned(s.to_owned())), @@ -2209,7 +2235,7 @@ pub fn type_is_subtype_of_property_mapped_key( }); let result = check_logical_property( (base, property_generics, optional), - (right_property, right_type_arguments), + (right_property, subtypepe_arguments), state, information, types, @@ -2236,7 +2262,7 @@ pub fn type_is_subtype_of_property_mapped_key( | Type::AliasTo { to, name: _, parameters: _ } => type_is_subtype_of_property_mapped_key( MappedKey { value: (*to).into(), key: mapped_key.key }, (base, property_generics, optional), - (ty, right_type_arguments), + (ty, subtypepe_arguments), state, information, types, @@ -2245,7 +2271,7 @@ pub fn type_is_subtype_of_property_mapped_key( let left = type_is_subtype_of_property_mapped_key( MappedKey { value: (*left).into(), key: mapped_key.key }, (base, property_generics, optional), - (ty, right_type_arguments), + (ty, subtypepe_arguments), state, information, types, @@ -2254,7 +2280,7 @@ pub fn type_is_subtype_of_property_mapped_key( type_is_subtype_of_property_mapped_key( MappedKey { value: (*right).into(), key: mapped_key.key }, (base, property_generics, optional), - (ty, right_type_arguments), + (ty, subtypepe_arguments), state, information, types, @@ -2267,7 +2293,7 @@ pub fn type_is_subtype_of_property_mapped_key( let left = type_is_subtype_of_property_mapped_key( MappedKey { value: (*left).into(), key: mapped_key.key }, (base, property_generics, optional), - (ty, right_type_arguments), + (ty, subtypepe_arguments), state, information, types, @@ -2278,7 +2304,7 @@ pub fn type_is_subtype_of_property_mapped_key( type_is_subtype_of_property_mapped_key( MappedKey { value: (*right).into(), key: mapped_key.key }, (base, property_generics, optional), - (ty, right_type_arguments), + (ty, subtypepe_arguments), state, information, types, @@ -2293,7 +2319,7 @@ pub fn type_is_subtype_of_property_mapped_key( type_is_subtype_of_property_mapped_key( MappedKey { value: value.into(), key: mapped_key.key }, (base, property_generics, optional), - (ty, right_type_arguments), + (ty, subtypepe_arguments), state, information, types, @@ -2323,7 +2349,7 @@ pub fn type_is_subtype_of_property_mapped_key( let result = type_is_subtype_of_property_mapped_key( MappedKey { value, key: mapped_key.key }, (base, property_generics, optional), - (ty, right_type_arguments), + (ty, subtypepe_arguments), state, information, types, @@ -2341,8 +2367,8 @@ pub fn type_is_subtype_of_property_mapped_key( Type::Class { .. } => todo!(), Type::Constant(_) => { let right_property = get_property_unbound( - (ty, right_type_arguments), - (Publicity::Public, &PropertyKey::Type(key_ty), right_type_arguments), + (ty, subtypepe_arguments), + (Publicity::Public, &PropertyKey::Type(key_ty), subtypepe_arguments), true, information, types, @@ -2358,7 +2384,7 @@ pub fn type_is_subtype_of_property_mapped_key( }); check_logical_property( (base, property_generics, optional), - (right_property, right_type_arguments), + (right_property, subtypepe_arguments), state, information, types, @@ -2708,7 +2734,7 @@ pub(crate) fn slice_matches_type( ) } Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: TypeId::MULTIPLE_OF | TypeId::LESS_THAN | TypeId::GREATER_THAN, + on: TypeId::MULTIPLE_OF | TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE, arguments: _, }) if allow_casts => { // Special behavior here to treat numerical property keys (which are strings) as numbers @@ -2812,6 +2838,7 @@ pub(crate) fn slice_matches_type( lhs, rhs, operator: MathematicalAndBitwise::Add, + result: _, }) => { let lhs = base_type_arguments .as_ref() @@ -2890,33 +2917,31 @@ pub(crate) fn number_matches_type( } } Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { - on: on @ (TypeId::MULTIPLE_OF | TypeId::LESS_THAN | TypeId::GREATER_THAN), + on: TypeId::MULTIPLE_OF, arguments, }) => { - // Special behavior here to treat numerical property keys (which are strings) as numbers - // TODO unify with the subtyping - let argument = arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap(); - - let arg_ty = types.get_type_by_id(argument); - if let Type::Constant(Constant::Number(argument)) = arg_ty { + let argument = + arguments.get_structure_restriction(TypeId::NUMBER_FLOOR_GENERIC).unwrap(); + if let Type::Constant(Constant::Number(argument)) = types.get_type_by_id(argument) { let number: ordered_float::NotNan = number.try_into().unwrap(); - crate::utilities::notify!("value={:?}, arg={:?}", number, argument); - match *on { - TypeId::LESS_THAN => *argument < number, - TypeId::GREATER_THAN => *argument > number, - TypeId::MULTIPLE_OF => number % *argument == 0f64, - _ => unreachable!(), - } + (number % argument) == 0. } else { - crate::utilities::notify!("TODO argument is dependent {:?}", arg_ty); + crate::utilities::notify!("Here?"); false } } + Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { + on: TypeId::INCLUSIVE_RANGE | TypeId::EXCLUSIVE_RANGE, + arguments: _, + }) => { + let lhs_range = intrinsics::get_range(base, types).unwrap(); + intrinsics::FloatRange::single(number.try_into().unwrap()).contained_in(lhs_range) + } Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on: TypeId::NOT_RESTRICTION, arguments, }) => { - let argument = arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap(); + let argument = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap(); !number_matches_type( (argument, base_type_arguments), diff --git a/checker/src/types/terms.rs b/checker/src/types/terms.rs index 04e25147..16e747eb 100644 --- a/checker/src/types/terms.rs +++ b/checker/src/types/terms.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use super::TypeId; -/// Terms +/// Terms. `null` is a special object /// TODO: /// - `BigInt` () #[derive(Eq, PartialEq, Hash, Debug, Clone, binary_serialize_derive::BinarySerializable)] @@ -12,6 +12,7 @@ pub enum Constant { Boolean(bool), Symbol { key: String }, NaN, + Undefined, } impl Constant { @@ -24,6 +25,7 @@ impl Constant { Constant::Boolean(value) => Cow::Borrowed(if *value { "true" } else { "false" }), Constant::Symbol { key } => Cow::Owned(format!("Symbol({key})")), Constant::NaN => Cow::Borrowed("NaN"), + Constant::Undefined => Cow::Borrowed("undefined"), } } @@ -37,16 +39,19 @@ impl Constant { Constant::Boolean(value) => if *value { "true" } else { "false" }.to_owned(), Constant::Symbol { key } => format!("Symbol({key})"), Constant::NaN => "NaN".to_owned(), + Constant::Undefined => "undefined".to_owned(), } } #[must_use] - pub fn get_backing_type_id(&self) -> TypeId { + pub fn get_backing_type(&self) -> TypeId { match self { Constant::Number(_) | Constant::NaN => TypeId::NUMBER_TYPE, Constant::String(_) => TypeId::STRING_TYPE, Constant::Boolean(_) => TypeId::BOOLEAN_TYPE, Constant::Symbol { .. } => TypeId::SYMBOL_TYPE, + // TODO ... + Constant::Undefined => TypeId::NEVER_TYPE, } } } diff --git a/checker/src/utilities/float_range.rs b/checker/src/utilities/float_range.rs new file mode 100644 index 00000000..133e226e --- /dev/null +++ b/checker/src/utilities/float_range.rs @@ -0,0 +1,207 @@ +type BetterF64 = ordered_float::NotNan; + +// TODO +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum FloatRange { + /// yes or `===` + Inclusive { floor: BetterF64, ceiling: BetterF64 }, + /// but not necessarily `===` + Exclusive { floor: BetterF64, ceiling: BetterF64 }, +} + +impl FloatRange { + #[must_use] + pub fn single(on: BetterF64) -> Self { + Self::Inclusive { floor: on, ceiling: on } + } + + /// For disjointness. TODO Think this is correct + #[must_use] + pub fn overlaps(self, other: Self) -> bool { + crate::utilities::notify!("{:?} ∩ {:?} != ∅", self, other); + + if let ( + Self::Inclusive { floor: l_floor, ceiling: l_ceiling }, + Self::Inclusive { floor: r_floor, ceiling: r_ceiling }, + ) = (self, other) + { + if l_floor <= r_floor { + l_ceiling >= r_floor + } else if l_ceiling >= r_ceiling { + l_floor <= r_ceiling + } else { + false + } + } else { + let (Self::Inclusive { floor: l_floor, ceiling: l_ceiling } + | Self::Exclusive { floor: l_floor, ceiling: l_ceiling }) = self; + let (Self::Inclusive { floor: r_floor, ceiling: r_ceiling } + | Self::Exclusive { floor: r_floor, ceiling: r_ceiling }) = other; + if l_floor < r_floor { + l_ceiling > r_floor + } else if l_ceiling > r_ceiling { + l_floor < r_ceiling + } else { + false + } + } + } + + /// The ⊆ relation (non-strict). For subtyping. TODO Think this is correct + #[must_use] + pub fn contained_in(self, other: Self) -> bool { + crate::utilities::notify!("{:?} ⊆ {:?}", self, other); + // Edge case + if let ( + Self::Inclusive { floor: l_floor, ceiling: l_ceiling }, + Self::Exclusive { floor: r_floor, ceiling: r_ceiling }, + ) = (self, other) + { + l_floor > r_floor && l_ceiling < r_ceiling + } else { + let (Self::Inclusive { floor: l_floor, ceiling: l_ceiling } + | Self::Exclusive { floor: l_floor, ceiling: l_ceiling }) = self; + let (Self::Inclusive { floor: r_floor, ceiling: r_ceiling } + | Self::Exclusive { floor: r_floor, ceiling: r_ceiling }) = other; + l_floor >= r_floor && l_ceiling <= r_ceiling + } + } + + /// ∀ a in self, ∀ b in other: a > b + #[must_use] + pub fn above(self, other: Self) -> bool { + crate::utilities::notify!("{:?} > {:?}", self, other); + if let ( + Self::Inclusive { floor: l_floor, ceiling: _ }, + Self::Inclusive { floor: _, ceiling: r_ceiling }, + ) = (self, other) + { + l_floor > r_ceiling + } else { + let (Self::Inclusive { floor: l_floor, ceiling: _ } + | Self::Exclusive { floor: l_floor, ceiling: _ }) = self; + let (Self::Inclusive { floor: _, ceiling: r_ceiling } + | Self::Exclusive { floor: _, ceiling: r_ceiling }) = other; + l_floor >= r_ceiling + } + } + + /// ∀ a in self, ∀ b in other: a < b + #[must_use] + pub fn below(self, other: Self) -> bool { + crate::utilities::notify!("{:?} < {:?}", self, other); + if let ( + Self::Inclusive { floor: _, ceiling: l_ceiling }, + Self::Inclusive { floor: r_floor, ceiling: _ }, + ) = (self, other) + { + l_ceiling < r_floor + } else { + let (Self::Inclusive { floor: _, ceiling: l_ceiling } + | Self::Exclusive { floor: _, ceiling: l_ceiling }) = self; + let (Self::Inclusive { floor: r_floor, ceiling: _ } + | Self::Exclusive { floor: r_floor, ceiling: _ }) = other; + l_ceiling <= r_floor + } + } + + #[must_use] + pub fn space_addition(self, other: Self) -> Self { + if let ( + Self::Inclusive { floor: l_floor, ceiling: l_ceiling }, + Self::Inclusive { floor: r_floor, ceiling: r_ceiling }, + ) = (self, other) + { + Self::Inclusive { floor: l_floor + r_floor, ceiling: l_ceiling + r_ceiling } + } else { + let (Self::Inclusive { floor: l_floor, ceiling: l_ceiling } + | Self::Exclusive { floor: l_floor, ceiling: l_ceiling }) = self; + let (Self::Inclusive { floor: r_floor, ceiling: r_ceiling } + | Self::Exclusive { floor: r_floor, ceiling: r_ceiling }) = other; + Self::Exclusive { floor: l_floor + r_floor, ceiling: l_ceiling + r_ceiling } + } + } + + #[must_use] + pub fn space_multiplication(self, other: Self) -> Self { + let inclusive = matches!((self, other), (Self::Inclusive { .. }, Self::Inclusive { .. })); + let (Self::Inclusive { floor: l_floor, ceiling: l_ceiling } + | Self::Exclusive { floor: l_floor, ceiling: l_ceiling }) = self; + let (Self::Inclusive { floor: r_floor, ceiling: r_ceiling } + | Self::Exclusive { floor: r_floor, ceiling: r_ceiling }) = other; + // being lazy + let corners = + [l_floor * r_floor, l_floor * r_ceiling, r_floor * l_ceiling, l_ceiling * r_ceiling]; + let floor = *corners.iter().min().unwrap(); + let ceiling = *corners.iter().max().unwrap(); + if inclusive { + Self::Inclusive { floor, ceiling } + } else { + Self::Exclusive { floor, ceiling } + } + } + + // TODO more :) +} + +// TODO more +#[cfg(test)] +mod tests { + use super::{BetterF64, FloatRange}; + + #[test] + fn contained_in() { + assert!(FloatRange::single(2.into()) + .contained_in(FloatRange::Exclusive { floor: 0.into(), ceiling: 5.into() })); + } + + #[test] + fn overlaps() { + assert!(FloatRange::Exclusive { floor: 0.into(), ceiling: 4.into() } + .overlaps(FloatRange::Exclusive { floor: 2.into(), ceiling: 5.into() })); + assert!(!FloatRange::Exclusive { floor: 0.into(), ceiling: 1.into() } + .overlaps(FloatRange::Exclusive { floor: 2.into(), ceiling: 5.into() })); + } + + #[test] + fn above() { + assert!(FloatRange::Exclusive { floor: 8.into(), ceiling: 10.into() } + .above(FloatRange::Exclusive { floor: 6.into(), ceiling: 7.into() })); + assert!(!FloatRange::Exclusive { floor: 0.into(), ceiling: 1.into() } + .above(FloatRange::Exclusive { floor: 0.into(), ceiling: 5.into() })); + } + + #[test] + fn below() { + assert!(FloatRange::Exclusive { floor: 0.into(), ceiling: 4.into() } + .below(FloatRange::Exclusive { floor: 6.into(), ceiling: 7.into() })); + assert!(!FloatRange::Exclusive { floor: 0.into(), ceiling: 1.into() } + .below(FloatRange::Exclusive { floor: 0.into(), ceiling: 5.into() })); + } + + #[test] + fn space_addition() { + assert_eq!( + FloatRange::Exclusive { floor: 0.into(), ceiling: 4.into() } + .space_addition(FloatRange::Exclusive { floor: 6.into(), ceiling: 7.into() }), + FloatRange::Exclusive { floor: 6.into(), ceiling: 11.into() } + ); + } + + #[test] + fn space_multiplication() { + assert_eq!( + FloatRange::Exclusive { floor: 0.into(), ceiling: 4.into() } + .space_multiplication(FloatRange::Exclusive { floor: 6.into(), ceiling: 7.into() }), + FloatRange::Exclusive { floor: 0.into(), ceiling: 28.into() } + ); + assert_eq!( + FloatRange::Exclusive { floor: BetterF64::from(-2i32), ceiling: 4.into() } + .space_multiplication(FloatRange::Exclusive { + floor: BetterF64::from(-10i32), + ceiling: 1.into() + }), + FloatRange::Exclusive { floor: BetterF64::from(-40i32), ceiling: 20.into() } + ); + } +} diff --git a/checker/src/utilities/mod.rs b/checker/src/utilities/mod.rs index e8427ee6..222c487a 100644 --- a/checker/src/utilities/mod.rs +++ b/checker/src/utilities/mod.rs @@ -1,6 +1,7 @@ //! These should probably be made into reusable crates at some point mod debugging; +pub mod float_range; pub mod map; pub mod range_map; pub mod serialization;