ezno_checker/types/
subtyping.rs

1//! Type subtype checking. (making sure the RHS type contains all the properties the LHS type requires)
2
3use source_map::SpanWithSource;
4
5use crate::{
6	context::{GeneralContext, InformationChain},
7	features::{
8		objects::{self, SpecialObject},
9		operations::MathematicalOrBitwiseOperation,
10	},
11	Constant, Environment, PropertyValue, TypeId,
12};
13
14use super::{
15	generics::{
16		chain::{GenericChain, GenericChainLink, SpecialGenericChainLink},
17		contributions::{ContributionDepth, Contributions, CovariantContribution, TriMap},
18		generic_type_arguments::GenericArguments,
19	},
20	get_constraint,
21	intrinsics::{self, apply_string_intrinsic},
22	logical::{BasedOnKey, Logical, LogicalOrValid, NeedsCalculation, PropertyOn},
23	printing::print_type,
24	properties::PropertyKey,
25	properties::{get_properties_on_single_type2, get_property_unbound, Publicity},
26	Constructor, ObjectNature, PartiallyAppliedGenerics, PolyNature, Type, TypeStore,
27};
28
29pub use super::{NonEqualityReason, PropertyError};
30
31// TODO implement `Try` / `?` on `SubTypeResult`
32#[derive(Debug)]
33pub enum SubTypeResult {
34	IsSubType,
35	IsNotSubType(NonEqualityReason),
36}
37
38impl SubTypeResult {
39	#[must_use]
40	pub fn is_mismatch(&self) -> bool {
41		matches!(self, Self::IsNotSubType(..))
42	}
43
44	#[must_use]
45	pub fn is_subtype(&self) -> bool {
46		matches!(self, Self::IsSubType)
47	}
48}
49
50#[derive(Clone, Copy)]
51pub enum SubTypingMode {
52	/// *output*
53	///
54	/// ```ts
55	/// <T>(t: T) => T
56	///        ^
57	/// ```
58	Contravariant { depth: u8 },
59	/// From subtyping against a function
60	///
61	/// ```ts
62	/// <T>(cb: (t: T) => bool) => T
63	///             ^
64	/// ```
65	Covariant {
66		/// From parameters or explicit generic position
67		position: SpanWithSource,
68	},
69}
70
71impl Default for SubTypingMode {
72	fn default() -> Self {
73		Self::Contravariant { depth: 0 }
74	}
75}
76
77// TODO these methods are bound to cause trouble
78impl SubTypingMode {
79	pub(crate) fn one_deeper(self) -> SubTypingMode {
80		match self {
81			SubTypingMode::Contravariant { depth } => Self::Contravariant { depth: depth + 1 },
82			o @ SubTypingMode::Covariant { .. } => o,
83		}
84	}
85
86	pub(crate) fn one_shallower(self) -> SubTypingMode {
87		match self {
88			SubTypingMode::Contravariant { depth } => {
89				Self::Contravariant { depth: depth.saturating_sub(1) }
90			}
91			o @ SubTypingMode::Covariant { .. } => o,
92		}
93	}
94}
95
96mod musing {
97	use crate::TypeId;
98
99	enum _AddPropertyConstraint {
100		/// For `satisfies`
101		No,
102		/// For variables
103		Yes,
104		/// Only for functions with things, specified by a field that doesn't exist
105		OnlyFor(Vec<TypeId>),
106	}
107}
108
109#[derive(Debug, Clone, Copy)]
110pub struct SubTypingOptions {
111	/// Don't allow `ERROR_TYPE` to pass as everything. This allows using `satisfies` to check that LHS != error
112	pub allow_errors: bool,
113}
114
115impl Default for SubTypingOptions {
116	fn default() -> Self {
117		Self { allow_errors: true }
118	}
119}
120
121impl SubTypingOptions {
122	#[must_use]
123	pub fn satisfies() -> Self {
124		Self { allow_errors: false }
125	}
126}
127
128/// Use for assignments, declaration etc
129pub fn type_is_subtype_object(
130	base_type: TypeId,
131	ty: TypeId,
132	environment: &mut Environment,
133	types: &mut TypeStore,
134) -> SubTypeResult {
135	let mut state = State {
136		already_checked: Vec::new(),
137		mode: SubTypingMode::default(),
138		contributions: None,
139		others: SubTypingOptions { allow_errors: true },
140		object_constraints: Some(Vec::new()),
141	};
142
143	let result = type_is_subtype(base_type, ty, &mut state, environment, types);
144
145	environment.add_object_constraints(state.object_constraints.unwrap().into_iter(), types);
146	// TODO information.add_inferred_constraints(x, types);
147
148	result
149}
150
151/// Checks whether `ty` is a subtype of the `base_type`
152/// - equivalently ...`base_type :>= ty` (`ty <=: base_type`)
153/// - equivalently ... whether `ty` could be substituted as `base_type`.
154/// - equivalently ... whether `ty`'s properties imply the existence of `base_type` properties.
155pub fn type_is_subtype(
156	base_type: TypeId,
157	ty: TypeId,
158	state: &mut State,
159	information: &impl InformationChain,
160	types: &TypeStore,
161) -> SubTypeResult {
162	type_is_subtype_with_generics(
163		(base_type, GenericChain::None),
164		(ty, GenericChain::None),
165		state,
166		information,
167		types,
168	)
169}
170
171/// Using `Vec` as it needs to do a sequential removal
172pub type AlreadyChecked = Vec<(TypeId, TypeId)>;
173
174/// Additional information during subtype checking
175// TODO pub constraint_inference_requests: Vec<TypeId, TypeId>
176pub struct State<'a> {
177	/// Prevents cycles
178	pub already_checked: AlreadyChecked,
179	pub mode: SubTypingMode,
180	/// TODO with slices and commit / head length
181	pub contributions: Option<Contributions<'a>>,
182	/// `None` if satisfies or parameters
183	pub object_constraints: Option<Vec<(TypeId, TypeId)>>,
184	pub others: SubTypingOptions,
185}
186
187pub type StateSavePoint = [u16; 4];
188
189impl<'a> State<'a> {
190	/// For `or`s, some items might have to be removed if the branch fails
191	#[must_use]
192	pub fn produce_save_point(&self) -> StateSavePoint {
193		let contributions = self.contributions.as_ref();
194		[
195			self.already_checked.len() as u16,
196			contributions.map_or(0, |c| c.staging_covariant.len().try_into().unwrap()),
197			contributions.map_or(0, |c| c.staging_contravariant.len().try_into().unwrap()),
198			self.object_constraints.as_ref().map_or(0, |c| c.len().try_into().unwrap()),
199		]
200	}
201
202	/// For setting the state back to where it was at the point of [`Self::produce_save_point`]
203	pub fn reset(&mut self, last: StateSavePoint) {
204		let [already_checked, contributions_covariant, contributions_contravariant, object_constraint_count] =
205			last;
206
207		let _ = self.already_checked.drain((already_checked as usize)..);
208		if let Some(ref mut contributions) = self.contributions {
209			let _ =
210				contributions.staging_covariant.drop_range((contributions_covariant as usize)..);
211			let _ = contributions
212				.staging_contravariant
213				.drop_range((contributions_contravariant as usize)..);
214		}
215		if let Some(ref mut object_constraints) = self.object_constraints {
216			let _ = object_constraints.drain((object_constraint_count as usize)..);
217		}
218	}
219}
220
221pub(crate) fn type_is_subtype_with_generics(
222	(base_type, base_type_arguments): (TypeId, GenericChain),
223	(ty, ty_structure_arguments): (TypeId, GenericChain),
224	state: &mut State,
225	information: &impl InformationChain,
226	types: &TypeStore,
227) -> SubTypeResult {
228	{
229		let debug = true;
230		crate::utilities::notify!(
231			"Checking {} :>= {}, with {:?}",
232			print_type(base_type, types, information, debug),
233			print_type(ty, types, information, debug),
234			base_type_arguments
235		);
236	}
237
238	if base_type == TypeId::ANY_TYPE || ty == TypeId::NEVER_TYPE {
239		return SubTypeResult::IsSubType;
240	}
241
242	if base_type == ty {
243		return SubTypeResult::IsSubType;
244	}
245
246	{
247		// Prevents cycles
248		if state.already_checked.iter().any(|(a, b)| *a == base_type && *b == ty) {
249			crate::utilities::notify!("Subtyping recursion");
250			return SubTypeResult::IsSubType;
251		}
252
253		state.already_checked.push((base_type, ty));
254	}
255
256	let supertype = types.get_type_by_id(base_type);
257	let subtype = types.get_type_by_id(ty);
258
259	// Eager things
260	match subtype {
261		Type::Narrowed { narrowed_to: right, .. } | Type::AliasTo { to: right, .. } => {
262			let result = type_is_subtype_with_generics(
263				(base_type, base_type_arguments),
264				(*right, ty_structure_arguments),
265				state,
266				information,
267				types,
268			);
269			// Temp fix for narrowing constants
270			// crate::utilities::notify!("{:?}", super::helpers::is_not_of_constant(*right, types));
271			// SubTypeResult::IsNotSubType(_)
272			return if let (Type::Narrowed { from, .. }, _, true) =
273				(subtype, &result, super::helpers::is_not_of_constant(*right, types))
274			{
275				type_is_subtype_with_generics(
276					(base_type, base_type_arguments),
277					(*from, ty_structure_arguments),
278					state,
279					information,
280					types,
281				)
282			} else {
283				result
284			};
285		}
286		Type::Or(left, right) => {
287			let right = *right;
288			// crate::utilities::notify!("OR RHS: left and right");
289			let left_result = type_is_subtype_with_generics(
290				(base_type, base_type_arguments),
291				(*left, ty_structure_arguments),
292				state,
293				information,
294				types,
295			);
296
297			return if let SubTypeResult::IsSubType = left_result {
298				type_is_subtype_with_generics(
299					(base_type, base_type_arguments),
300					(right, ty_structure_arguments),
301					state,
302					information,
303					types,
304				)
305			} else {
306				// else return the failing result
307				left_result
308			};
309		}
310		Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
311			on: on @ TypeId::NOT_RESTRICTION,
312			arguments: _,
313		}) => {
314			match *on {
315				TypeId::NOT_RESTRICTION => {
316					// This only happens when subtype ∪ supertype = `any`. This is only true when
317					// one is `any`. `Not<any>` is already `never` and `supertype = any` is handled above
318					return SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch);
319				}
320				_ => unreachable!(),
321			}
322		}
323		t @ (Type::RootPolyType(..) | Type::Constructor(..)) => {
324			if let Type::RootPolyType(PolyNature::Error(to)) = t {
325				// (unless specified) treat as subtype as error would have already been thrown
326				crate::utilities::notify!("Here {:?}", state.others.allow_errors);
327				return if state.others.allow_errors && *to == TypeId::ANY_TYPE {
328					SubTypeResult::IsSubType
329				} else {
330					type_is_subtype(base_type, *to, state, information, types)
331				};
332			}
333
334			// crate::utilities::notify!("Looking for {:?} with {:?}", ty, ty_structure_arguments);
335
336			if let Some(arg) = ty_structure_arguments.and_then(|tas| tas.get_argument_covariant(ty))
337			{
338				return match arg {
339					CovariantContribution::TypeId(ty) => type_is_subtype_with_generics(
340						(base_type, base_type_arguments),
341						(ty, ty_structure_arguments),
342						state,
343						information,
344						types,
345					),
346					CovariantContribution::String(string) => {
347						let contributions =
348							state.contributions.as_mut().map(|n| &mut n.staging_contravariant);
349						let matches = slice_matches_type(
350							(base_type, base_type_arguments),
351							&string,
352							contributions,
353							information,
354							types,
355							false,
356						);
357						if matches {
358							SubTypeResult::IsSubType
359						} else {
360							SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
361						}
362					}
363					CovariantContribution::SliceOf(s, (l, r)) => todo!("{:?}", (s, (l, r))),
364					CovariantContribution::CaseInsensitive(ci) => todo!("{:?}", ci),
365					CovariantContribution::Number(n) => {
366						let contributions =
367							state.contributions.as_mut().map(|n| &mut n.staging_contravariant);
368						let matches = number_matches_type(
369							(base_type, base_type_arguments),
370							n,
371							contributions,
372							information,
373							types,
374						);
375						if matches {
376							SubTypeResult::IsSubType
377						} else {
378							SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
379						}
380					}
381				};
382			}
383
384			if let SubTypingMode::Covariant { position: _ } = state.mode {
385				crate::utilities::notify!("Here covariant parameter chaos?");
386				// if let Some(ref mut contributions) = state.contributions {
387				// 	contributions.staging_covariant.insert(ty, (base_type, position))
388				// }
389				// return SubTypeResult::IsSubType;
390			}
391
392			// If lhs is not operator unless argument is operator
393			// if !T::INFER_GENERICS && ty_structure_arguments.is_none() {
394			let right_constraint = get_constraint(ty, types).unwrap();
395
396			// crate::utilities::notify!(
397			// 	"RHS is parameter, edge case results to {:?}",
398			// 	(
399			// 		types.get_type_by_id(ty),
400			// 		types.get_type_by_id(right_constraint),
401			// 		types.get_type_by_id(right_constraint).is_operator()
402			// 	)
403			// );
404
405			// This is important that LHS is not operator
406			let left_is_operator_right_is_not =
407				supertype.is_operator() && !types.get_type_by_id(right_constraint).is_operator();
408
409			// edge cases on edge cases
410			// If any of these are true. Then do not perform constraint argument lookup
411			let edge_case = left_is_operator_right_is_not
412				|| matches!(
413					supertype,
414					Type::RootPolyType(rpt)
415					if rpt.is_substitutable()
416				) || matches!(supertype, Type::Constructor(..))
417				|| base_type_arguments
418					.and_then(|args| args.get_argument_covariant(base_type))
419					.is_some();
420			if !edge_case {
421				let result = type_is_subtype_with_generics(
422					(base_type, base_type_arguments),
423					(right_constraint, ty_structure_arguments),
424					state,
425					information,
426					types,
427				);
428
429				// TODO is the above event needed or constructor with constraint == TypeId::ANY_TYPE
430				return if result.is_mismatch()
431					&& matches!(subtype, Type::RootPolyType(root) if root.is_inferrable())
432				{
433					crate::utilities::notify!("Setting inferred request");
434					// state.add_request(ty, base_type);
435					SubTypeResult::IsSubType
436				} else {
437					result
438				};
439			}
440		}
441		Type::PartiallyAppliedGenerics(..) => {}
442		_ => (),
443	}
444
445	match supertype {
446		Type::FunctionReference(left_func)
447		| Type::SpecialObject(SpecialObject::Function(left_func, _)) => subtype_function(
448			(*left_func, base_type_arguments),
449			(subtype, ty, ty_structure_arguments),
450			state,
451			information,
452			types,
453		),
454		Type::Constant(lhs) => {
455			if let Type::Constant(rhs) = subtype {
456				if lhs == rhs {
457					SubTypeResult::IsSubType
458				} else {
459					SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
460				}
461			} else {
462				// TODO what about if LHS has inferred constraint
463				// crate::utilities::notify!("Constant {:?} against RHS {:#?}", lhs, subtype);
464				SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
465			}
466		}
467		Type::Object(ObjectNature::AnonymousTypeAnnotation(properties)) => subtype_properties(
468			(base_type, properties.iter(), base_type_arguments),
469			(ty, ty_structure_arguments),
470			state,
471			information,
472			types,
473		),
474		Type::Object(ObjectNature::RealDeal) => {
475			crate::utilities::notify!(
476				"what {:?} (subtyping where LHS = ObjectNature::RealDeal)",
477				ty
478			);
479
480			subtype_floating_properties(
481				(base_type, base_type_arguments),
482				(ty, ty_structure_arguments),
483				state,
484				information,
485				types,
486			)
487		}
488		Type::And(left, right) => {
489			let right = *right;
490			// crate::utilities::notify!("AND: Checking left and right");
491			let left_result = type_is_subtype_with_generics(
492				(*left, base_type_arguments),
493				(ty, ty_structure_arguments),
494				state,
495				information,
496				types,
497			);
498
499			if let SubTypeResult::IsSubType = left_result {
500				type_is_subtype_with_generics(
501					(right, base_type_arguments),
502					(ty, ty_structure_arguments),
503					state,
504					information,
505					types,
506				)
507			} else {
508				// Return bad result
509				left_result
510			}
511		}
512		Type::Or(left, right) => {
513			let right = *right;
514			let save_point = state.produce_save_point();
515
516			let left_result = type_is_subtype_with_generics(
517				(*left, base_type_arguments),
518				(ty, ty_structure_arguments),
519				state,
520				information,
521				types,
522			);
523
524			if let SubTypeResult::IsSubType = left_result {
525				if state.contributions.is_some() {
526					// only for double generics specialisation. Otherwise short-circuiting is fine
527					let _res = type_is_subtype_with_generics(
528						(right, base_type_arguments),
529						(ty, ty_structure_arguments),
530						state,
531						information,
532						types,
533					);
534				}
535				SubTypeResult::IsSubType
536			} else {
537				// IMPORTANT: Invalidate any already checked types
538				state.reset(save_point);
539
540				type_is_subtype_with_generics(
541					(right, base_type_arguments),
542					(ty, ty_structure_arguments),
543					state,
544					information,
545					types,
546				)
547			}
548		}
549		Type::RootPolyType(nature) => {
550			if let PolyNature::Error(_) = nature {
551				return SubTypeResult::IsSubType;
552			}
553
554			// TODO little weird, handing two very different cases beside each other. Might introduce bugs.. :(
555			let base_arg =
556				base_type_arguments.and_then(|args| args.get_argument_covariant(base_type));
557
558			if let Some(base_arg) = base_arg {
559				match base_arg {
560					CovariantContribution::TypeId(base_arg) => type_is_subtype_with_generics(
561						(base_arg, base_type_arguments),
562						(ty, ty_structure_arguments),
563						state,
564						information,
565						types,
566					),
567					CovariantContribution::String(left_string) => {
568						if let Type::Constant(Constant::String(right_string)) = subtype {
569							if &left_string == right_string {
570								SubTypeResult::IsSubType
571							} else {
572								SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
573							}
574						} else {
575							SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
576						}
577					}
578					CovariantContribution::SliceOf(s, (l, r)) => todo!("{:?}", (s, (l, r))),
579					CovariantContribution::CaseInsensitive(ci) => todo!("{:?}", (ci)),
580					CovariantContribution::Number(n) => {
581						unreachable!("{:?}", n)
582						// crate::utilities::notify!("Here?");
583						// if let Type::Constant(Constant::String(right_string)) = subtype {
584						// 	if left_string == right_string {
585						// 		SubTypeResult::IsSubType
586						// 	} else {
587						// 		SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
588						// 	}
589						// } else {
590						// 	SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
591						// }
592					}
593				}
594			} else if let Some(ref mut contributions) = state.contributions {
595				match state.mode {
596					SubTypingMode::Contravariant { depth } => {
597						// With <*base_type* extends *under> check ty is under
598
599						crate::utilities::notify!(
600							"contributions.parent={:?}",
601							contributions.parent
602						);
603
604						let result = if let Some(under) =
605							contributions.get_standard_restriction(base_type)
606						{
607							type_is_subtype_with_generics(
608								(under, GenericChain::None),
609								(ty, ty_structure_arguments),
610								state,
611								information,
612								types,
613							)
614						} else {
615							type_is_subtype_with_generics(
616								(nature.get_constraint(), GenericChain::None),
617								(ty, ty_structure_arguments),
618								state,
619								information,
620								types,
621							)
622						};
623
624						state
625							.contributions
626							.as_mut()
627							.unwrap()
628							.staging_contravariant
629							.insert(base_type, (ty.into(), depth));
630
631						result
632					}
633					SubTypingMode::Covariant { position } => {
634						state
635							.contributions
636							.as_mut()
637							.unwrap()
638							.staging_covariant
639							.insert(base_type, (ty, position));
640
641						// TODO temp
642						SubTypeResult::IsSubType
643					}
644				}
645			} else {
646				// TODO what does this do
647				// TODO temp fix
648				if let Type::Constructor(c) = subtype {
649					crate::utilities::notify!("TODO right hand side maybe okay");
650					if c.get_constraint() == base_type {
651						return SubTypeResult::IsSubType;
652					}
653				}
654				if let PolyNature::FunctionGeneric { .. } = nature {
655					/// WIP
656					fn check_and_includes(expecting: TypeId, rhs: &Type) -> bool {
657						if let Type::And(left, right) = rhs {
658							*left == expecting || *right == expecting
659						} else {
660							false
661						}
662					}
663
664					return if check_and_includes(base_type, subtype) {
665						SubTypeResult::IsSubType
666					} else {
667						// Already eliminated equal == case above, so always invalid here
668						SubTypeResult::IsNotSubType(NonEqualityReason::GenericParameterMismatch)
669					};
670				}
671
672				crate::utilities::notify!(
673					"Subtyping LHS={:?} against RHS, without setting type arguments",
674					nature
675				);
676
677				let constraint = get_constraint(base_type, types).unwrap();
678
679				type_is_subtype_with_generics(
680					(constraint, base_type_arguments),
681					(ty, ty_structure_arguments),
682					state,
683					information,
684					types,
685				)
686			}
687		}
688		Type::Narrowed { narrowed_to, .. } => {
689			crate::utilities::notify!("Narrowed on Left?");
690			type_is_subtype_with_generics(
691				(*narrowed_to, base_type_arguments),
692				(ty, ty_structure_arguments),
693				state,
694				information,
695				types,
696			)
697		}
698		Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on, arguments }) => {
699			match *on {
700				TypeId::READONLY_RESTRICTION => {
701					crate::utilities::notify!("TODO temp readonly inner check");
702					let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap();
703					// Some(GenericChainLink::SpecialGenericChainLink {
704					// 	parent_link: ty_structure_arguments.as_ref(),
705					// 	special: SpecialGenericChainLink::Readonly,
706					// })
707					return if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
708						on: TypeId::READONLY_RESTRICTION,
709						arguments,
710					}) = subtype
711					{
712						let ty = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap();
713						type_is_subtype_with_generics(
714							(inner, ty_structure_arguments),
715							(ty, base_type_arguments),
716							state,
717							information,
718							types,
719						)
720					} else if information
721						.get_chain_of_info()
722						.any(|info| info.frozen.contains_key(&ty))
723						|| matches!(subtype, Type::Constant(_))
724						|| matches!(
725							ty,
726							TypeId::STRING_TYPE | TypeId::BOOLEAN_TYPE | TypeId::NUMBER_TYPE
727						) {
728						type_is_subtype_with_generics(
729							(inner, ty_structure_arguments),
730							(ty, base_type_arguments),
731							state,
732							information,
733							types,
734						)
735					} else {
736						// TODO is not readonly
737						SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
738					};
739				}
740				TypeId::EXCLUSIVE_RESTRICTION => {
741					let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap();
742					return type_is_subtype_with_generics(
743						(
744							inner,
745							Some(GenericChainLink::SpecialGenericChainLink {
746								parent_link: ty_structure_arguments.as_ref(),
747								special: SpecialGenericChainLink::Exclusive,
748							}),
749						),
750						(ty, base_type_arguments),
751						state,
752						information,
753						types,
754					);
755				}
756				TypeId::NOT_RESTRICTION => {
757					let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap();
758					// https://leanprover-community.github.io/mathlib4_docs/Mathlib/Data/Set/Basic.html#Set.subset_compl_iff_disjoint_left
759
760					let result = super::disjoint::types_are_disjoint(
761						ty,
762						inner,
763						&mut state.already_checked,
764						information,
765						types,
766					);
767					// crate::utilities::notify!("Here {:?}", (&result, inner));
768					return if result {
769						SubTypeResult::IsSubType
770					} else {
771						SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
772					};
773				}
774				// TSC intrinsics
775				TypeId::STRING_CAPITALIZE
776				| TypeId::STRING_UNCAPITALIZE
777				| TypeId::STRING_LOWERCASE
778				| TypeId::STRING_UPPERCASE => {
779					if let Type::Constant(Constant::String(rs)) = subtype {
780						let contributions =
781							state.contributions.as_mut().map(|n| &mut n.staging_contravariant);
782						let matches = slice_matches_type(
783							(base_type, base_type_arguments),
784							rs,
785							contributions,
786							information,
787							types,
788							false,
789						);
790						return if matches {
791							SubTypeResult::IsSubType
792						} else {
793							// TODO remove contributions
794							SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
795						};
796					}
797				}
798				// Ezno intrinsic
799				TypeId::LITERAL_RESTRICTION => {
800					let inner = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap();
801					return if let Type::Constant(_)
802					| Type::Object(ObjectNature::RealDeal)
803					| Type::SpecialObject(..) = subtype
804					{
805						type_is_subtype_with_generics(
806							(inner, base_type_arguments),
807							(ty, ty_structure_arguments),
808							state,
809							information,
810							types,
811						)
812					} else {
813						// TODO what about if the rhs == TypeId::CONSTANT_RESTRICTION
814						// TODO non-constant error
815						SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
816					};
817				}
818				TypeId::NO_INFER => {
819					let on = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap();
820					let current_contributing =
821						state.contributions.as_ref().map(|c| c.staging_contravariant.len());
822					let result = type_is_subtype_with_generics(
823						(on, base_type_arguments),
824						(ty, ty_structure_arguments),
825						state,
826						information,
827						types,
828					);
829					// Drop any infer-ed results
830					if let (Some(contributions), Some(current_contributing)) =
831						(state.contributions.as_mut(), current_contributing)
832					{
833						let _ =
834							contributions.staging_contravariant.drop_range(current_contributing..);
835					}
836					return result;
837				}
838				TypeId::MULTIPLE_OF => {
839					let argument =
840						arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap();
841
842					let right_multiple = crate::types::intrinsics::get_multiple(ty, types);
843					return if let (
844						Type::Constant(Constant::Number(argument)),
845						Some(Type::Constant(Constant::Number(right_multiple))),
846					) = (
847						types.get_type_by_id(argument),
848						right_multiple.map(|right_multiple| types.get_type_by_id(right_multiple)),
849					) {
850						if (right_multiple % argument) == 0. {
851							SubTypeResult::IsSubType
852						} else {
853							SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
854						}
855					} else {
856						crate::utilities::notify!("TODO multiple of {:?}", (argument, ty, subtype));
857						SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
858					};
859				}
860				TypeId::GREATER_THAN => {
861					let argument =
862						arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap();
863					let argument_type = types.get_type_by_id(argument);
864					return if let (
865						Type::Constant(Constant::Number(value)),
866						Type::Constant(Constant::Number(subtype_number)),
867					) = (argument_type, subtype)
868					{
869						if subtype_number > value {
870							SubTypeResult::IsSubType
871						} else {
872							SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
873						}
874					} else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
875						on: TypeId::GREATER_THAN,
876						arguments,
877					}) = subtype
878					{
879						let subtype_argument =
880							arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap();
881						// Transitivity
882						if argument == subtype_argument {
883							SubTypeResult::IsSubType
884						} else {
885							let subtype_argument = types.get_type_by_id(subtype_argument);
886							if let (
887								Type::Constant(Constant::Number(subtype_number)),
888								Type::Constant(Constant::Number(value)),
889							) = (argument_type, subtype_argument)
890							{
891								if subtype_number > value {
892									SubTypeResult::IsSubType
893								} else {
894									SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
895								}
896							} else {
897								crate::utilities::notify!("Here");
898								SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
899							}
900						}
901					} else {
902						SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
903					};
904				}
905				TypeId::LESS_THAN => {
906					let argument =
907						arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap();
908					let argument_type = types.get_type_by_id(argument);
909					return if let (
910						Type::Constant(Constant::Number(value)),
911						Type::Constant(Constant::Number(subtype_number)),
912					) = (argument_type, subtype)
913					{
914						if subtype_number < value {
915							SubTypeResult::IsSubType
916						} else {
917							SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
918						}
919					} else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
920						on: TypeId::GREATER_THAN,
921						arguments,
922					}) = subtype
923					{
924						let subtype_argument =
925							arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap();
926						// Transitivity
927						if argument == subtype_argument {
928							SubTypeResult::IsSubType
929						} else {
930							let subtype_argument = types.get_type_by_id(subtype_argument);
931							if let (
932								Type::Constant(Constant::Number(subtype_number)),
933								Type::Constant(Constant::Number(value)),
934							) = (argument_type, subtype_argument)
935							{
936								if subtype_number < value {
937									SubTypeResult::IsSubType
938								} else {
939									SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
940								}
941							} else {
942								crate::utilities::notify!("Here");
943								SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
944							}
945						}
946					} else {
947						SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
948					};
949				}
950				TypeId::CASE_INSENSITIVE => {
951					if let Type::Constant(Constant::String(rs)) = subtype {
952						let contributions =
953							state.contributions.as_mut().map(|n| &mut n.staging_contravariant);
954						// Slice matches handles this
955						let matches = slice_matches_type(
956							(base_type, base_type_arguments),
957							rs,
958							contributions,
959							information,
960							types,
961							false,
962						);
963
964						return if matches {
965							SubTypeResult::IsSubType
966						} else {
967							// TODO remove contributions
968							SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
969						};
970					}
971				}
972				_ => {}
973			}
974
975			if let Some(lookup) = types.lookup_generic_map.get(on) {
976				fn get_structure_generics_on(
977					r#type: &Type,
978					expected: TypeId,
979				) -> Option<&GenericArguments> {
980					match r#type {
981						Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
982							on,
983							arguments,
984						}) if expected == *on => Some(arguments),
985						_ => None,
986					}
987				}
988
989				if let Some(ref mut object_constraints) = state.object_constraints {
990					object_constraints.push((ty, base_type));
991				}
992				// TODO a bit of a mess
993
994				return if let Some(sgs) = get_structure_generics_on(subtype, *on) {
995					match (arguments, sgs) {
996						(
997							GenericArguments::ExplicitRestrictions(left),
998							GenericArguments::ExplicitRestrictions(right),
999						) => {
1000							for (lk, (lv, _)) in left.iter() {
1001								let (rv, _) = right.get(lk).unwrap();
1002								let argument_is_subtype = type_is_subtype_with_generics(
1003									(*lv, base_type_arguments),
1004									(*rv, ty_structure_arguments),
1005									state,
1006									information,
1007									types,
1008								);
1009								if let err @ SubTypeResult::IsNotSubType(_) = argument_is_subtype {
1010									return err;
1011								}
1012							}
1013							SubTypeResult::IsSubType
1014						}
1015						pair => todo!("{:?}", pair),
1016					}
1017				} else if let Type::Object(super::ObjectNature::RealDeal) = subtype {
1018					let prototype =
1019						information.get_chain_of_info().find_map(|info| info.prototypes.get(&ty));
1020
1021					crate::utilities::notify!("prototype is {:?}", prototype);
1022
1023					if prototype.is_some_and(|prototype| prototype == on) {
1024						for (argument, lookup) in lookup.iter() {
1025							// TODO no vec
1026							let backing_type =
1027								arguments.get_structure_restriction(*argument).unwrap();
1028							for value in lookup.calculate_lookup(information, types, ty) {
1029								let type_is_subtype =
1030									type_is_subtype(backing_type, value, state, information, types);
1031								if let e @ SubTypeResult::IsNotSubType(_) = type_is_subtype {
1032									return e;
1033								}
1034							}
1035						}
1036						SubTypeResult::IsSubType
1037					} else {
1038						SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1039					}
1040				} else {
1041					SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1042				};
1043			}
1044
1045			if let TypeId::ARRAY_TYPE = *on {
1046				let backing_type = arguments
1047					.get_structure_restriction(TypeId::T_TYPE)
1048					.expect("array T argument not set ?");
1049
1050				crate::utilities::notify!(
1051					"Array type is {}",
1052					print_type(backing_type, types, information, false)
1053				);
1054
1055				// TODO temp fix for general parameters
1056				if let Type::Object(_) = subtype {
1057					// let Some(lookup_restriction) =
1058					// 	types.get_look_up_generic_from_prototype(TypeId::ARRAY_TYPE, ty)
1059					// else {
1060					// 	crate::utilities::notify!("Here");
1061					// 	return SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch);
1062					// };
1063
1064					// TODO don't create vec
1065					// for value in lookup_restriction.calculate_lookup(information) {
1066
1067					// }
1068
1069					if let Some(ref mut object_constraints) = state.object_constraints {
1070						object_constraints.push((ty, base_type));
1071					}
1072
1073					SubTypeResult::IsSubType
1074				} else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
1075					on: TypeId::ARRAY_TYPE,
1076					arguments: right_arguments,
1077				}) = subtype
1078				{
1079					let left_arg = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap();
1080					let right_arg =
1081						right_arguments.get_structure_restriction(TypeId::T_TYPE).unwrap();
1082
1083					crate::utilities::notify!("{:?} :> {:?}", left_arg, right_arg);
1084
1085					// TODO unsure about arguments here
1086					type_is_subtype_with_generics(
1087						(left_arg, base_type_arguments),
1088						(right_arg, ty_structure_arguments),
1089						state,
1090						information,
1091						types,
1092					)
1093				} else {
1094					crate::utilities::notify!("Not array-ish {:?}", subtype);
1095					SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1096				}
1097			} else {
1098				fn is_arguments_cyclic(a: &GenericArguments) -> bool {
1099					if let GenericArguments::ExplicitRestrictions(arguments) = a {
1100						arguments.iter().any(|(left, (right, _))| left == right)
1101					} else {
1102						false
1103					}
1104				}
1105				// TODO temp fix
1106				if is_arguments_cyclic(arguments) {
1107					let GenericArguments::ExplicitRestrictions(arguments) = arguments else {
1108						unreachable!();
1109					};
1110
1111					let filtered: crate::Map<_, _> = arguments
1112						.iter()
1113						.filter(|(left, (right, _))| left != right)
1114						.copied()
1115						.collect();
1116					let refe = GenericArguments::ExplicitRestrictions(filtered);
1117					let base_type_arguments =
1118						GenericChainLink::append(base_type, base_type_arguments.as_ref(), &refe);
1119
1120					type_is_subtype_with_generics(
1121						(*on, base_type_arguments),
1122						(ty, ty_structure_arguments),
1123						state,
1124						information,
1125						types,
1126					)
1127				} else {
1128					let base_type_arguments = GenericChainLink::append(
1129						base_type,
1130						base_type_arguments.as_ref(),
1131						arguments,
1132					);
1133
1134					type_is_subtype_with_generics(
1135						(*on, base_type_arguments),
1136						(ty, ty_structure_arguments),
1137						state,
1138						information,
1139						types,
1140					)
1141				}
1142			}
1143		}
1144		Type::Constructor(cst) => match cst {
1145			// For template literal types
1146			Constructor::BinaryOperator {
1147				operator: crate::types::MathematicalOrBitwiseOperation::Add,
1148				result: TypeId::STRING_TYPE,
1149				..
1150			} => {
1151				if let Type::Constant(Constant::String(rs)) = subtype {
1152					let matches = slice_matches_type(
1153						(base_type, base_type_arguments),
1154						rs,
1155						state.contributions.as_mut().map(|n| &mut n.staging_contravariant),
1156						information,
1157						types,
1158						false,
1159					);
1160					if matches {
1161						SubTypeResult::IsSubType
1162					} else {
1163						// TODO clear contributions
1164						SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1165					}
1166				} else if let Type::Constructor(Constructor::BinaryOperator {
1167					operator: crate::types::MathematicalOrBitwiseOperation::Add,
1168					result: TypeId::STRING_TYPE,
1169					..
1170				}) = subtype
1171				{
1172					crate::utilities::notify!("TODO test prefixes");
1173					SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1174				} else {
1175					SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1176				}
1177			}
1178			Constructor::BinaryOperator {
1179				operator: crate::types::MathematicalOrBitwiseOperation::Add,
1180				result: TypeId::NUMBER_TYPE,
1181				..
1182			} => {
1183				crate::utilities::notify!("TODO here!");
1184				SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1185			}
1186			Constructor::BinaryOperator { .. } | Constructor::CanonicalRelationOperator { .. } => {
1187				unreachable!("invalid constructor on LHS")
1188			}
1189			Constructor::TypeOperator(_) => todo!(),
1190			Constructor::TypeExtends(_) => todo!(),
1191			Constructor::Image { on: _, with: _, result } => {
1192				crate::utilities::notify!("Here");
1193				type_is_subtype_with_generics(
1194					(*result, base_type_arguments),
1195					(ty, ty_structure_arguments),
1196					state,
1197					information,
1198					types,
1199				)
1200			}
1201			Constructor::ConditionalResult {
1202				condition,
1203				truthy_result: _,
1204				otherwise_result,
1205				result_union: result,
1206			} => {
1207				// implements `assert is condition annotation`
1208				if let (
1209					Type::Constructor(Constructor::TypeExtends(extends)),
1210					TypeId::NEVER_TYPE,
1211					Type::Constructor(Constructor::ConditionalResult {
1212						condition: rhs_condition,
1213						truthy_result: _,
1214						otherwise_result: TypeId::NEVER_TYPE,
1215						result_union: _,
1216					}),
1217				) = (types.get_type_by_id(*condition), *otherwise_result, subtype)
1218				{
1219					if extends.equal_to_rhs(*rhs_condition, types) {
1220						SubTypeResult::IsSubType
1221					} else {
1222						crate::utilities::notify!(
1223							"Here {:?}",
1224							types.get_type_by_id(*rhs_condition)
1225						);
1226						SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1227					}
1228				} else {
1229					crate::utilities::notify!("Here {:?}", subtype);
1230
1231					type_is_subtype_with_generics(
1232						(*result, base_type_arguments),
1233						(ty, ty_structure_arguments),
1234						state,
1235						information,
1236						types,
1237					)
1238				}
1239			}
1240			Constructor::Property { on, under, result: _, mode: _ } => {
1241				// Ezno custom state
1242				// TODO might be based of T
1243				if let Type::Constructor(Constructor::Property {
1244					on: r_on,
1245					under: r_under,
1246					result: _,
1247					mode: _,
1248				}) = subtype
1249				{
1250					if on == r_on && under == r_under {
1251						return SubTypeResult::IsSubType;
1252					}
1253				}
1254
1255				// TODO this only seems to work in simple cases. For mapped types
1256				// crate::utilities::notify!(
1257				// 	"base_structure_arguments={:?}, ty_structure_arguments={:?}, *on={:?}",
1258				// 	base_type_arguments,
1259				// 	ty_structure_arguments,
1260				// 	on
1261				// );
1262
1263				if let Some(on) = base_type_arguments.and_then(|args| args.get_single_argument(*on))
1264				{
1265					let new_under;
1266					let under = if let PropertyKey::Type(original) = under {
1267						// let ty = types.get_type_by_id(*original);
1268						crate::utilities::notify!(
1269							"original={:?}, bta={:?}",
1270							original,
1271							base_type_arguments
1272						);
1273						// let original = if let Type::RootPolyType(
1274						// 	crate::types::PolyNature::MappedGeneric { name, extends },
1275						// ) = ty
1276						// {
1277						// 	*extends
1278						// } else {
1279						// 	*original
1280						// };
1281
1282						let original = *original;
1283						new_under = if let Some(under) = base_type_arguments
1284							.and_then(|args| args.get_argument_covariant(original))
1285						{
1286							under.into_property_key()
1287						} else {
1288							crate::utilities::notify!(
1289								"Could not find key type {:?} {:?}",
1290								original,
1291								base_type_arguments
1292							);
1293							PropertyKey::from_type(original, types)
1294						};
1295						&new_under
1296					} else {
1297						under
1298					};
1299
1300					crate::utilities::notify!(
1301						"Here got under={:?}, on={:?}",
1302						under,
1303						types.get_type_by_id(on)
1304					);
1305					let property = get_property_unbound(
1306						(on, base_type_arguments),
1307						(Publicity::Public, under, ty_structure_arguments),
1308						false,
1309						information,
1310						types,
1311					);
1312					if let Ok(LogicalOrValid::Logical(property)) = property {
1313						crate::utilities::notify!("Here 3");
1314						match property {
1315							Logical::Pure(property) => {
1316								crate::utilities::notify!("Here 4 {:?}", property);
1317								let property_value = property.as_get_type(types);
1318								return type_is_subtype_with_generics(
1319									(property_value, base_type_arguments),
1320									(ty, ty_structure_arguments),
1321									state,
1322									information,
1323									types,
1324								);
1325							}
1326							Logical::BasedOnKey(BasedOnKey::Right(PropertyOn { on, key })) => {
1327								crate::utilities::notify!("TODO {:?}", (on, key));
1328								// let filter = get_constraint(key, types).unwrap_or(key);
1329
1330								// let properties =
1331								// 	crate::types::properties::get_properties_on_single_type(
1332								// 		on,
1333								// 		types,
1334								// 		information,
1335								// 		false,
1336								// 		filter,
1337								// 	);
1338
1339								// for (_, _, value) in properties {
1340								// 	crate::utilities::notify!("{:?}", value);
1341								// let result = type_is_subtype_with_generics(
1342								// 	(property, base_type_arguments),
1343								// 	(value.as_set_type(), ty_structure_arguments),
1344								// 	state,
1345								// 	information,
1346								// 	types,
1347								// );
1348
1349								// if let SubTypeResult::IsNotSubType(_) = result {
1350								// 	return result;
1351								// }
1352								// }
1353							}
1354							value => {
1355								crate::utilities::notify!("TODO not checking with {:?}", value);
1356								// SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1357							} // Logical::Or { based_on, left, right } => todo!(),
1358							  // Logical::Implies { on, antecedent } => todo!(),
1359						}
1360					}
1361				} else {
1362					crate::utilities::notify!(
1363						"Could not find argument for {:?}",
1364						(on, base_type_arguments)
1365					);
1366				}
1367				// else if let Type::Interface { .. }
1368				// | Type::Object(ObjectNature::AnonymousTypeAnnotation)
1369				// | Type::AliasTo { .. } = types.get_type_by_id(*on)
1370				// {
1371				// 	let property = get_property_unbound(
1372				// 		(*on, base_structure_arguments),
1373				// 		(Publicity::Public, under, ty_structure_arguments),
1374				// 		information,
1375				// 		types,
1376				// 	);
1377				// 	if let Ok(property) = property {
1378				// 		crate::utilities::notify!("Here");
1379				// 		match property {
1380				// 			Logical::Pure(PropertyValue::Value(property)) => {
1381				// 				crate::utilities::notify!("Here");
1382				// 				return type_is_subtype_with_generics(
1383				// 					(property, base_structure_arguments),
1384				// 					(ty, ty_structure_arguments),
1385				// 					state,
1386				// 					information,
1387				// 					types,
1388				// 				);
1389				// 			}
1390				// 			value => todo!("{:?}", value), // Logical::Or { based_on, left, right } => todo!(),
1391				// 			                               // Logical::Implies { on, antecedent } => todo!(),
1392				// 		}
1393				// 	}
1394				// }
1395
1396				crate::utilities::notify!("Here *on={:?}", types.get_type_by_id(*on));
1397				crate::utilities::notify!("Mismatched property");
1398
1399				SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1400			}
1401			Constructor::Awaited { .. } => todo!(),
1402			Constructor::KeyOf(on) => {
1403				if let Type::Constant(crate::Constant::String(s)) = subtype {
1404					let get_property_unbound = get_property_unbound(
1405						(*on, base_type_arguments),
1406						(
1407							Publicity::Public,
1408							&PropertyKey::String(std::borrow::Cow::Borrowed(s)),
1409							ty_structure_arguments,
1410						),
1411						false,
1412						information,
1413						types,
1414					);
1415					if get_property_unbound.is_ok() {
1416						SubTypeResult::IsSubType
1417					} else {
1418						SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1419					}
1420				} else {
1421					crate::utilities::notify!("TODO keyof stuff");
1422					SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1423				}
1424			}
1425		},
1426		Type::AliasTo { to, parameters, name: _ } => {
1427			let base_structure_arguments = if let Some(parameters) = parameters {
1428				crate::utilities::notify!("Skipping looking at parameters {:?}", parameters);
1429				base_type_arguments
1430			} else {
1431				base_type_arguments
1432			};
1433
1434			type_is_subtype_with_generics(
1435				(*to, base_structure_arguments),
1436				(ty, ty_structure_arguments),
1437				state,
1438				information,
1439				types,
1440			)
1441		}
1442		// TODO WIP nominal mechanism
1443		Type::Class { .. } => match subtype {
1444			Type::Constant(constant) => {
1445				if constant.get_backing_type() == base_type {
1446					SubTypeResult::IsSubType
1447				} else {
1448					SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1449				}
1450			}
1451			Type::Object(..) => {
1452				// WIP Nominal-ness for #128
1453				if let Some(prototype) =
1454					information.get_chain_of_info().find_map(|info| info.prototypes.get(&ty))
1455				{
1456					if *prototype == base_type {
1457						SubTypeResult::IsSubType
1458					} else {
1459						crate::utilities::notify!(
1460							"Mismatched prototype {:?} != {:?}",
1461							prototype,
1462							base_type
1463						);
1464						SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1465					}
1466				} else {
1467					crate::utilities::notify!("No prototype");
1468					SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1469				}
1470			}
1471			Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on, arguments }) => {
1472				let into = arguments.clone();
1473				let right =
1474					(*on, GenericChainLink::append(ty, ty_structure_arguments.as_ref(), &into));
1475				type_is_subtype_with_generics(
1476					(base_type, base_type_arguments),
1477					right,
1478					state,
1479					information,
1480					types,
1481				)
1482			}
1483			Type::And(left, right) => {
1484				// This only happens in predicate edge cases (with numbers)
1485				let left_result = type_is_subtype_with_generics(
1486					(base_type, base_type_arguments),
1487					(*left, ty_structure_arguments),
1488					state,
1489					information,
1490					types,
1491				);
1492
1493				if let SubTypeResult::IsSubType = left_result {
1494					left_result
1495				} else {
1496					type_is_subtype_with_generics(
1497						(base_type, base_type_arguments),
1498						(*right, ty_structure_arguments),
1499						state,
1500						information,
1501						types,
1502					)
1503				}
1504			}
1505			Type::SpecialObject(SpecialObject::Function(..)) | Type::FunctionReference(..)
1506				if base_type == TypeId::FUNCTION_TYPE =>
1507			{
1508				SubTypeResult::IsSubType
1509			}
1510			_ty => {
1511				// crate::utilities::notify!("{:?} does not match class", base_type);
1512				SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1513			}
1514		},
1515		Type::Interface { .. } => {
1516			// TODO weird that these are interfaces
1517			// If not captured above
1518			if matches!(base_type, TypeId::UNDEFINED_TYPE | TypeId::NULL_TYPE | TypeId::NEVER_TYPE)
1519			{
1520				return SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch);
1521			}
1522
1523			// TODO a bit messy
1524			match subtype {
1525				Type::Constant(constant) => {
1526					if constant.get_backing_type() == base_type {
1527						SubTypeResult::IsSubType
1528					} else {
1529						SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1530					}
1531				}
1532				Type::Object(..) => subtype_floating_properties(
1533					(base_type, base_type_arguments),
1534					(ty, ty_structure_arguments),
1535					state,
1536					information,
1537					types,
1538				),
1539				Type::SpecialObject(SpecialObject::Function(..)) => {
1540					crate::utilities::notify!("TODO implement function checking");
1541					SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1542				}
1543				Type::And(a, b) => {
1544					// TODO more
1545					crate::utilities::notify!("Here LHS interface, RHS and");
1546					if *a == base_type || *b == base_type {
1547						SubTypeResult::IsSubType
1548					} else {
1549						SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1550					}
1551				}
1552				Type::Or(_left, _right) => {
1553					unreachable!()
1554				}
1555				Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on, arguments }) => {
1556					let into = arguments.clone();
1557					let append =
1558						GenericChainLink::append(ty, ty_structure_arguments.as_ref(), &into);
1559					type_is_subtype_with_generics(
1560						(base_type, base_type_arguments),
1561						(*on, append),
1562						state,
1563						information,
1564						types,
1565					)
1566				}
1567				Type::FunctionReference(_)
1568				| Type::SpecialObject(_)
1569				| Type::Class { .. }
1570				| Type::AliasTo { .. }
1571				| Type::Interface { .. } => {
1572					crate::utilities::notify!("supertype={:?}, subtype={:?}", supertype, subtype);
1573					// TODO
1574					SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1575				}
1576				Type::Narrowed { .. } | Type::Constructor(..) | Type::RootPolyType(..) => {
1577					let arg =
1578						base_type_arguments.and_then(|args| args.get_argument_covariant(base_type));
1579
1580					crate::utilities::notify!("TODO {:?}", arg);
1581					SubTypeResult::IsSubType
1582
1583					// if let Some(args) = arg {
1584					// 	for arg in args {
1585					// 		let result = type_is_subtype_with_generics(
1586					// 			(arg, base_type_arguments),
1587					// 			(ty, ty_structure_arguments),
1588					// 			state,
1589					// 			information,
1590					// 			types,
1591					// 		);
1592
1593					// 		if let e @ SubTypeResult::IsNotSubType(_) = result {
1594					// 			return e;
1595					// 		}
1596					// 	}
1597					// 	SubTypeResult::IsSubType
1598					// } else {
1599					// 	let to = get_constraint(ty, types).unwrap();
1600
1601					// 	if to == TypeId::ANY_TYPE {
1602					// 		crate::utilities::notify!("Modify constraint for equality");
1603					// 	}
1604
1605					// 	type_is_subtype_with_generics(
1606					// 		(base_type, base_type_arguments),
1607					// 		(to, ty_structure_arguments),
1608					// 		state,
1609					// 		information,
1610					// 		types,
1611					// 	)
1612					// }
1613				}
1614			}
1615		}
1616		Type::SpecialObject(SpecialObject::Null) => {
1617			crate::utilities::notify!("rhs={:?}", subtype);
1618			SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
1619		}
1620		Type::SpecialObject(_) => todo!(),
1621	}
1622}
1623
1624fn subtype_function(
1625	(left_func, base_type_arguments): (crate::FunctionId, GenericChain),
1626	(subtype, ty, subtypepe_arguments): (&Type, TypeId, GenericChain),
1627	state: &mut State,
1628	information: &impl InformationChain,
1629	types: &TypeStore,
1630) -> SubTypeResult {
1631	let right_func = if let Type::FunctionReference(right_func)
1632	| Type::SpecialObject(SpecialObject::Function(right_func, _)) = subtype
1633	{
1634		right_func
1635	} else if let Some(constraint) = get_constraint(ty, types) {
1636		// TODO explain why get_constraint early breaks a bunch of tests
1637		let subtype = types.get_type_by_id(constraint);
1638		if let Type::FunctionReference(right_func)
1639		| Type::SpecialObject(SpecialObject::Function(right_func, _)) = subtype
1640		{
1641			right_func
1642		} else {
1643			crate::utilities::notify!("Not function after constraint!! {:?}", subtype);
1644			return SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch);
1645		}
1646	} else {
1647		crate::utilities::notify!("Not function!! {:?}", subtype);
1648		return SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch);
1649	};
1650
1651	let left_func = types.functions.get(&left_func).unwrap();
1652	let right_func = types.functions.get(right_func).unwrap();
1653
1654	for (idx, lhs_param) in left_func.parameters.parameters.iter().enumerate() {
1655		if let Some((right_param_ty, position)) =
1656			right_func.parameters.get_parameter_type_at_index(idx)
1657		{
1658			let last_mode =
1659				std::mem::replace(&mut state.mode, SubTypingMode::Covariant { position });
1660
1661			// Reverse is important
1662			let result = type_is_subtype_with_generics(
1663				(right_param_ty, subtypepe_arguments),
1664				(lhs_param.ty, base_type_arguments),
1665				state,
1666				information,
1667				types,
1668			);
1669
1670			if let err @ SubTypeResult::IsNotSubType(_) = result {
1671				let lhs = print_type(right_param_ty, types, information, true);
1672				let rhs = print_type(lhs_param.ty, types, information, true);
1673				crate::utilities::notify!(
1674					"Parameter invalid rhs ({:?} {:?}) <- lhs ({:?} {:?})",
1675					rhs,
1676					subtypepe_arguments,
1677					lhs,
1678					base_type_arguments
1679				);
1680				// TODO don't short circuit
1681				return err;
1682			}
1683
1684			state.mode = last_mode;
1685		} else {
1686			// This is allowed. TODO only in some cases
1687			// if !lhs_param.is_optional {
1688			// 	crate::utilities::notify!("Expected parameter, for non optional parameter");
1689			// 	return SubTypeResult::IsNotSubType(NonEqualityReason::MissingParameter);
1690			// }
1691		}
1692	}
1693
1694	// TODO optional and rest parameters
1695
1696	// `void` return type means anything goes here
1697	if TypeId::VOID_TYPE == left_func.return_type {
1698		SubTypeResult::IsSubType
1699	} else {
1700		let type_is_subtype_with_generics = type_is_subtype_with_generics(
1701			(left_func.return_type, base_type_arguments),
1702			(right_func.return_type, subtypepe_arguments),
1703			state,
1704			information,
1705			types,
1706		);
1707
1708		if type_is_subtype_with_generics.is_mismatch() {
1709			crate::utilities::notify!("return type invalid");
1710		}
1711
1712		type_is_subtype_with_generics
1713	}
1714}
1715
1716fn subtype_floating_properties(
1717	(base_type, base_type_arguments): (TypeId, GenericChain),
1718	(ty, subtypepe_arguments): (TypeId, GenericChain),
1719	state: &mut State,
1720	information: &impl InformationChain,
1721	types: &TypeStore,
1722) -> SubTypeResult {
1723	let reversed_flattened_properties_on_base = information
1724		.get_chain_of_info()
1725		.filter_map(|info| info.current_properties.get(&base_type).map(|v| v.iter().rev()))
1726		.flatten();
1727
1728	subtype_properties(
1729		(base_type, reversed_flattened_properties_on_base, base_type_arguments),
1730		(ty, subtypepe_arguments),
1731		state,
1732		information,
1733		types,
1734	)
1735}
1736
1737fn subtype_properties<'a, T>(
1738	(base_type, base_properties, base_type_arguments): (TypeId, T, GenericChain),
1739	(ty, subtypepe_arguments): (TypeId, GenericChain),
1740	state: &mut State,
1741	information: &impl InformationChain,
1742	types: &TypeStore,
1743) -> SubTypeResult
1744where
1745	T: Iterator<Item = &'a (Publicity, PropertyKey<'static>, PropertyValue)> + 'a,
1746{
1747	// TODO this will cause problems if not reversed at end
1748	state.mode = state.mode.one_deeper();
1749	let mut property_errors = Vec::new();
1750
1751	// Note this won't check for conditional stuff being true etc or things being deleted
1752	for (publicity, key, lhs_property) in base_properties {
1753		// crate::utilities::notify!(
1754		// 	"key {:?} with base_type_arguments={:?}",
1755		// 	key,
1756		// 	base_type_arguments
1757		// );
1758		let holding_key;
1759		let key = match key {
1760			PropertyKey::Type(key_ty) => {
1761				if let Some(base_type_arguments) = base_type_arguments {
1762					let key_ty =
1763						base_type_arguments.get_single_argument(*key_ty).unwrap_or(*key_ty);
1764					holding_key = PropertyKey::from_type(key_ty, types);
1765					&holding_key
1766				} else {
1767					key
1768				}
1769			}
1770			PropertyKey::String(_) => key,
1771		};
1772
1773		let result = check_lhs_property_is_super_type_of_rhs(
1774			(*publicity, key),
1775			(lhs_property, base_type_arguments, false),
1776			(ty, subtypepe_arguments),
1777			state,
1778			information,
1779			types,
1780		);
1781
1782		if let Err(err) = result {
1783			property_errors.push((key.into_owned(), err));
1784		}
1785	}
1786
1787	state.mode = state.mode.one_shallower();
1788
1789	if property_errors.is_empty() {
1790		if let Type::Interface { extends: Some(extends), .. } = types.get_type_by_id(base_type) {
1791			let extends_result = type_is_subtype_with_generics(
1792				(*extends, base_type_arguments),
1793				(ty, subtypepe_arguments),
1794				state,
1795				information,
1796				types,
1797			);
1798			if let e @ SubTypeResult::IsNotSubType(_) = extends_result {
1799				return e;
1800			}
1801		}
1802
1803		// Exclusive check
1804		if base_type_arguments.is_some_and(|base| base.exclusive_mode()) {
1805			use crate::types::properties;
1806
1807			let get_properties = properties::get_properties_on_single_type(
1808				ty,
1809				types,
1810				information,
1811				false,
1812				TypeId::ANY_TYPE,
1813			);
1814
1815			// Assert base_type contains all the keys of the LHS
1816			for (publicity, key, _value) in get_properties {
1817				let result = properties::get_property_unbound(
1818					(base_type, base_type_arguments),
1819					(publicity, &key, None),
1820					true,
1821					information,
1822					types,
1823				);
1824
1825				// TODO more
1826				if result.is_err() {
1827					// TODO excess property
1828					return SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch);
1829				}
1830			}
1831		}
1832
1833		// TODO type arguments
1834		if let Some(ref mut object_constraints) = state.object_constraints {
1835			let base_type = if let Some(GenericChainLink::PartiallyAppliedGenericArgumentsLink {
1836				ref from,
1837				parent_link,
1838				value: _,
1839			}) = base_type_arguments
1840			{
1841				if parent_link.is_some() {
1842					crate::utilities::notify!("TODO recursive get_from");
1843				}
1844				*from
1845			} else {
1846				base_type
1847			};
1848			object_constraints.push((ty, base_type));
1849		}
1850
1851		SubTypeResult::IsSubType
1852	} else {
1853		SubTypeResult::IsNotSubType(NonEqualityReason::PropertiesInvalid {
1854			errors: property_errors,
1855		})
1856	}
1857}
1858
1859fn check_lhs_property_is_super_type_of_rhs(
1860	(publicity, key): (Publicity, &PropertyKey<'_>),
1861	(lhs_property, base_type_arguments, optional): (&PropertyValue, GenericChain, bool),
1862	(ty, subtypepe_arguments): (TypeId, GenericChain),
1863	state: &mut State,
1864	information: &impl InformationChain,
1865	types: &TypeStore,
1866) -> Result<(), PropertyError> {
1867	match lhs_property {
1868		PropertyValue::Value(lhs_value) => {
1869			let right_result = get_property_unbound(
1870				(ty, subtypepe_arguments),
1871				(publicity, key, base_type_arguments),
1872				false,
1873				information,
1874				types,
1875			);
1876
1877			// {
1878			// 	crate::utilities::notify!("LHS value is {:?}", lhs_value);
1879			// 	crate::utilities::notify!(
1880			// 		"RHS value is {:?} {:?}",
1881			// 		right_result,
1882			// 		(key, base_type_arguments)
1883			// 	);
1884			// }
1885
1886			match right_result {
1887				Ok(LogicalOrValid::Logical(res)) => {
1888					let res = check_logical_property(
1889						(*lhs_value, base_type_arguments, optional),
1890						(res, subtypepe_arguments),
1891						state,
1892						information,
1893						types,
1894					);
1895					match res {
1896						SubTypeResult::IsSubType => Ok(()),
1897						SubTypeResult::IsNotSubType(err) => Err(PropertyError::Invalid {
1898							expected: *lhs_value,
1899							// TODO logical -> TypeId
1900							found: TypeId::UNIMPLEMENTED_ERROR_TYPE,
1901							mismatch: err,
1902						}),
1903					}
1904				}
1905				// PROXY HANDLING!!
1906				Ok(LogicalOrValid::NeedsCalculation(NeedsCalculation::Proxy(
1907					objects::Proxy { handler, over },
1908					_,
1909				))) => {
1910					crate::utilities::notify!("TODO set as well?");
1911					let get_handler = get_property_unbound(
1912						(handler, subtypepe_arguments),
1913						(
1914							Publicity::Public,
1915							&PropertyKey::String(std::borrow::Cow::Borrowed("get")),
1916							base_type_arguments,
1917						),
1918						false,
1919						information,
1920						types,
1921					);
1922
1923					if let Ok(LogicalOrValid::Logical(Logical::Pure(get_res))) = get_handler {
1924						let function = get_res.as_get_type(types);
1925						if let Type::SpecialObject(SpecialObject::Function(id, _)) =
1926							types.get_type_by_id(function)
1927						{
1928							let function = types.get_function_from_id(*id);
1929							let mut map = crate::Map::new();
1930							// `Some` weird but accounts for missing parameters
1931							if let Some((first, _)) =
1932								function.parameters.get_parameter_type_at_index(0)
1933							{
1934								map.insert(first, (CovariantContribution::TypeId(over), 0));
1935							}
1936							if let Some((second, _)) =
1937								function.parameters.get_parameter_type_at_index(1)
1938							{
1939								map.insert(
1940									second,
1941									(CovariantContribution::from(key.clone().into_owned()), 0),
1942								);
1943							}
1944							if let Some((third, _)) =
1945								function.parameters.get_parameter_type_at_index(2)
1946							{
1947								map.insert(third, (CovariantContribution::TypeId(handler), 0));
1948							}
1949
1950							let subtypepe_arguments = Some(GenericChainLink::MappedPropertyLink {
1951								parent_link: subtypepe_arguments.as_ref(),
1952								value: &map,
1953							});
1954
1955							let result = type_is_subtype_with_generics(
1956								(*lhs_value, base_type_arguments),
1957								(function.return_type, subtypepe_arguments),
1958								state,
1959								information,
1960								types,
1961							);
1962							if let SubTypeResult::IsSubType = result {
1963								Ok(())
1964							} else {
1965								// crate::utilities::notify!("One missing");
1966								Err(PropertyError::Missing)
1967							}
1968						} else {
1969							crate::utilities::notify!("{:?}", get_res);
1970
1971							check_lhs_property_is_super_type_of_rhs(
1972								(publicity, key),
1973								(lhs_property, base_type_arguments, optional),
1974								(handler, subtypepe_arguments),
1975								state,
1976								information,
1977								types,
1978							)
1979						}
1980					} else {
1981						check_lhs_property_is_super_type_of_rhs(
1982							(publicity, key),
1983							(lhs_property, base_type_arguments, optional),
1984							(handler, subtypepe_arguments),
1985							state,
1986							information,
1987							types,
1988						)
1989					}
1990				}
1991				Ok(LogicalOrValid::NeedsCalculation(NeedsCalculation::Infer { .. })) => {
1992					crate::utilities::notify!("TODO add constraint candidate");
1993					Ok(())
1994				}
1995				Err(_) => {
1996					if optional {
1997						Ok(())
1998					} else {
1999						// crate::utilities::notify!("One missing");
2000						Err(PropertyError::Missing)
2001					}
2002				}
2003			}
2004		}
2005		PropertyValue::GetterAndSetter { getter, setter } => {
2006			todo!("{:?}", (getter, setter));
2007		}
2008		PropertyValue::Getter(_getter) => {
2009			let res =
2010				get_property_unbound((ty, None), (publicity, key, None), true, information, types);
2011			crate::utilities::notify!("looking for {:?} found {:?}", key, res);
2012
2013			match res {
2014				Ok(LogicalOrValid::Logical(_res)) => {
2015					todo!("get get return type")
2016				}
2017				// TODO
2018				res => {
2019					crate::utilities::notify!("res={:?}", res);
2020					Err(PropertyError::Missing)
2021				}
2022			}
2023		}
2024		PropertyValue::Setter(_) => {
2025			let rhs =
2026				get_property_unbound((ty, None), (publicity, key, None), true, information, types);
2027
2028			match rhs {
2029				Ok(ok) => {
2030					crate::utilities::notify!("Set vs {:?}", ok);
2031					Ok(())
2032				}
2033				Err(_err) => Err(PropertyError::Missing),
2034			}
2035		}
2036		PropertyValue::Deleted => {
2037			// TODO WIP
2038			let res =
2039				get_property_unbound((ty, None), (publicity, key, None), true, information, types);
2040			if res.is_ok() {
2041				// TODO the opposite of missing
2042				Err(PropertyError::Missing)
2043			} else {
2044				// Fine !
2045				Ok(())
2046			}
2047		}
2048		PropertyValue::ConditionallyExists { condition, truthy } => {
2049			crate::utilities::notify!("Here {:?}", (key, ty, condition, truthy));
2050
2051			// TODO `NON_OPTIONAL_KEY_ARGUMENT` temp
2052			let is_optional =
2053				!matches!(*condition, TypeId::TRUE | TypeId::NON_OPTIONAL_KEY_ARGUMENT);
2054
2055			check_lhs_property_is_super_type_of_rhs(
2056				(publicity, key),
2057				(truthy, base_type_arguments, is_optional),
2058				(ty, subtypepe_arguments),
2059				state,
2060				information,
2061				types,
2062			)
2063			// if let PropertyValue::Value(lhs_value) = &**truthy {
2064			// let property = get_property_unbound(
2065			// 	(ty, subtypepe_arguments),
2066			// 	(publicity, key, base_type_arguments),
2067			// 	information,
2068			// 	types,
2069			// );
2070			// crate::utilities::notify!("property={:?}", property);
2071
2072			// if let Ok(LogicalOrValid::Logical(property)) = property {
2073			// 	// TODO for error reporting
2074			// 	let found = if let Logical::Pure(PropertyValue::Value(ref found)) = property {
2075			// 		*found
2076			// 	} else {
2077			// 		TypeId::UNIMPLEMENTED_ERROR_TYPE
2078			// 	};
2079
2080			// 	crate::utilities::notify!("{:?}", property);
2081
2082			// 	let res = check_logical_property(
2083			// 		(*lhs_value, base_type_arguments),
2084			// 		(property, subtypepe_arguments),
2085			// 		state,
2086			// 		information,
2087			// 		types,
2088			// 	);
2089
2090			// 	if let SubTypeResult::IsNotSubType(reason) = res {
2091			// 		Err(PropertyError::Invalid {
2092			// 			expected: *lhs_value,
2093			// 			found,
2094			// 			mismatch: reason,
2095			// 		})
2096			// 	} else {
2097			// 		Ok(())
2098			// 	}
2099			// } else {
2100			// 	crate::utilities::notify!("Here");
2101			// 	// Err(PropertyError::Missing)
2102			// 	// Okay if missing because of the above
2103			// 	Ok(())
2104			// }
2105			// } else {
2106			// 	crate::utilities::notify!("Here maybe errors needs to continue checking {:?}", truthy);
2107			// 	Ok(())
2108			// }
2109		}
2110		PropertyValue::Configured { on, .. } => {
2111			crate::utilities::notify!("TODO check readonly");
2112			check_lhs_property_is_super_type_of_rhs(
2113				(publicity, key),
2114				(on, base_type_arguments, optional),
2115				(ty, subtypepe_arguments),
2116				state,
2117				information,
2118				types,
2119			)
2120		}
2121	}
2122}
2123
2124fn check_logical_property(
2125	(lhs_property_value, lhs_property_value_type_arguments, optional): (TypeId, GenericChain, bool),
2126	(rhs_property, subtypepe_arguments): (Logical<PropertyValue>, GenericChain),
2127	state: &mut State,
2128	information: &impl InformationChain,
2129	types: &TypeStore,
2130) -> SubTypeResult {
2131	match rhs_property {
2132		Logical::Pure(rhs_property) => {
2133			let rhs_type = rhs_property.as_get_type(types);
2134			// crate::utilities::notify!(
2135			// 	"Checking {} with {}, against {}, left={:?}",
2136			// 	print_type(key, types, information, true),
2137			// 	print_type(property, types, information, true),
2138			// 	print_type(rhs_type, types, information, true),
2139			// 	lhs_property_value_type_arguments
2140			// );
2141
2142			type_is_subtype_with_generics(
2143				(lhs_property_value, lhs_property_value_type_arguments),
2144				(rhs_type, subtypepe_arguments),
2145				state,
2146				information,
2147				types,
2148			)
2149		}
2150		Logical::Or { condition, left, right } => {
2151			crate::utilities::notify!("{:?}", (condition, &left, &right));
2152
2153			if let (LogicalOrValid::Logical(left), LogicalOrValid::Logical(right)) = (*left, *right)
2154			{
2155				let left_result = check_logical_property(
2156					(lhs_property_value, lhs_property_value_type_arguments, optional),
2157					(left, subtypepe_arguments),
2158					state,
2159					information,
2160					types,
2161				);
2162
2163				if let SubTypeResult::IsSubType = left_result {
2164					check_logical_property(
2165						(lhs_property_value, lhs_property_value_type_arguments, optional),
2166						(right, subtypepe_arguments),
2167						state,
2168						information,
2169						types,
2170					)
2171				} else {
2172					// else return the failing result
2173					left_result
2174				}
2175			} else if optional {
2176				SubTypeResult::IsSubType
2177			} else {
2178				crate::utilities::notify!("One missing");
2179				SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
2180			}
2181		}
2182		Logical::Implies { on, antecedent } => {
2183			crate::utilities::notify!("{:?}", antecedent);
2184			check_logical_property(
2185				(lhs_property_value, lhs_property_value_type_arguments, optional),
2186				(
2187					*on,
2188					GenericChainLink::append(
2189						TypeId::UNIMPLEMENTED_ERROR_TYPE,
2190						subtypepe_arguments.as_ref(),
2191						&antecedent,
2192					),
2193				),
2194				state,
2195				information,
2196				types,
2197			)
2198		}
2199		Logical::BasedOnKey(kind) => match kind {
2200			BasedOnKey::Left { value, key_arguments } => {
2201				let property_generics = Some(GenericChainLink::MappedPropertyLink {
2202					parent_link: subtypepe_arguments.as_ref(),
2203					value: &key_arguments,
2204				});
2205				check_logical_property(
2206					(lhs_property_value, lhs_property_value_type_arguments, optional),
2207					(*value, property_generics),
2208					state,
2209					information,
2210					types,
2211				)
2212			}
2213			BasedOnKey::Right(PropertyOn { on, key }) => {
2214				if let Type::RootPolyType(PolyNature::MappedGeneric { name: _, extends }) =
2215					types.get_type_by_id(key)
2216				{
2217					type_is_subtype_of_property_mapped_key(
2218						MappedKey { value: (*extends).into(), key },
2219						(lhs_property_value, lhs_property_value_type_arguments, optional),
2220						(on, subtypepe_arguments),
2221						state,
2222						information,
2223						types,
2224					)
2225				} else {
2226					let filter = get_constraint(key, types).unwrap_or(key);
2227
2228					let properties = get_properties_on_single_type2(
2229						(on, subtypepe_arguments),
2230						types,
2231						information,
2232						filter,
2233					);
2234					for (_key, rhs_property, _args) in properties {
2235						let result = check_logical_property(
2236							(lhs_property_value, lhs_property_value_type_arguments, optional),
2237							(Logical::Pure(rhs_property), subtypepe_arguments),
2238							state,
2239							information,
2240							types,
2241						);
2242						if result.is_mismatch() {
2243							return result;
2244						}
2245					}
2246					SubTypeResult::IsSubType
2247				}
2248			}
2249		},
2250	}
2251}
2252
2253pub struct MappedKey {
2254	/// covariant contribution allows for slices and `PropertyKey::String`
2255	pub value: CovariantContribution,
2256	/// This points towards the `Type::RootPolyType(PolyNature::MappedGeneric)`
2257	pub key: TypeId,
2258}
2259
2260pub fn type_is_subtype_of_property_mapped_key(
2261	mapped_key: MappedKey,
2262	(base, property_generics, optional): (TypeId, GenericChain, bool),
2263	(ty, subtypepe_arguments): (TypeId, GenericChain),
2264	state: &mut State,
2265	information: &impl InformationChain,
2266	types: &TypeStore,
2267) -> SubTypeResult {
2268	// TODO use covariant contribution as property key. Also what about slices on types?
2269	match mapped_key.value {
2270		CovariantContribution::String(ref s) => {
2271			{
2272				crate::utilities::notify!(
2273					"Reading {:?}, with {:?} {:?}",
2274					types.get_type_by_id(ty),
2275					s,
2276					(property_generics.as_ref(), subtypepe_arguments.as_ref())
2277				);
2278			}
2279			let right_property = get_property_unbound(
2280				(ty, subtypepe_arguments),
2281				(
2282					Publicity::Public,
2283					&PropertyKey::String(std::borrow::Cow::Owned(s.to_owned())),
2284					None,
2285				),
2286				false,
2287				information,
2288				types,
2289			);
2290
2291			match right_property {
2292				Ok(LogicalOrValid::Logical(right_property)) => {
2293					let map = crate::Map::from_iter([(mapped_key.key, (mapped_key.value, 0))]);
2294					let property_generics = Some(GenericChainLink::MappedPropertyLink {
2295						parent_link: property_generics.as_ref(),
2296						value: &map,
2297					});
2298					let result = check_logical_property(
2299						(base, property_generics, optional),
2300						(right_property, subtypepe_arguments),
2301						state,
2302						information,
2303						types,
2304					);
2305
2306					crate::utilities::notify!("Got {:?}", result);
2307
2308					result
2309				}
2310				// TODO
2311				_res => {
2312					crate::utilities::notify!("Missing");
2313					if optional {
2314						SubTypeResult::IsSubType
2315					} else {
2316						SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
2317					}
2318				}
2319			}
2320		}
2321		CovariantContribution::TypeId(key_ty) => {
2322			match types.get_type_by_id(key_ty) {
2323				Type::Narrowed { narrowed_to: to, .. }
2324				| Type::AliasTo { to, name: _, parameters: _ } => type_is_subtype_of_property_mapped_key(
2325					MappedKey { value: (*to).into(), key: mapped_key.key },
2326					(base, property_generics, optional),
2327					(ty, subtypepe_arguments),
2328					state,
2329					information,
2330					types,
2331				),
2332				Type::And(left, right) => {
2333					let left = type_is_subtype_of_property_mapped_key(
2334						MappedKey { value: (*left).into(), key: mapped_key.key },
2335						(base, property_generics, optional),
2336						(ty, subtypepe_arguments),
2337						state,
2338						information,
2339						types,
2340					);
2341					if left.is_mismatch() {
2342						type_is_subtype_of_property_mapped_key(
2343							MappedKey { value: (*right).into(), key: mapped_key.key },
2344							(base, property_generics, optional),
2345							(ty, subtypepe_arguments),
2346							state,
2347							information,
2348							types,
2349						)
2350					} else {
2351						left
2352					}
2353				}
2354				Type::Or(left, right) => {
2355					let left = type_is_subtype_of_property_mapped_key(
2356						MappedKey { value: (*left).into(), key: mapped_key.key },
2357						(base, property_generics, optional),
2358						(ty, subtypepe_arguments),
2359						state,
2360						information,
2361						types,
2362					);
2363					if left.is_mismatch() {
2364						left
2365					} else {
2366						type_is_subtype_of_property_mapped_key(
2367							MappedKey { value: (*right).into(), key: mapped_key.key },
2368							(base, property_generics, optional),
2369							(ty, subtypepe_arguments),
2370							state,
2371							information,
2372							types,
2373						)
2374					}
2375				}
2376				Type::RootPolyType(_) => {
2377					// TODO get_covariant contribution
2378					if let Some(value) =
2379						property_generics.and_then(|args| args.get_single_argument(key_ty))
2380					{
2381						type_is_subtype_of_property_mapped_key(
2382							MappedKey { value: value.into(), key: mapped_key.key },
2383							(base, property_generics, optional),
2384							(ty, subtypepe_arguments),
2385							state,
2386							information,
2387							types,
2388						)
2389					} else {
2390						todo!("no value {:?}", (ty, property_generics))
2391					}
2392				}
2393				Type::Constructor(Constructor::Property { .. }) => {
2394					todo!()
2395				}
2396				Type::Constructor(Constructor::KeyOf(key_of_ty)) => {
2397					let properties = get_properties_on_single_type2(
2398						(*key_of_ty, property_generics),
2399						types,
2400						information,
2401						TypeId::ANY_TYPE,
2402					);
2403					for (key, _, _) in properties {
2404						let value = match key {
2405							PropertyKey::Type(ty) => CovariantContribution::TypeId(ty),
2406							PropertyKey::String(str) => {
2407								CovariantContribution::String(str.into_owned())
2408							}
2409						};
2410						crate::utilities::notify!("Here {:?}", value);
2411						let result = type_is_subtype_of_property_mapped_key(
2412							MappedKey { value, key: mapped_key.key },
2413							(base, property_generics, optional),
2414							(ty, subtypepe_arguments),
2415							state,
2416							information,
2417							types,
2418						);
2419
2420						if result.is_mismatch() {
2421							return SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch);
2422						}
2423					}
2424					SubTypeResult::IsSubType
2425				}
2426				Type::Constructor(_) => todo!(),
2427				Type::PartiallyAppliedGenerics(_) => todo!(),
2428				Type::Interface { .. } => todo!(),
2429				Type::Class { .. } => todo!(),
2430				Type::Constant(_) => {
2431					let right_property = get_property_unbound(
2432						(ty, subtypepe_arguments),
2433						(Publicity::Public, &PropertyKey::Type(key_ty), subtypepe_arguments),
2434						true,
2435						information,
2436						types,
2437					);
2438
2439					match right_property {
2440						Ok(LogicalOrValid::Logical(right_property)) => {
2441							let map =
2442								crate::Map::from_iter([(mapped_key.key, (mapped_key.value, 0))]);
2443							let property_generics = Some(GenericChainLink::MappedPropertyLink {
2444								parent_link: property_generics.as_ref(),
2445								value: &map,
2446							});
2447							check_logical_property(
2448								(base, property_generics, optional),
2449								(right_property, subtypepe_arguments),
2450								state,
2451								information,
2452								types,
2453							)
2454						}
2455						// TODO
2456						_res => {
2457							if optional {
2458								SubTypeResult::IsSubType
2459							} else {
2460								SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
2461							}
2462						}
2463					}
2464				}
2465				Type::FunctionReference(_) => todo!(),
2466				Type::Object(_) => todo!(),
2467				Type::SpecialObject(_) => todo!(),
2468			}
2469		}
2470		value => todo!("{:?}", value),
2471	}
2472}
2473
2474/// TODO integrate `set_restriction`, but it can't create a type ? maybe object restriction should be logically.
2475/// maybe sub function
2476pub fn type_is_subtype_of_property(
2477	(property, property_generics): (&Logical<PropertyValue>, GenericChain),
2478	ty: TypeId,
2479	state: &mut State,
2480	information: &impl InformationChain,
2481	types: &TypeStore,
2482) -> SubTypeResult {
2483	match property {
2484		Logical::Pure(prop) => type_is_subtype_with_generics(
2485			(prop.as_set_type(types), property_generics),
2486			(ty, GenericChain::None),
2487			state,
2488			information,
2489			types,
2490		),
2491		Logical::Or { condition: _, left, right } => {
2492			let left_result = if let LogicalOrValid::Logical(left) = &**left {
2493				type_is_subtype_of_property(
2494					(left, property_generics),
2495					ty,
2496					state,
2497					information,
2498					types,
2499				)
2500			} else {
2501				SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
2502			};
2503			if let SubTypeResult::IsSubType = left_result {
2504				left_result
2505			} else if let LogicalOrValid::Logical(right) = &**right {
2506				type_is_subtype_of_property(
2507					(right, property_generics),
2508					ty,
2509					state,
2510					information,
2511					types,
2512				)
2513			} else {
2514				SubTypeResult::IsNotSubType(NonEqualityReason::Mismatch)
2515			}
2516		}
2517		Logical::Implies { on, antecedent } => {
2518			let property_generics = GenericChainLink::append(
2519				TypeId::UNIMPLEMENTED_ERROR_TYPE,
2520				property_generics.as_ref(),
2521				antecedent,
2522			);
2523			type_is_subtype_of_property((on, property_generics), ty, state, information, types)
2524		}
2525		Logical::BasedOnKey(on) => {
2526			// if let BasedOnKey::Right { on, key } = on {
2527			// 	if let Type::RootPolyType(PolyNature::MappedGeneric { name: _, extends }) =
2528			// 		types.get_type_by_id(*filter)
2529			// 	{
2530			// 		type_is_subtype_of_(
2531			// 			Some(MappedKey { value: *extends, key: *filter }),
2532			// 			(property, property_generics),
2533			// 			ty,
2534			// 			state,
2535			// 			information,
2536			// 			types,
2537			// 		)
2538			// 	} else {
2539			// 		crate::utilities::notify!("TODO, returning IsSubType {:?}", on);
2540			// 		SubTypeResult::IsSubType
2541			// 	}
2542			// } else {
2543			crate::utilities::notify!("TODO, returning IsSubType {:?}", on);
2544			SubTypeResult::IsSubType
2545			// }
2546		}
2547	}
2548}
2549
2550impl NonEqualityReason {
2551	pub(crate) fn _into_error_message(self, _information: &GeneralContext) -> Vec<String> {
2552		match self {
2553			NonEqualityReason::GenericParameterMismatch
2554			| NonEqualityReason::MissingParameter
2555			| NonEqualityReason::Mismatch => Vec::new(),
2556			NonEqualityReason::PropertiesInvalid { errors } => {
2557				errors.into_iter().map(|error| format!("{error:?}")).collect()
2558			}
2559			NonEqualityReason::TooStrict => todo!(),
2560			NonEqualityReason::Excess => todo!(),
2561		}
2562	}
2563}
2564
2565pub type SliceArguments =
2566	TriMap<TypeId, super::generics::contributions::CovariantContribution, ContributionDepth>;
2567
2568// #[derive(Debug, Default)]
2569// pub struct SliceArguments {
2570// 	pub(crate) covariant: TriMap<TypeId, super::generics::contributions::CovariantContribution, ContributionDepth>,
2571// 	/// WIP for mapped inference
2572// 	pub(crate) contravariant: TriMap<TypeId, super::generics::contributions::CovariantContribution, ContributionDepth>
2573// }
2574
2575/// `allow_casts=true` is for property keys
2576pub(crate) fn slice_matches_type(
2577	(base, base_type_arguments): (TypeId, Option<super::GenericChainLink>),
2578	slice: &str,
2579	mut contributions: Option<&mut SliceArguments>,
2580	information: &impl InformationChain,
2581	types: &TypeStore,
2582	allow_casts: bool,
2583) -> bool {
2584	let base_ty = types.get_type_by_id(base);
2585
2586	// {
2587	// 	crate::utilities::notify!(
2588	// 		"Slice checking {} ({:?}) :>= '{}'",
2589	// 		print_type(base, types, information, true),
2590	// 		base_type_arguments,
2591	// 		slice
2592	// 	);
2593	// }
2594
2595	// TODO cast string
2596	if allow_casts {
2597		if base == TypeId::ANY_TYPE {
2598			return true;
2599		} else if base == TypeId::BOOLEAN_TYPE {
2600			return slice == "true" || slice == "false";
2601		} else if base == TypeId::NUMBER_TYPE {
2602			return slice.parse::<usize>().is_ok();
2603		} else if base == TypeId::STRING_TYPE {
2604			// crate::utilities::notify!("Here!");
2605			// TODO is this okay?
2606			return slice.parse::<usize>().is_err();
2607		}
2608	}
2609	match base_ty {
2610		Type::Constant(Constant::String(base_string)) => {
2611			if let Some(transform) = base_type_arguments.and_then(|a| a.get_string_transform()) {
2612				apply_string_intrinsic(transform, base_string).as_str() == slice
2613			} else if base_type_arguments.is_some_and(|a| a.is_case_insensitive()) {
2614				base_string.to_lowercase() == slice.to_lowercase()
2615			} else {
2616				base_string == slice
2617			}
2618		}
2619		Type::RootPolyType(rpt) => {
2620			// TODO temp fix to set keyof arguments
2621			{
2622				let constraint = rpt.get_constraint();
2623				if let Type::Constructor(Constructor::KeyOf { .. }) =
2624					types.get_type_by_id(constraint)
2625				{
2626					let mut new_contributions = SliceArguments::default();
2627					let _ = slice_matches_type(
2628						(constraint, base_type_arguments),
2629						slice,
2630						Some(&mut new_contributions),
2631						information,
2632						types,
2633						allow_casts,
2634					);
2635					if let Some(ref mut contributions) = contributions {
2636						contributions.extend(new_contributions);
2637					}
2638				}
2639			}
2640
2641			if let Some(argument) = base_type_arguments.and_then(|v| v.get_single_argument(base)) {
2642				slice_matches_type(
2643					(argument, base_type_arguments),
2644					slice,
2645					contributions,
2646					information,
2647					types,
2648					allow_casts,
2649				)
2650			} else if let Some(contributions) = contributions {
2651				// assert!(rpt.is_substitutable(), "{:?}", rpt);
2652				let constraint = rpt.get_constraint();
2653				let res = slice_matches_type(
2654					(constraint, base_type_arguments),
2655					slice,
2656					Some(contributions),
2657					information,
2658					types,
2659					allow_casts,
2660				);
2661				if res {
2662					contributions
2663						.insert(base, (CovariantContribution::String(slice.to_owned()), 0));
2664				}
2665				res
2666			} else {
2667				false
2668			}
2669		}
2670		Type::Narrowed { narrowed_to: to, .. } | Type::AliasTo { to, .. } => slice_matches_type(
2671			(*to, base_type_arguments),
2672			slice,
2673			contributions,
2674			information,
2675			types,
2676			allow_casts,
2677		),
2678		Type::Or(l, r) => {
2679			// TODO temp for
2680			let mut new_contributions = SliceArguments::default();
2681			let matches = slice_matches_type(
2682				(*l, base_type_arguments),
2683				slice,
2684				Some(&mut new_contributions),
2685				information,
2686				types,
2687				allow_casts,
2688			);
2689			if matches {
2690				if let Some(ref mut contributions) = contributions {
2691					contributions.extend(new_contributions);
2692				}
2693				true
2694			} else {
2695				// TODO clear contributions
2696				slice_matches_type(
2697					(*r, base_type_arguments),
2698					slice,
2699					contributions,
2700					information,
2701					types,
2702					allow_casts,
2703				)
2704			}
2705		}
2706		Type::And(l, r) => {
2707			let mut new_contributions = SliceArguments::default();
2708			let matches = slice_matches_type(
2709				(*l, base_type_arguments),
2710				slice,
2711				Some(&mut new_contributions),
2712				information,
2713				types,
2714				allow_casts,
2715			);
2716			if matches {
2717				if let Some(ref mut contributions) = contributions {
2718					contributions.extend(new_contributions);
2719				}
2720				slice_matches_type(
2721					(*r, base_type_arguments),
2722					slice,
2723					contributions,
2724					information,
2725					types,
2726					allow_casts,
2727				)
2728			} else {
2729				false
2730			}
2731		}
2732		Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
2733			on:
2734				transform @ (TypeId::STRING_CAPITALIZE
2735				| TypeId::STRING_UNCAPITALIZE
2736				| TypeId::STRING_LOWERCASE
2737				| TypeId::STRING_UPPERCASE),
2738			arguments,
2739		}) => {
2740			let matches_constraint = match *transform {
2741				TypeId::STRING_CAPITALIZE => slice.chars().next().map_or(true, char::is_uppercase),
2742				TypeId::STRING_UNCAPITALIZE => {
2743					slice.chars().next().map_or(true, char::is_lowercase)
2744				}
2745				TypeId::STRING_LOWERCASE => slice.chars().all(char::is_lowercase),
2746				TypeId::STRING_UPPERCASE => slice.chars().all(char::is_uppercase),
2747				_ => unreachable!(),
2748			};
2749
2750			if matches_constraint {
2751				let generic_chain_link = Some(GenericChainLink::SpecialGenericChainLink {
2752					parent_link: base_type_arguments.as_ref(),
2753					special: SpecialGenericChainLink::CaseTransform { transform: *transform },
2754				});
2755				let inner = arguments.get_structure_restriction(TypeId::STRING_GENERIC).unwrap();
2756
2757				let mut new_contributions = SliceArguments::default();
2758				// TODO any contributions in here SHOULD be wrapped in case insensitive
2759				let matches = slice_matches_type(
2760					(inner, generic_chain_link),
2761					slice,
2762					Some(&mut new_contributions),
2763					information,
2764					types,
2765					allow_casts,
2766				);
2767				if let (true, Some(current)) = (matches, contributions) {
2768					crate::utilities::notify!("{:?}", new_contributions);
2769					for (id, (c, d)) in new_contributions {
2770						current
2771							.insert(id, (CovariantContribution::CaseInsensitive(Box::new(c)), d));
2772					}
2773					crate::utilities::notify!("{:?}", current);
2774				}
2775				matches
2776			} else {
2777				false
2778			}
2779		}
2780		Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
2781			on: TypeId::CASE_INSENSITIVE,
2782			arguments,
2783		}) => {
2784			let base_type_arguments = Some(GenericChainLink::SpecialGenericChainLink {
2785				parent_link: base_type_arguments.as_ref(),
2786				special: SpecialGenericChainLink::CaseInsensitive,
2787			});
2788			let inner = arguments.get_structure_restriction(TypeId::STRING_GENERIC).unwrap();
2789			slice_matches_type(
2790				(inner, base_type_arguments),
2791				slice,
2792				contributions,
2793				information,
2794				types,
2795				allow_casts,
2796			)
2797		}
2798		Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
2799			on: TypeId::NOT_RESTRICTION,
2800			arguments,
2801		}) => {
2802			// Here don't have to use disjoint
2803			let argument = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap();
2804			// TODO what to do about contributions :/
2805			let matches = slice_matches_type(
2806				(argument, base_type_arguments),
2807				slice,
2808				contributions,
2809				information,
2810				types,
2811				allow_casts,
2812			);
2813			// crate::utilities::notify!("negated slice arguments={:?}", _k);
2814			!matches
2815		}
2816		Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { on, arguments: _ })
2817			if allow_casts && intrinsics::is_ezno_number_intrinsic(*on) =>
2818		{
2819			// Special behavior here to treat numerical property keys (which are strings) as numbers
2820			if let Ok(value) = slice.parse::<f64>() {
2821				number_matches_type(
2822					(base, base_type_arguments),
2823					value,
2824					contributions,
2825					information,
2826					types,
2827				)
2828			} else {
2829				false
2830			}
2831		}
2832		Type::Constructor(super::Constructor::KeyOf(on)) => {
2833			let argument =
2834				(Publicity::Public, &PropertyKey::String(std::borrow::Cow::Borrowed(slice)), None);
2835
2836			let arg = base_type_arguments
2837				.as_ref()
2838				.and_then(|link| link.get_single_argument(*on))
2839				.unwrap_or(*on);
2840
2841			let property = get_property_unbound(
2842				(arg, base_type_arguments),
2843				argument,
2844				true,
2845				information,
2846				types,
2847			);
2848
2849			// crate::utilities::notify!("Here {:?}", property);
2850
2851			if let Ok(LogicalOrValid::Logical(property)) = property {
2852				// For mapped types
2853				if let Some(contributions) = contributions {
2854					// WIP!!
2855					let is_writable =
2856						if let Logical::Pure(PropertyValue::Configured { on: _, ref descriptor }) =
2857							property
2858						{
2859							descriptor.writable
2860						} else {
2861							// TODO might be missing something here via LogicalOr etc
2862							crate::utilities::notify!("Might be missing {:?}", property);
2863							TypeId::TRUE
2864						};
2865
2866					// WIP!!
2867					let is_defined = if let Logical::Pure(PropertyValue::ConditionallyExists {
2868						ref condition,
2869						..
2870					}) = property
2871					{
2872						*condition
2873					} else {
2874						// TODO might be missing something here via LogicalOr etc
2875						crate::utilities::notify!("Might be missing {:?}", property);
2876						TypeId::TRUE
2877					};
2878
2879					contributions.insert(
2880						TypeId::WRITABLE_KEY_ARGUMENT,
2881						(CovariantContribution::TypeId(is_writable), 0),
2882					);
2883					contributions.insert(
2884						TypeId::NON_OPTIONAL_KEY_ARGUMENT,
2885						(CovariantContribution::TypeId(is_defined), 0),
2886					);
2887					crate::utilities::notify!(
2888						"For MT set: (is_writable, is_defined)={:?}",
2889						(is_writable, is_defined)
2890					);
2891				}
2892
2893				true
2894			} else {
2895				false
2896			}
2897		}
2898		Type::Constant(Constant::Number(base)) => {
2899			crate::utilities::notify!("Here");
2900			if let Ok(slice_as_float) = slice.parse::<f64>() {
2901				*base == slice_as_float
2902			} else {
2903				false
2904			}
2905		}
2906		Type::Constructor(super::Constructor::BinaryOperator {
2907			lhs,
2908			rhs,
2909			operator: MathematicalOrBitwiseOperation::Add,
2910			result: _,
2911		}) => {
2912			let lhs = base_type_arguments
2913				.as_ref()
2914				.and_then(|link| link.get_single_argument(*lhs))
2915				.unwrap_or(*lhs);
2916
2917			let rhs = base_type_arguments
2918				.as_ref()
2919				.and_then(|link| link.get_single_argument(*rhs))
2920				.unwrap_or(*rhs);
2921
2922			if let Type::Constant(Constant::String(prefix)) = types.get_type_by_id(lhs) {
2923				if let Some(after) = slice.strip_prefix(prefix) {
2924					slice_matches_type(
2925						(rhs, base_type_arguments),
2926						after,
2927						contributions,
2928						information,
2929						types,
2930						allow_casts,
2931					)
2932				} else {
2933					false
2934				}
2935			} else if let Type::Constant(Constant::String(suffix)) = types.get_type_by_id(rhs) {
2936				if let Some(before) = slice.strip_suffix(suffix) {
2937					slice_matches_type(
2938						(lhs, base_type_arguments),
2939						before,
2940						contributions,
2941						information,
2942						types,
2943						allow_casts,
2944					)
2945				} else {
2946					false
2947				}
2948			} else {
2949				let lhs = types.get_type_by_id(lhs);
2950				let rhs = types.get_type_by_id(rhs);
2951				crate::utilities::notify!(
2952					"More complex type here, returning false. lhs={:?}, rhs={:?}, {:?}",
2953					lhs,
2954					rhs,
2955					base_type_arguments
2956				);
2957				false
2958			}
2959		}
2960		_ => {
2961			if base == TypeId::STRING_TYPE || base == TypeId::ANY_TYPE {
2962				true
2963			} else {
2964				crate::utilities::notify!("Cannot match key {:?}", base_ty);
2965				false
2966			}
2967		}
2968	}
2969}
2970
2971// TODO keyof
2972#[allow(clippy::only_used_in_recursion)]
2973pub(crate) fn number_matches_type(
2974	(base, base_type_arguments): (TypeId, Option<super::GenericChainLink>),
2975	number: f64,
2976	mut contributions: Option<&mut SliceArguments>,
2977	information: &impl InformationChain,
2978	types: &TypeStore,
2979) -> bool {
2980	match types.get_type_by_id(base) {
2981		Type::Constant(cst) => {
2982			if let Constant::Number(base_number) = cst {
2983				*base_number == number
2984			} else {
2985				false
2986			}
2987		}
2988		Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
2989			on: TypeId::MULTIPLE_OF,
2990			arguments,
2991		}) => {
2992			let argument = arguments.get_structure_restriction(TypeId::NUMBER_GENERIC).unwrap();
2993			if let Type::Constant(Constant::Number(argument)) = types.get_type_by_id(argument) {
2994				let number: ordered_float::NotNan<f64> = number.try_into().unwrap();
2995				(number % argument) == 0.
2996			} else {
2997				crate::utilities::notify!("Here?");
2998				false
2999			}
3000		}
3001		Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
3002			on: TypeId::LESS_THAN,
3003			arguments: _,
3004		}) => {
3005			todo!()
3006			// let lhs_range = intrinsics::get_range(base, types).unwrap();
3007			// intrinsics::FloatRange::new_single(number.try_into().unwrap()).contained_in(lhs_range)
3008		}
3009		Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
3010			on: TypeId::GREATER_THAN,
3011			arguments: _,
3012		}) => {
3013			todo!()
3014			// let lhs_range = intrinsics::get_range(base, types).unwrap();
3015			// intrinsics::FloatRange::new_single(number.try_into().unwrap()).contained_in(lhs_range)
3016		}
3017		Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
3018			on: TypeId::NOT_RESTRICTION,
3019			arguments,
3020		}) => {
3021			let argument = arguments.get_structure_restriction(TypeId::T_TYPE).unwrap();
3022
3023			!number_matches_type(
3024				(argument, base_type_arguments),
3025				number,
3026				contributions,
3027				information,
3028				types,
3029			)
3030		}
3031		Type::Or(l, r) => {
3032			// TODO temp for
3033			let mut new_contributions = SliceArguments::default();
3034			let matches = number_matches_type(
3035				(*l, base_type_arguments),
3036				number,
3037				Some(&mut new_contributions),
3038				information,
3039				types,
3040			);
3041			if matches {
3042				if let Some(ref mut contributions) = contributions {
3043					contributions.extend(new_contributions);
3044				}
3045				true
3046			} else {
3047				// TODO clear contributions
3048				number_matches_type(
3049					(*r, base_type_arguments),
3050					number,
3051					contributions,
3052					information,
3053					types,
3054				)
3055			}
3056		}
3057		Type::And(l, r) => {
3058			let mut new_contributions = SliceArguments::default();
3059			let matches = number_matches_type(
3060				(*l, base_type_arguments),
3061				number,
3062				Some(&mut new_contributions),
3063				information,
3064				types,
3065			);
3066			if matches {
3067				if let Some(ref mut contributions) = contributions {
3068					contributions.extend(new_contributions);
3069				}
3070				number_matches_type(
3071					(*r, base_type_arguments),
3072					number,
3073					contributions,
3074					information,
3075					types,
3076				)
3077			} else {
3078				false
3079			}
3080		}
3081		ty => {
3082			crate::utilities::notify!("TODO number matches ty={:?}", ty);
3083			true
3084		}
3085	}
3086}