Skip to main content

ezno_checker/types/
calling.rs

1use source_map::{BaseSpan, Nullable, SpanWithSource};
2
3use crate::{
4	context::{invocation::CheckThings, CallCheckingBehavior, Environment, InformationChain},
5	diagnostics::{
6		InfoDiagnostic, TypeCheckError, TypeCheckWarning, TypeStringRepresentation,
7		VariableUsedInTDZ,
8	},
9	events::{
10		application::ApplicationInput, apply_events, ApplicationResult, Event, RootReference,
11	},
12	features::{
13		constant_functions::{
14			call_constant_function, CallSiteTypeArguments, ConstantFunctionError, ConstantOutput,
15		},
16		objects::{ObjectBuilder, SpecialObject},
17	},
18	subtyping::{
19		type_is_subtype, type_is_subtype_with_generics, State, SubTypeResult, SubTypingMode,
20		SubTypingOptions,
21	},
22	types::{
23		functions::{FunctionBehavior, FunctionEffect, FunctionType},
24		generics::substitution::SubstitutionArguments,
25		helpers::get_structure_arguments_based_on_object_constraint,
26		logical::{Invalid, Logical, LogicalOrValid, NeedsCalculation, PossibleLogical},
27		properties::AccessMode,
28		substitute, GenericChainLink, ObjectNature, PartiallyAppliedGenerics, Type,
29	},
30	FunctionId, GenericTypeParameters, ReadFromFS, SpecialExpressions, TypeId,
31};
32
33use super::{
34	generics::{contributions::Contributions, generic_type_arguments::GenericArguments},
35	get_constraint,
36	properties::PropertyKey,
37	Constructor, GenericChain, PolyNature, TypeRestrictions, TypeStore,
38};
39
40/// Other information to do with calling
41#[derive(Clone, Copy)]
42pub struct CallingInput {
43	/// Also depicts what happens with `this`
44	pub called_with_new: CalledWithNew,
45	pub call_site: SpanWithSource,
46
47	/// An option to invocation
48	pub max_inline: u16,
49}
50
51#[derive(Clone, Copy, Debug, Default, binary_serialize_derive::BinarySerializable)]
52pub enum ThisValue {
53	Passed(TypeId),
54	/// Or pick from [`Constructor::Property`]
55	#[default]
56	UseParent,
57}
58
59impl ThisValue {
60	pub(crate) fn get(
61		self,
62		environment: &mut Environment,
63		types: &TypeStore,
64		position: SpanWithSource,
65	) -> TypeId {
66		match self {
67			ThisValue::Passed(value) => value,
68			ThisValue::UseParent => environment.get_value_of_this(types, position),
69		}
70	}
71
72	pub(crate) fn get_passed(self) -> Option<TypeId> {
73		match self {
74			ThisValue::Passed(value) => Some(value),
75			ThisValue::UseParent => None,
76		}
77	}
78}
79
80/// For diagnostics
81#[derive(Copy, Clone, Default)]
82pub enum CallingContext {
83	#[default]
84	Regular,
85	Getter,
86	Setter,
87	JSX,
88	TemplateLiteral,
89	Super,
90	Iteration,
91}
92
93#[derive(Default)]
94pub struct CallingDiagnostics {
95	pub errors: Vec<FunctionCallingError>,
96	pub warnings: Vec<crate::diagnostics::TypeCheckWarning>,
97	pub info: Vec<crate::diagnostics::InfoDiagnostic>,
98}
99
100impl CallingDiagnostics {
101	pub(crate) fn append_to(
102		self,
103		context: CallingContext,
104		diagnostics_container: &mut crate::DiagnosticsContainer,
105	) {
106		self.errors.into_iter().for_each(|error| {
107			let error = match context {
108				CallingContext::Regular => TypeCheckError::FunctionCallingError(error),
109				CallingContext::JSX => TypeCheckError::JSXCallingError(error),
110				CallingContext::TemplateLiteral => {
111					TypeCheckError::TemplateLiteralCallingError(error)
112				}
113				CallingContext::Getter => TypeCheckError::GetterCallingError(error),
114				CallingContext::Setter => TypeCheckError::SetterCallingError(error),
115				CallingContext::Super => TypeCheckError::SuperCallError(error),
116				// TODO maybe separate error (even thoguh this should not occur)
117				#[allow(clippy::match_same_arms)]
118				CallingContext::Iteration => TypeCheckError::FunctionCallingError(error),
119			};
120			diagnostics_container.add_error(error);
121		});
122		self.warnings.into_iter().for_each(|warning| diagnostics_container.add_warning(warning));
123		self.info.into_iter().for_each(|crate::diagnostics::InfoDiagnostic(message, position)| {
124			diagnostics_container.add_info(crate::diagnostics::Diagnostic::Position {
125				reason: message,
126				position,
127				kind: crate::diagnostics::DiagnosticKind::Info,
128			});
129		});
130	}
131}
132
133#[derive(Clone, Debug, binary_serialize_derive::BinarySerializable)]
134pub struct SynthesisedArgument {
135	pub(crate) spread: bool,
136	pub(crate) value: TypeId,
137	pub(crate) position: SpanWithSource,
138}
139
140impl SynthesisedArgument {
141	pub fn non_spread_type(&self) -> Result<TypeId, ()> {
142		if self.spread {
143			Err(())
144		} else {
145			Ok(self.value)
146		}
147	}
148}
149
150/// Intermediate step for inference
151pub struct UnsynthesisedArgument<'a, A: crate::ASTImplementation> {
152	pub spread: bool,
153	pub expression: &'a A::Expression<'a>,
154}
155
156/// Intermediate type for calling a function
157///
158/// Generic arguments handled with `Logical::Implies`
159#[derive(Debug, Clone, binary_serialize_derive::BinarySerializable)]
160pub struct FunctionLike {
161	pub(crate) function: FunctionId,
162	/// For generic calls
163	pub(crate) from: Option<TypeId>,
164	/// From, maybe ignored if [`CalledWithNew`] overrides
165	pub(crate) this_value: ThisValue,
166}
167
168pub struct CallingOutput {
169	pub called: Option<FunctionId>,
170	/// TODO should this be
171	pub result: Option<ApplicationResult>,
172	pub special: Option<SpecialExpressions>,
173	// pub special: Option<SpecialExpressions>,
174	pub result_was_const_computation: bool,
175}
176
177pub struct BadCallOutput {
178	/// This might be from specialisation
179	pub returned_type: TypeId,
180}
181
182/// Also synthesise arguments in terms of expected types
183pub fn call_type_handle_errors<T: crate::ReadFromFS, A: crate::ASTImplementation>(
184	ty: TypeId,
185	call_site_type_arguments: Option<Vec<(TypeId, SpanWithSource)>>,
186	arguments: &[UnsynthesisedArgument<A>],
187	input: CallingInput,
188	environment: &mut Environment,
189	checking_data: &mut crate::CheckingData<T, A>,
190	_expected: TypeId,
191) -> (TypeId, Option<SpecialExpressions>) {
192	let call_site = input.call_site;
193
194	// input.this_value
195	let callable = get_logical_callable_from_type(ty, None, None, &checking_data.types);
196
197	match callable {
198		Ok(LogicalOrValid::Logical(callable)) => {
199			// crate::utilities::notify!("{:?}", callable);
200
201			// Fix as `.map` doesn't get this passed down
202			let parent_arguments = if let Logical::Pure(FunctionLike {
203				this_value: ThisValue::Passed(this_passed),
204				..
205			}) = callable
206			{
207				if let Type::Object(..) = checking_data.types.get_type_by_id(this_passed) {
208					if let Some(arguments) = get_structure_arguments_based_on_object_constraint(
209						this_passed,
210						environment,
211						&checking_data.types,
212					) {
213						crate::utilities::notify!("Here :)");
214						Some(arguments.clone())
215					} else {
216						let prototype = environment
217							.get_chain_of_info()
218							.find_map(|facts| facts.prototypes.get(&this_passed))
219							.copied();
220
221						if prototype.is_some_and(|prototype| {
222							checking_data.types.lookup_generic_map.contains_key(&prototype)
223						}) {
224							crate::utilities::notify!("Registering lookup for calling");
225
226							Some(GenericArguments::LookUp { on: this_passed })
227						} else {
228							None
229						}
230					}
231				} else {
232					None
233				}
234			} else {
235				None
236			};
237
238			let (arguments, type_argument_restrictions) =
239				synthesise_argument_expressions_wrt_parameters(
240					&callable,
241					arguments,
242					(call_site_type_arguments, parent_arguments.as_ref()),
243					environment,
244					checking_data,
245				);
246
247			let mut check_things = CheckThings { debug_types: checking_data.options.debug_types };
248			let mut diagnostics = CallingDiagnostics::default();
249			let result = call_logical(
250				callable,
251				(arguments, type_argument_restrictions, parent_arguments),
252				input,
253				environment,
254				&mut checking_data.types,
255				(&mut check_things, &mut diagnostics),
256			);
257
258			diagnostics
259				.append_to(CallingContext::Regular, &mut checking_data.diagnostics_container);
260
261			match result {
262				Ok(CallingOutput {
263					result,
264					called: _,
265					special,
266					result_was_const_computation: _,
267				}) => {
268					let returned_type = application_result_to_return_type(
269						result,
270						environment,
271						&mut checking_data.types,
272					);
273
274					(returned_type, special)
275				}
276				Err(error) => (error.returned_type, None),
277			}
278		}
279		Ok(LogicalOrValid::NeedsCalculation(l)) => match l {
280			NeedsCalculation::Infer { on } => {
281				if on == TypeId::ERROR_TYPE {
282					(TypeId::UNIMPLEMENTED_ERROR_TYPE, None)
283				} else {
284					crate::utilities::notify!("TODO function calling inference on {:?}", on);
285					(TypeId::UNIMPLEMENTED_ERROR_TYPE, None)
286				}
287			}
288			NeedsCalculation::Proxy(..) => {
289				crate::utilities::notify!("TODO calling proxy");
290				(TypeId::UNIMPLEMENTED_ERROR_TYPE, None)
291			}
292		},
293		Err(Invalid(ty)) => {
294			checking_data.diagnostics_container.add_error(TypeCheckError::FunctionCallingError(
295				FunctionCallingError::NotCallable {
296					calling: TypeStringRepresentation::from_type_id(
297						ty,
298						environment,
299						&checking_data.types,
300						checking_data.options.debug_types,
301					),
302					call_site,
303				},
304			));
305			(TypeId::ERROR_TYPE, None)
306		}
307	}
308}
309
310/// TODO also return partial result
311pub fn application_result_to_return_type(
312	result: Option<ApplicationResult>,
313	environment: &mut Environment,
314	types: &mut TypeStore,
315) -> TypeId {
316	if let Some(result) = result {
317		match result {
318			// TODO
319			ApplicationResult::Return { returned, position: _ } => returned,
320			ApplicationResult::Throw { thrown, position } => {
321				environment.throw_value(thrown, position, types);
322				TypeId::NEVER_TYPE
323			}
324			ApplicationResult::Yield {} => todo!("Create generator object"),
325			ApplicationResult::Or { on, truthy_result, otherwise_result } => {
326				let truthy_result =
327					application_result_to_return_type(Some(*truthy_result), environment, types);
328				let otherwise_result =
329					application_result_to_return_type(Some(*otherwise_result), environment, types);
330				types.new_conditional_type(on, truthy_result, otherwise_result)
331			}
332			ApplicationResult::Continue { .. } | ApplicationResult::Break { .. } => {
333				unreachable!("returning conditional or break (carry failed)")
334			}
335		}
336	} else {
337		TypeId::UNDEFINED_TYPE
338	}
339}
340
341#[derive(Debug, Copy, Clone, binary_serialize_derive::BinarySerializable)]
342pub enum Callable {
343	/// TODO `ThisValue` not always needed
344	Fixed(FunctionId, ThisValue),
345	Type(TypeId),
346}
347
348impl Callable {
349	pub(crate) fn from_type(ty: TypeId, types: &TypeStore) -> Self {
350		if let Type::SpecialObject(SpecialObject::Function(func_id, _)) = types.get_type_by_id(ty) {
351			Callable::Fixed(*func_id, ThisValue::UseParent)
352		} else {
353			crate::utilities::notify!("Here!!");
354			Callable::Type(ty)
355		}
356	}
357
358	pub(crate) fn new_from_function(function: FunctionType, types: &mut TypeStore) -> Self {
359		let id = function.id;
360		types.functions.insert(id, function);
361		Callable::Fixed(id, ThisValue::UseParent)
362	}
363
364	pub(crate) fn into_type(self, types: &mut TypeStore) -> TypeId {
365		match self {
366			Callable::Fixed(id, this_value) => {
367				types.register_type(Type::SpecialObject(SpecialObject::Function(id, this_value)))
368			}
369			Callable::Type(ty) => ty,
370		}
371	}
372
373	pub(crate) fn get_return_type(self, types: &TypeStore) -> TypeId {
374		match self {
375			Callable::Fixed(id, _this_value) => types.get_function_from_id(id).return_type,
376			Callable::Type(ty) => {
377				if let Type::SpecialObject(SpecialObject::Function(id, _)) =
378					types.get_type_by_id(ty)
379				{
380					types.get_function_from_id(*id).return_type
381				} else {
382					crate::utilities::notify!("Cannot get return type");
383					TypeId::UNIMPLEMENTED_ERROR_TYPE
384				}
385			}
386		}
387	}
388
389	pub(crate) fn get_first_argument(self, types: &TypeStore) -> TypeId {
390		match self {
391			Callable::Fixed(id, _this_value) => types
392				.get_function_from_id(id)
393				.parameters
394				.get_parameter_type_at_index(0)
395				.map_or(TypeId::ERROR_TYPE, |(ty, _)| ty),
396			Callable::Type(ty) => {
397				if let Type::SpecialObject(SpecialObject::Function(id, _)) =
398					types.get_type_by_id(ty)
399				{
400					types
401						.get_function_from_id(*id)
402						.parameters
403						.get_parameter_type_at_index(0)
404						.map_or(TypeId::ERROR_TYPE, |(ty, _)| ty)
405				} else {
406					crate::utilities::notify!("Cannot get first arugment");
407					TypeId::ERROR_TYPE
408				}
409			}
410		}
411	}
412
413	pub(crate) fn call<B: CallCheckingBehavior>(
414		self,
415		arguments: Vec<SynthesisedArgument>,
416		input: CallingInput,
417		top_environment: &mut Environment,
418		(behavior, diagnostics): (&mut B, &mut CallingDiagnostics),
419		types: &mut TypeStore,
420	) -> Result<CallingOutput, BadCallOutput> {
421		match self {
422			Callable::Fixed(on, this_value) => {
423				let function_like = FunctionLike { function: on, from: None, this_value };
424
425				call_logical(
426					Logical::Pure(function_like),
427					(arguments, None, None),
428					input,
429					top_environment,
430					types,
431					(behavior, diagnostics),
432				)
433			}
434			Callable::Type(on) => {
435				let callable = get_logical_callable_from_type(on, None, None, types);
436				if let Ok(LogicalOrValid::Logical(callable)) = callable {
437					call_logical(
438						callable,
439						(arguments, None, None),
440						input,
441						top_environment,
442						types,
443						(behavior, diagnostics),
444					)
445				} else {
446					crate::utilities::notify!("callable={:?}", callable);
447
448					diagnostics.errors.push(FunctionCallingError::NotCallable {
449						calling: crate::diagnostics::TypeStringRepresentation::from_type_id(
450							on,
451							top_environment,
452							types,
453							behavior.debug_types(),
454						),
455						call_site: input.call_site,
456					});
457
458					Err(BadCallOutput { returned_type: TypeId::ERROR_TYPE })
459				}
460			}
461		}
462	}
463}
464
465/// This could possibly be done in one step
466fn get_logical_callable_from_type(
467	ty: TypeId,
468	on: Option<ThisValue>,
469	from: Option<TypeId>,
470	types: &TypeStore,
471) -> PossibleLogical<FunctionLike> {
472	// if ty == TypeId::ERROR_TYPE {
473	// 	return Err(MissingOrToCalculate::Error);
474	// }
475	if ty == TypeId::ANY_TYPE {
476		crate::utilities::notify!("Calling ANY");
477		// TODO temp
478		return Ok(NeedsCalculation::Infer { on: from.unwrap_or(TypeId::ERROR_TYPE) }.into());
479	}
480
481	let le_ty = types.get_type_by_id(ty);
482
483	match le_ty {
484		Type::Class { .. } | Type::Interface { .. } | Type::Constant(_) | Type::Object(_) => {
485			Err(Invalid(ty))
486		}
487		Type::And(_, _) => todo!(),
488		Type::Or(left, right) => {
489			let left = get_logical_callable_from_type(*left, on, from, types)?;
490			let right = get_logical_callable_from_type(*right, on, from, types)?;
491			Ok(Logical::Or {
492				condition: TypeId::OPEN_BOOLEAN_TYPE,
493				left: Box::new(left),
494				right: Box::new(right),
495			}
496			.into())
497		}
498		Type::Narrowed { narrowed_to, from } => {
499			get_logical_callable_from_type(*narrowed_to, on, Some(*from), types)
500		}
501		Type::AliasTo { to, name: _, parameters } => {
502			if parameters.is_some() {
503				todo!()
504			}
505			get_logical_callable_from_type(*to, on, from, types)
506		}
507		Type::FunctionReference(f) => {
508			let function = FunctionLike {
509				// TODO
510				function: *f,
511				from,
512				this_value: on.unwrap_or(ThisValue::UseParent),
513			};
514			Ok(Logical::Pure(function).into())
515		}
516		Type::SpecialObject(SpecialObject::Function(f, t)) => {
517			let this_value = on.unwrap_or(*t);
518			let from = Some(from.unwrap_or(ty));
519			Ok(Logical::Pure(FunctionLike { from, function: *f, this_value }).into())
520		}
521		Type::SpecialObject(so) => match so {
522			crate::features::objects::SpecialObject::Proxy { .. } => todo!(),
523			_ => Err(Invalid(ty)),
524		},
525		Type::PartiallyAppliedGenerics(generic) => {
526			let result = get_logical_callable_from_type(generic.on, on, from, types)?;
527			if let LogicalOrValid::Logical(result) = result {
528				Ok(Logical::Implies { on: Box::new(result), antecedent: generic.arguments.clone() }
529					.into())
530			} else {
531				crate::utilities::notify!("Should not be here");
532				Ok(result)
533			}
534		}
535		Type::Constructor(Constructor::Property { on, under: _, result, mode }) => {
536			crate::utilities::notify!("Passing {:?}", on);
537
538			let this_value = if let AccessMode::DoNotBindThis = mode {
539				ThisValue::UseParent
540			} else {
541				ThisValue::Passed(*on)
542			};
543			let result =
544				get_logical_callable_from_type(*result, Some(this_value), Some(ty), types)?;
545
546			let and_then = get_constraint(*on, types).and_then(|c| {
547				if let Type::PartiallyAppliedGenerics(generic) = types.get_type_by_id(c) {
548					Some(generic.arguments.clone())
549				} else {
550					None
551				}
552			});
553
554			if let Some(antecedent) = and_then {
555				if let LogicalOrValid::Logical(result) = result {
556					Ok(Logical::Implies { on: Box::new(result), antecedent }.into())
557				} else {
558					crate::utilities::notify!("TODO not antecedent {:?}", antecedent);
559					Ok(result)
560				}
561			} else {
562				Ok(result)
563			}
564		}
565		Type::RootPolyType(_) | Type::Constructor(_) => {
566			let constraint = get_constraint(ty, types).unwrap();
567			crate::utilities::notify!(
568				"Getting callable constructor / root poly type! {:?}",
569				constraint
570			);
571			get_logical_callable_from_type(constraint, on, Some(ty), types)
572		}
573	}
574}
575
576#[allow(clippy::too_many_arguments)]
577fn call_logical<B: CallCheckingBehavior>(
578	logical: Logical<FunctionLike>,
579	(arguments, explicit_type_arguments, structure_generics): (
580		Vec<SynthesisedArgument>,
581		Option<CallSiteTypeArguments>,
582		Option<GenericArguments>,
583	),
584	input: CallingInput,
585	top_environment: &mut Environment,
586	types: &mut TypeStore,
587	(behavior, diagnostics): (&mut B, &mut CallingDiagnostics),
588) -> Result<CallingOutput, BadCallOutput> {
589	match logical {
590		Logical::Pure(function) => {
591			if let Some(function_type) = types.functions.get(&function.function) {
592				// TODO clone
593				let function_type = function_type.clone();
594
595				if let FunctionEffect::Constant { identifier: ref const_fn_ident, .. } =
596					&function_type.effect
597				{
598					let has_dependent_argument =
599						arguments.iter().any(|arg| types.get_type_by_id(arg.value).is_dependent());
600
601					// || matches!(this_value, ThisValue::Passed(ty) if types.get_type_by_id(ty).is_dependent());
602
603					crate::utilities::notify!(
604						"has_dependent_argument={:?}, const_fn_ident={:?}",
605						has_dependent_argument,
606						const_fn_ident
607					);
608
609					// TODO just for debugging. These have their constant things called every time AND queue an event
610					let is_independent_function = const_fn_ident.ends_with("independent");
611
612					let call_anyway = const_fn_ident.starts_with("debug")
613						|| const_fn_ident.starts_with("print")
614						|| is_independent_function
615						|| matches!(
616							const_fn_ident.as_str(),
617							"satisfies"
618								| "is_dependent" | "bind"
619								| "proxy:constructor" | "setPrototypeOf"
620								| "getPrototypeOf"
621						);
622
623					// {
624					// 	let arguments = arguments
625					// 		.iter()
626					// 		.map(|a| types.get_type_by_id(a.value))
627					// 		.collect::<Vec<_>>();
628
629					// 	crate::utilities::notify!(
630					// 		"E::CHECK_PARAMETERS={:?}, args={:?}, ident={:?}",
631					// 		E::CHECK_PARAMETERS,
632					// 		arguments,
633					// 		const_fn_ident
634					// 	);
635					// }
636
637					// For debugging only
638					if is_independent_function && B::CHECK_PARAMETERS {
639						let function_id = function.function;
640						let value = Event::CallsType {
641							on: Callable::Fixed(function.function, function.this_value),
642							with: arguments.clone().into_boxed_slice(),
643							timing: crate::events::CallingTiming::Synchronous,
644							called_with_new: input.called_with_new,
645							reflects_dependency: None,
646							call_site: input.call_site,
647							possibly_thrown: None,
648						};
649						behavior.get_latest_info(top_environment).events.push(value);
650						return Ok(CallingOutput {
651							called: Some(function_id),
652							result: None,
653							special: Some(SpecialExpressions::Marker),
654							result_was_const_computation: true,
655						});
656					}
657
658					// Most of the time don't call using constant function if an argument is dependent.
659					// But sometimes do for the cases above
660					if call_anyway || !has_dependent_argument {
661						// TODO event
662						let result = call_constant_function(
663							const_fn_ident,
664							// TODO
665							function.this_value,
666							explicit_type_arguments.as_ref(),
667							&arguments,
668							types,
669							top_environment,
670							input.call_site,
671							diagnostics,
672						);
673
674						match result {
675							Ok(ConstantOutput::Value(value)) => {
676								// Very special
677								let special = if const_fn_ident == "compile_type_to_object" {
678									Some(SpecialExpressions::CompileOut)
679								} else {
680									None
681								};
682								return Ok(CallingOutput {
683									result: Some(ApplicationResult::Return {
684										returned: value,
685										position: input.call_site,
686									}),
687									called: None,
688									result_was_const_computation: !is_independent_function,
689									special,
690								});
691							}
692							Ok(ConstantOutput::Diagnostic(diagnostic)) => {
693								diagnostics.info.push(InfoDiagnostic(diagnostic, input.call_site));
694								return Ok(CallingOutput {
695									result: None,
696									called: None,
697									result_was_const_computation: !is_independent_function,
698									// WIP!!
699									special: Some(SpecialExpressions::Marker),
700								});
701							}
702							Err(ConstantFunctionError::NoLogicForIdentifier(name)) => {
703								let err = FunctionCallingError::NoLogicForIdentifier(
704									name,
705									input.call_site,
706								);
707								diagnostics.errors.push(err);
708								return Err(BadCallOutput {
709									returned_type: types.new_error_type(function_type.return_type),
710								});
711							}
712							Err(ConstantFunctionError::FunctionCallingError(err)) => {
713								diagnostics.errors.push(err);
714								return Err(BadCallOutput {
715									returned_type: types.new_error_type(function_type.return_type),
716								});
717							}
718							Err(ConstantFunctionError::CannotComputeConstant) => {
719								crate::utilities::notify!(
720									"Constant function calling failed, non constant params"
721								);
722							}
723						}
724					} else if !has_dependent_argument {
725						// TODO with cloned!!
726						let call = function_type.call(
727							(
728								function.this_value,
729								&arguments,
730								explicit_type_arguments,
731								structure_generics,
732							),
733							input,
734							top_environment,
735							(behavior, diagnostics),
736							types,
737						);
738
739						return match call {
740							Ok(call) => Ok(CallingOutput {
741								result: call.result,
742								called: None,
743								special: None,
744								result_was_const_computation: false,
745								// TODO
746							}),
747							Err(err) => {
748								crate::utilities::notify!(
749									"Calling function with dependent argument failed"
750								);
751								Err(err)
752							}
753						};
754					}
755				}
756
757				// Temp fix for structure generics, doesn't work in access
758				let structure_generics = if let (false, Some(on)) =
759					(structure_generics.is_some(), function.this_value.get_passed())
760				{
761					if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
762						on: _,
763						arguments,
764					}) = types.get_type_by_id(get_constraint(on, types).unwrap_or(on))
765					{
766						Some(arguments.clone())
767					} else {
768						None
769					}
770				} else {
771					structure_generics
772				};
773
774				let mut result = function_type.call(
775					(function.this_value, &arguments, explicit_type_arguments, structure_generics),
776					input,
777					top_environment,
778					(behavior, diagnostics),
779					types,
780				)?;
781
782				// crate::utilities::notify!("result {:?} {:?}", result.called, result.returned_type);
783
784				// is poly
785				let is_poly_or_failed_const_call = matches!(
786					function_type.effect,
787					FunctionEffect::Unknown | FunctionEffect::InputOutput { .. }
788				) || (matches!(function_type.effect, FunctionEffect::Constant { .. } if !result.result_was_const_computation));
789
790				if is_poly_or_failed_const_call && function.from.is_some() {
791					// if function_type.effect
792					// This should be okay, constant or IO functions don't mutate their arguments...?
793					// if !matches!(
794					// 	&function_type.effect,
795					// 	FunctionEffect::Constant { .. } | FunctionEffect::InputOutput { .. }
796					// ) {
797					// 	for a
798					// 	mark_possible_mutation(&arguments, types, top_environment);
799					// }
800
801					// match function_type.effect {
802					// 	FunctionEffect::SideEffects { .. } => unreachable!(),
803					// 	FunctionEffect::Constant { may_throw, .. }
804					// 	| FunctionEffect::InputOutput { may_throw, .. } => {
805					// 		if let Some(thrown_type) = may_throw {
806					// 			// TODO not top
807					// 			top_environment
808					// 				.context_type
809					// 				.state
810					// 				.append_termination(ApplicationResult::may_throw(thrown_type));
811					// 		}
812					// 	}
813					// 	FunctionEffect::Unknown => {
814					// 		top_environment
815					// 			.context_type
816					// 			.state
817					// 			.append_termination(ApplicationResult::may_throw(TypeId::ANY_TYPE));
818					// 	}
819					// }
820
821					let with = arguments.clone().into_boxed_slice();
822					let result_as_type = application_result_to_return_type(
823						result.result.take(),
824						top_environment,
825						types,
826					);
827					let reflects_dependency = if types.get_type_by_id(result_as_type).is_constant()
828					{
829						None
830					} else {
831						let id = types.register_type(Type::Constructor(Constructor::Image {
832							// TODO
833							on: function.from.expect("function `on`"),
834							// TODO...
835							with: with.clone(),
836							result: result_as_type,
837						}));
838
839						Some(id)
840					};
841
842					result.result = Some(ApplicationResult::Return {
843						returned: reflects_dependency.unwrap_or(result_as_type),
844						position: input.call_site,
845					});
846
847					let possibly_thrown = if let FunctionEffect::InputOutput { may_throw, .. }
848					| FunctionEffect::Constant { may_throw, .. } = &function_type.effect
849					{
850						// crate::utilities::notify!("Constant / input output: may_throw");
851						*may_throw
852					} else {
853						None
854					};
855
856					let on = match function.from {
857						Some(ty) => Callable::Type(ty),
858						None => Callable::Fixed(function.function, function.this_value),
859					};
860					behavior.get_latest_info(top_environment).events.push(Event::CallsType {
861						on,
862						with,
863						timing: crate::events::CallingTiming::Synchronous,
864						called_with_new: input.called_with_new,
865						reflects_dependency,
866						call_site: input.call_site,
867						possibly_thrown,
868					});
869				} else {
870					// This fixes tree shaking of functions that are called within callbacks
871					// TODO this could be done under an option
872					let value = Event::Miscellaneous(
873						crate::events::MiscellaneousEvents::MarkFunctionAsCalled(function.function),
874					);
875					behavior.get_latest_info(top_environment).events.push(value);
876				}
877
878				Ok(result)
879			} else {
880				panic!("no function")
881			}
882		}
883		Logical::Or { .. } => {
884			crate::utilities::notify!("Calling OR");
885			Err(BadCallOutput { returned_type: TypeId::UNIMPLEMENTED_ERROR_TYPE })
886			// todo!("{:?}", (condition, left, right));
887			// if let (Ok(_left), Ok(_right)) = (*left, *right) {
888			// let (truthy_result, otherwise_result) = behavior.evaluate_conditionally(
889			// 	top_environment,
890			// 	types,
891			// 	based_on,
892			// 	(left, right),
893			// 	(explicit_type_arguments, structure_generics, arguments),
894			// 	|top_environment, types, target, func, data| {
895			// 		let (explicit_type_arguments, structure_generics, arguments) = data;
896			// 		let result = call_logical(
897			// 			func,
898			// 			called_with_new,
899			// 			call_site,
900			// 			explicit_type_arguments.clone(),
901			// 			structure_generics.clone(),
902			// 			arguments.clone(),
903			// 			top_environment,
904			// 			types,
905			// 			target,
906			// 		);
907
908			// 		result.map(|result| {
909			// 			application_result_to_return_type(
910			// 				result.result,
911			// 				top_environment,
912			// 				types,
913			// 			)
914			// 		})
915			// 	},
916			// );
917			// let truthy_result = truthy_result?;
918			// let otherwise_result = otherwise_result?;
919			// // truthy_result.warnings.append(&mut otherwise_result.warnings);
920			// let _result =
921			// 	types.new_conditional_type(based_on, truthy_result, otherwise_result);
922
923			// TODO combine information (merge)
924			// Ok(CallingOutput { called: None, result: Soe, warnings: (), special: (), result_was_const_computation: () })
925			// } else {
926			// 	// TODO inference and errors
927			// 	let err = FunctionCallingError::NotCallable {
928			// 		calling: TypeStringRepresentation::from_type_id(
929			// 			based_on,
930			// 			top_environment,
931			// 			types,
932			// 			false,
933			// 		),
934			// 		call_site: input.call_site,
935			// 	};
936			// 	Err(BadCallOutput { returned_type: TypeId::ERROR_TYPE, errors: vec![err] })
937			// }
938		}
939		Logical::Implies { on, antecedent } => {
940			// crate::utilities::notify!("antecedent={:?}", antecedent);
941			call_logical(
942				*on,
943				(arguments, explicit_type_arguments, Some(antecedent)),
944				input,
945				top_environment,
946				types,
947				(behavior, diagnostics),
948			)
949		}
950		Logical::BasedOnKey { .. } => {
951			crate::utilities::notify!("Calling based on key?");
952			Err(BadCallOutput { returned_type: TypeId::UNIMPLEMENTED_ERROR_TYPE })
953		}
954	}
955}
956
957fn mark_possible_mutation(
958	argument: &SynthesisedArgument,
959	parameter_type: TypeId,
960	types: &mut TypeStore,
961	top_environment: &mut Environment,
962) {
963	match types.get_type_by_id(argument.value) {
964		Type::Interface { .. }
965		| Type::Class { .. }
966		| Type::AliasTo { .. }
967		| Type::And(_, _)
968		| Type::Object(ObjectNature::AnonymousTypeAnnotation(_))
969		| Type::FunctionReference(_)
970		| Type::PartiallyAppliedGenerics(_)
971		| Type::Or(_, _) => {
972			crate::utilities::notify!("Unreachable");
973		}
974		Type::Constant(_) => {}
975		Type::Narrowed { .. } | Type::RootPolyType(_) | Type::Constructor(_) => {
976			// All dependent anyway
977			crate::utilities::notify!("TODO if any properties set etc");
978		}
979		Type::SpecialObject(SpecialObject::Function(_, _)) => {
980			crate::utilities::notify!("TODO record that function could be called");
981		}
982		Type::Object(ObjectNature::RealDeal) => {
983			top_environment.possibly_mutated_objects.insert(argument.value, parameter_type);
984			crate::utilities::notify!("TODO record methods could be called here as well");
985		}
986		Type::SpecialObject(_) => {
987			crate::utilities::notify!("TODO record stuff if mutable");
988		}
989	}
990}
991
992/// Errors from trying to call a function
993pub enum FunctionCallingError {
994	InvalidArgumentType {
995		parameter_type: TypeStringRepresentation,
996		argument_type: TypeStringRepresentation,
997		argument_position: SpanWithSource,
998		parameter_position: SpanWithSource,
999		restriction: Option<(SpanWithSource, TypeStringRepresentation)>,
1000	},
1001	MissingArgument {
1002		parameter_position: SpanWithSource,
1003		call_site: SpanWithSource,
1004	},
1005	ExcessArguments {
1006		count: usize,
1007		position: SpanWithSource,
1008	},
1009	ExcessTypeArguments {
1010		expected_count: usize,
1011		count: usize,
1012		position: SpanWithSource,
1013	},
1014	NotCallable {
1015		calling: TypeStringRepresentation,
1016		call_site: SpanWithSource,
1017	},
1018	ReferenceRestrictionDoesNotMatch {
1019		reference: RootReference,
1020		requirement: TypeStringRepresentation,
1021		found: TypeStringRepresentation,
1022	},
1023	CyclicRecursion(FunctionId, SpanWithSource),
1024	NoLogicForIdentifier(String, SpanWithSource),
1025	NeedsToBeCalledWithNewKeyword(SpanWithSource),
1026	VariableUsedInTDZ {
1027		error: VariableUsedInTDZ,
1028		/// Should be set by parent
1029		call_site: SpanWithSource,
1030	},
1031	InvalidRegExp(crate::diagnostics::InvalidRegExp),
1032	/// For #18
1033	SetPropertyConstraint {
1034		property_type: TypeStringRepresentation,
1035		value_type: TypeStringRepresentation,
1036		assignment_position: SpanWithSource,
1037		/// Should be set by parent
1038		call_site: SpanWithSource,
1039	},
1040	/// For #18
1041	DeleteConstraint {
1042		constraint: TypeStringRepresentation,
1043		delete_position: SpanWithSource,
1044		/// Should be set by parent
1045		call_site: SpanWithSource,
1046	},
1047	NotConfigurable {
1048		property: crate::diagnostics::PropertyKeyRepresentation,
1049		/// Should be set by parent
1050		call_site: SpanWithSource,
1051	},
1052	MismatchedThis {
1053		expected: TypeStringRepresentation,
1054		found: TypeStringRepresentation,
1055		call_site: SpanWithSource,
1056	},
1057	CannotCatch {
1058		catch: TypeStringRepresentation,
1059		thrown: TypeStringRepresentation,
1060		thrown_position: SpanWithSource,
1061	},
1062}
1063
1064#[derive(Debug, Default, Clone, Copy, binary_serialize_derive::BinarySerializable)]
1065pub enum CalledWithNew {
1066	/// With `new X(...)`;
1067	New {
1068		// [See new.target](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target), which contains a reference to what called new. This does not == this_type
1069		on: TypeId,
1070	},
1071	GetterOrSetter {
1072		this_type: TypeId,
1073	},
1074	/// With `super(...)`
1075	Super {
1076		this_type: TypeId,
1077	},
1078	#[default]
1079	None,
1080}
1081
1082impl FunctionType {
1083	/// Calls the function and returns warnings and errors
1084	pub(crate) fn call<B: CallCheckingBehavior>(
1085		&self,
1086		(this_value, arguments, call_site_type_arguments, parent_arguments): (
1087			ThisValue,
1088			&[SynthesisedArgument],
1089			Option<CallSiteTypeArguments>,
1090			Option<GenericArguments>,
1091		),
1092		input: CallingInput,
1093		environment: &mut Environment,
1094		(behavior, diagnostics): (&mut B, &mut CallingDiagnostics),
1095		types: &mut crate::TypeStore,
1096	) -> Result<CallingOutput, BadCallOutput> {
1097		// TODO check that parameters vary
1098		if behavior.in_recursive_cycle(self.id) {
1099			crate::utilities::notify!("Encountered recursion");
1100			crate::utilities::notify!("TODO should be BadCallOutput");
1101			let returned = types.new_error_type(self.return_type);
1102			return Ok(CallingOutput {
1103				called: Some(self.id),
1104				result: Some(ApplicationResult::Return { returned, position: input.call_site }),
1105				// TODO ?
1106				special: None,
1107				result_was_const_computation: false,
1108			});
1109			// let reason = FunctionCallingError::Recursed(self.id, call_site);
1110			// return Err(vec![reason])
1111		}
1112
1113		if environment.is_always_run() {
1114			types.called_functions.insert(self.id);
1115		}
1116
1117		let errors = CallingDiagnostics::default();
1118
1119		let mut type_arguments = SubstitutionArguments {
1120			parent: None,
1121			arguments: crate::Map::default(),
1122			closures: if let Some(GenericArguments::Closure(ref cs)) = parent_arguments {
1123				cs.clone()
1124			} else {
1125				Default::default()
1126			},
1127		};
1128
1129		self.set_this_for_behavior(
1130			input.called_with_new,
1131			this_value,
1132			&mut type_arguments.arguments,
1133			environment,
1134			types,
1135			input.call_site,
1136			(behavior, diagnostics),
1137		);
1138
1139		self.assign_arguments_to_parameters::<B>(
1140			arguments,
1141			&mut type_arguments,
1142			(call_site_type_arguments.clone(), parent_arguments.as_ref()),
1143			environment,
1144			types,
1145			input.call_site,
1146			matches!(self.effect, FunctionEffect::SideEffects { .. }),
1147			(behavior, diagnostics),
1148		);
1149
1150		if !diagnostics.errors.is_empty() {
1151			return Err(BadCallOutput {
1152				// TODO what about `new`
1153				returned_type: types.new_error_type(self.return_type),
1154			});
1155		}
1156
1157		let result = if let FunctionEffect::SideEffects {
1158			events,
1159			closed_over_variables,
1160			free_variables: _,
1161		} = &self.effect
1162		{
1163			behavior.new_function_context(self.id, |target| {
1164				// crate::utilities::notify!("events: {:?}", events);
1165				{
1166					let substitution: &mut SubstitutionArguments = &mut type_arguments;
1167					if !closed_over_variables.0.is_empty() {
1168						let closure_id = types.new_closure_id();
1169						substitution.closures.push(closure_id);
1170
1171						crate::utilities::notify!("Setting closure variables");
1172
1173						// Set closed over values
1174						// TODO `this`
1175						for (variable, value) in closed_over_variables.0.iter() {
1176							let value = substitute(*value, substitution, environment, types);
1177							environment
1178								.info
1179								.closure_current_values
1180								.insert((closure_id, RootReference::Variable(*variable)), value);
1181
1182							crate::utilities::notify!(
1183								"in {:?} set {:?} to {:?}",
1184								closure_id,
1185								variable,
1186								value
1187							);
1188						}
1189					}
1190
1191					let current_errors = diagnostics.errors.len();
1192					let current_warnings = diagnostics.warnings.len();
1193
1194					// Apply events here
1195					let result = apply_events(
1196						events,
1197						&ApplicationInput {
1198							this_value,
1199							call_site: input.call_site,
1200							max_inline: input.max_inline,
1201						},
1202						substitution,
1203						environment,
1204						target,
1205						types,
1206						diagnostics,
1207					);
1208
1209					// Adjust call sites. (because they aren't currently passed down)
1210					for d in &mut diagnostics.errors[current_errors..] {
1211						if let FunctionCallingError::VariableUsedInTDZ {
1212							call_site: ref mut c,
1213							..
1214						}
1215						| FunctionCallingError::SetPropertyConstraint {
1216							call_site: ref mut c,
1217							..
1218						} = d
1219						{
1220							*c = input.call_site;
1221						}
1222					}
1223					for d in &mut diagnostics.warnings[current_warnings..] {
1224						if let TypeCheckWarning::ConditionalExceptionInvoked {
1225							call_site: ref mut c,
1226							..
1227						} = d
1228						{
1229							*c = input.call_site;
1230						}
1231					}
1232
1233					result
1234				}
1235			})
1236		} else {
1237			if let Some(_parent_arguments) = parent_arguments {
1238				crate::utilities::notify!("TODO");
1239			}
1240			if let Some(ref call_site_type_arguments) = call_site_type_arguments {
1241				for (k, (v, _)) in call_site_type_arguments.iter() {
1242					type_arguments.arguments.insert(*k, *v);
1243				}
1244			}
1245
1246			// crate::utilities::notify!(
1247			// 	"Substituting return type (no return) type_arguments={:?}",
1248			// 	type_arguments
1249			// 		.arguments
1250			// 		.iter()
1251			// 		.map(|(k, v)| (types.get_type_by_id(*k), types.get_type_by_id(*v)))
1252			// 		.collect::<Vec<_>>()
1253			// );
1254
1255			let returned = substitute(self.return_type, &type_arguments, environment, types);
1256			Some(ApplicationResult::Return { returned, position: input.call_site })
1257		};
1258
1259		if !diagnostics.errors.is_empty() {
1260			crate::utilities::notify!("Got {} application errors", errors.errors.len());
1261			return Err(BadCallOutput {
1262				// TODO what about `new`
1263				returned_type: types.new_error_type(self.return_type),
1264			});
1265		}
1266
1267		// TODO what does super return?
1268		let result = if let CalledWithNew::New { .. } = input.called_with_new {
1269			// TODO ridiculous early return primitive rule
1270			let returned = match self.behavior {
1271				FunctionBehavior::ArrowFunction { .. } | FunctionBehavior::Method { .. } => {
1272					TypeId::ERROR_TYPE
1273				}
1274				FunctionBehavior::Constructor { prototype: _, this_object_type, name: _ } => {
1275					*type_arguments.arguments.get(&this_object_type).expect("no this argument?")
1276				}
1277				FunctionBehavior::Function { is_async: _, is_generator: _, this_id, .. } => {
1278					if let Type::Constructor(Constructor::ConditionalResult {
1279						truthy_result, ..
1280					}) = types.get_type_by_id(this_id)
1281					{
1282						*type_arguments.arguments.get(truthy_result).expect("no this argument?")
1283					} else {
1284						unreachable!()
1285					}
1286				}
1287			};
1288			// TODO if return type is primitive (or replace primitives with new_instance_type)
1289			Some(ApplicationResult::Return { returned, position: input.call_site })
1290		} else {
1291			result
1292		};
1293
1294		Ok(CallingOutput {
1295			result,
1296			called: Some(self.id),
1297			special: None,
1298			result_was_const_computation: false,
1299		})
1300	}
1301
1302	/// TODO set not using `type_arguments`
1303	#[allow(clippy::too_many_arguments)]
1304	fn set_this_for_behavior<B: CallCheckingBehavior>(
1305		&self,
1306		called_with_new: CalledWithNew,
1307		this_value: ThisValue,
1308		type_arguments: &mut crate::Map<TypeId, TypeId>,
1309		environment: &mut Environment,
1310		types: &mut TypeStore,
1311		call_site: SpanWithSource,
1312		(behavior, diagnostics): (&mut B, &mut CallingDiagnostics),
1313	) {
1314		match self.behavior {
1315			FunctionBehavior::ArrowFunction { .. } => {}
1316			FunctionBehavior::Method { free_this_id, .. } => {
1317				// TODO
1318				let value_of_this =
1319					if let CalledWithNew::GetterOrSetter { this_type } = called_with_new {
1320						this_type
1321					} else if let Some(value) = this_value.get_passed() {
1322						value
1323					} else {
1324						crate::utilities::notify!(
1325							"method has no 'this' passed :?. Passing `undefined` here"
1326						);
1327						TypeId::UNDEFINED_TYPE
1328					};
1329
1330				let base_type = get_constraint(free_this_id, types).unwrap_or(free_this_id);
1331
1332				let mut state = State {
1333					already_checked: Default::default(),
1334					contributions: None,
1335					mode: SubTypingMode::default(),
1336					object_constraints: None,
1337					others: Default::default(),
1338				};
1339
1340				let type_is_subtype =
1341					type_is_subtype(base_type, value_of_this, &mut state, environment, types);
1342
1343				if let SubTypeResult::IsNotSubType(_reason) = type_is_subtype {
1344					diagnostics.errors.push(FunctionCallingError::MismatchedThis {
1345						expected: TypeStringRepresentation::from_type_id(
1346							free_this_id,
1347							environment,
1348							types,
1349							behavior.debug_types(),
1350						),
1351						found: TypeStringRepresentation::from_type_id(
1352							value_of_this,
1353							environment,
1354							types,
1355							behavior.debug_types(),
1356						),
1357						call_site,
1358					});
1359				}
1360
1361				crate::utilities::notify!(
1362					"free this id {:?} & value of this {:?}",
1363					free_this_id,
1364					value_of_this
1365				);
1366
1367				type_arguments.insert(free_this_id, value_of_this);
1368			}
1369			FunctionBehavior::Function {
1370				is_async: _,
1371				is_generator: _,
1372				this_id,
1373				prototype,
1374				name: _,
1375			} => {
1376				match called_with_new {
1377					CalledWithNew::New { on: _ } => {
1378						// This condition is by creation
1379						// TODO THIS IS FRAGILE AND UNCLEAR
1380						let (this_id, constraint) =
1381							if let Type::Constructor(Constructor::ConditionalResult {
1382								truthy_result: this_id,
1383								otherwise_result: constraint,
1384								..
1385							}) = types.get_type_by_id(this_id)
1386							{
1387								(*this_id, *constraint)
1388							} else {
1389								unreachable!()
1390							};
1391
1392						// {
1393						// 	let debug = true;
1394						// 	crate::utilities::notify!(
1395						// 		"Checking against this constraint {}",
1396						// 		crate::types::printing::print_type(
1397						// 			this_id,
1398						// 			types,
1399						// 			environment,
1400						// 			debug
1401						// 		)
1402						// 	);
1403						// 	crate::utilities::notify!(
1404						// 		"Other {}",
1405						// 		crate::types::printing::print_type(
1406						// 			constraint,
1407						// 			types,
1408						// 			environment,
1409						// 			debug
1410						// 		)
1411						// 	);
1412						// }
1413
1414						// Check `this` constraint
1415						{
1416							let this_constraint = get_constraint(constraint, types);
1417
1418							// Check `this` has all prototype information
1419							if let Some(this_constraint) = this_constraint {
1420								let mut state = State {
1421									already_checked: Default::default(),
1422									mode: SubTypingMode::default(),
1423									contributions: None,
1424									object_constraints: None,
1425									others: SubTypingOptions::default(),
1426								};
1427
1428								let result = type_is_subtype(
1429									this_constraint,
1430									prototype,
1431									&mut state,
1432									environment,
1433									types,
1434								);
1435
1436								// crate::utilities::notify!("result={:?}", result);
1437
1438								if let SubTypeResult::IsNotSubType(_reason) = result {
1439									diagnostics.errors.push(FunctionCallingError::MismatchedThis {
1440										expected: TypeStringRepresentation::from_type_id(
1441											this_constraint,
1442											environment,
1443											types,
1444											behavior.debug_types(),
1445										),
1446										found: TypeStringRepresentation::from_type_id(
1447											prototype,
1448											environment,
1449											types,
1450											behavior.debug_types(),
1451										),
1452										call_site,
1453									});
1454								}
1455							}
1456						}
1457
1458						// Set argument
1459						{
1460							// TODO think so
1461							let is_under_dyn = true;
1462							let value_of_this = environment.info.new_object(
1463								Some(prototype),
1464								types,
1465								call_site,
1466								is_under_dyn,
1467							);
1468
1469							type_arguments.insert(this_id, value_of_this);
1470
1471							// crate::utilities::notify!("Calling new on regular function");
1472							// crate::utilities::notify!("Set {:?} top {:?}", free_this_id, value_of_this);
1473						}
1474					}
1475					CalledWithNew::GetterOrSetter { this_type }
1476					| CalledWithNew::Super { this_type } => {
1477						crate::utilities::notify!("Super / getter / setter on regular function?");
1478
1479						type_arguments.insert(this_id, this_type);
1480					}
1481					CalledWithNew::None => {
1482						// TODO
1483						let value_of_this = this_value.get(environment, types, call_site);
1484
1485						type_arguments.insert(this_id, value_of_this);
1486					}
1487				}
1488			}
1489			FunctionBehavior::Constructor { prototype, this_object_type, name: _ } => {
1490				crate::utilities::notify!("Here {:?}", called_with_new);
1491				match called_with_new {
1492					CalledWithNew::GetterOrSetter { .. } | CalledWithNew::None => {
1493						diagnostics
1494							.errors
1495							.push(FunctionCallingError::NeedsToBeCalledWithNewKeyword(call_site));
1496					}
1497					CalledWithNew::New { on: _ } => {
1498						// TODO think so
1499						let is_under_dyn = true;
1500						crate::utilities::notify!(
1501							"Creating new class object with prototype={:?}",
1502							prototype
1503						);
1504						let value_of_this = environment.info.new_object(
1505							Some(prototype),
1506							types,
1507							call_site,
1508							is_under_dyn,
1509						);
1510
1511						crate::utilities::notify!("Here with new");
1512						type_arguments.insert(this_object_type, value_of_this);
1513					}
1514					CalledWithNew::Super { this_type } => {
1515						crate::utilities::notify!(
1516							"Setting this_object {:?} to {:?} in super call",
1517							this_object_type,
1518							this_type
1519						);
1520						type_arguments.insert(this_object_type, this_type);
1521					}
1522				}
1523			}
1524		}
1525
1526		{
1527			let new_target_value = match called_with_new {
1528				CalledWithNew::New { on } => on,
1529				CalledWithNew::Super { .. } => {
1530					crate::utilities::notify!("Get this type for super new.target");
1531					TypeId::UNIMPLEMENTED_ERROR_TYPE
1532					// let ty = this_value.0;
1533					// let on = crate::types::printing::print_type(
1534					// 	ty,
1535					// 	types,
1536					// 	environment,
1537					// 	true,
1538					// );
1539					// crate::utilities::notify!("This argument {}", on);
1540					// ty
1541				}
1542				// In spec, not `new` -> `new.target === undefined`
1543				CalledWithNew::GetterOrSetter { .. } | CalledWithNew::None => {
1544					TypeId::UNDEFINED_TYPE
1545				}
1546			};
1547
1548			type_arguments.insert(TypeId::NEW_TARGET_ARG, new_target_value);
1549		}
1550	}
1551
1552	#[allow(clippy::too_many_arguments)]
1553	fn assign_arguments_to_parameters<B: CallCheckingBehavior>(
1554		&self,
1555		arguments: &[SynthesisedArgument],
1556		type_arguments: &mut SubstitutionArguments<'static>,
1557		(call_site_type_arguments, parent_arguments): (
1558			Option<CallSiteTypeArguments>,
1559			Option<&GenericArguments>,
1560		),
1561		environment: &mut Environment,
1562		types: &mut TypeStore,
1563		call_site: SpanWithSource,
1564		evaluating_effects: bool,
1565		(behavior, diagnostics): (&mut B, &mut CallingDiagnostics),
1566	) {
1567		for (parameter_idx, parameter) in self.parameters.parameters.iter().enumerate() {
1568			// TODO temp
1569
1570			// This handles if the argument is missing but allowing elided arguments
1571			let argument = arguments.get(parameter_idx);
1572
1573			if let Some(argument) = argument {
1574				if argument.spread {
1575					crate::utilities::notify!("TODO spread arguments");
1576				}
1577
1578				if B::CHECK_PARAMETERS {
1579					let result = check_parameter_type(
1580						parameter.ty,
1581						call_site_type_arguments.as_ref(),
1582						parent_arguments,
1583						argument,
1584						type_arguments,
1585						environment,
1586						types,
1587					);
1588
1589					if let SubTypeResult::IsNotSubType(_reasons) = result {
1590						let type_arguments = Some(GenericChainLink::FunctionRoot {
1591							parent_arguments,
1592							call_site_type_arguments: call_site_type_arguments.as_ref(),
1593							type_arguments: &type_arguments.arguments,
1594						});
1595
1596						crate::utilities::notify!("Type arguments are {:?}", type_arguments);
1597
1598						diagnostics.errors.push(FunctionCallingError::InvalidArgumentType {
1599							parameter_type: TypeStringRepresentation::from_type_id_with_generics(
1600								parameter.ty,
1601								type_arguments,
1602								environment,
1603								types,
1604								behavior.debug_types(),
1605							),
1606							argument_type: TypeStringRepresentation::from_type_id_with_generics(
1607								argument.value,
1608								type_arguments,
1609								environment,
1610								types,
1611								behavior.debug_types(),
1612							),
1613							parameter_position: parameter.position,
1614							argument_position: argument.position,
1615							restriction: None,
1616						});
1617					}
1618				} else {
1619					// Already checked so can set
1620					type_arguments.arguments.insert(parameter.ty, argument.value);
1621				}
1622			} else if parameter.is_optional {
1623				type_arguments.arguments.insert(parameter.ty, TypeId::UNDEFINED_TYPE);
1624			} else {
1625				diagnostics.errors.push(FunctionCallingError::MissingArgument {
1626					parameter_position: parameter.position,
1627					call_site,
1628				});
1629			}
1630		}
1631
1632		// Spread parameters here
1633		if self.parameters.parameters.len() < arguments.len() {
1634			if let Some(ref rest_parameter) = self.parameters.rest_parameter {
1635				// TODO reuse synthesise_array literal logic (especially for spread items)
1636				let mut basis = evaluating_effects.then(|| {
1637					ObjectBuilder::new(
1638						Some(TypeId::ARRAY_TYPE),
1639						types,
1640						rest_parameter.position,
1641						&mut environment.info,
1642					)
1643				});
1644
1645				let mut count = 0;
1646
1647				for argument in arguments.iter().skip(self.parameters.parameters.len()) {
1648					if B::CHECK_PARAMETERS {
1649						let result = check_parameter_type(
1650							rest_parameter.item_type,
1651							call_site_type_arguments.as_ref(),
1652							parent_arguments,
1653							argument,
1654							type_arguments,
1655							environment,
1656							types,
1657						);
1658
1659						// TODO different diagnostic?
1660						if let SubTypeResult::IsNotSubType(_reasons) = result {
1661							let type_arguments = Some(GenericChainLink::FunctionRoot {
1662								parent_arguments,
1663								call_site_type_arguments: call_site_type_arguments.as_ref(),
1664								type_arguments: &type_arguments.arguments,
1665							});
1666
1667							diagnostics.errors.push(FunctionCallingError::InvalidArgumentType {
1668								parameter_type:
1669									TypeStringRepresentation::from_type_id_with_generics(
1670										rest_parameter.ty,
1671										type_arguments,
1672										environment,
1673										types,
1674										behavior.debug_types(),
1675									),
1676								argument_type: TypeStringRepresentation::from_type_id_with_generics(
1677									argument.value,
1678									type_arguments,
1679									environment,
1680									types,
1681									behavior.debug_types(),
1682								),
1683								parameter_position: rest_parameter.position,
1684								argument_position: argument.position,
1685								restriction: None,
1686							});
1687						}
1688					}
1689
1690					// Add to spread array
1691					if let Some(basis) = basis.as_mut() {
1692						let key = PropertyKey::from_usize(count);
1693						basis.append(
1694							crate::types::properties::Publicity::Public,
1695							key,
1696							crate::types::properties::PropertyValue::Value(argument.value),
1697							argument.position,
1698							&mut environment.info,
1699						);
1700					}
1701
1702					count += 1;
1703				}
1704
1705				// Set length of spread array
1706				if let Some(mut basis) = basis {
1707					let length = types.new_constant_type(crate::Constant::Number(
1708						(count as f64).try_into().unwrap(),
1709					));
1710
1711					basis.append(
1712						crate::types::properties::Publicity::Public,
1713						PropertyKey::String("length".into()),
1714						crate::types::properties::PropertyValue::Value(length),
1715						rest_parameter.position,
1716						&mut environment.info,
1717					);
1718
1719					let rest_parameter_array_type = basis.build_object();
1720					type_arguments.arguments.insert(rest_parameter.ty, rest_parameter_array_type);
1721				}
1722			} else {
1723				// TODO types.options.allow_extra_arguments
1724				let mut left_over = arguments.iter().skip(self.parameters.parameters.len());
1725				let first = left_over.next().unwrap();
1726				let mut count = 1;
1727				let mut end = None;
1728				while let arg @ Some(_) = left_over.next() {
1729					count += 1;
1730					end = arg;
1731				}
1732				// Creating position for excess arguments
1733				let position = if let Some(end) = end {
1734					first
1735						.position
1736						.without_source()
1737						.union(end.position.without_source())
1738						.with_source(end.position.source)
1739				} else {
1740					first.position
1741				};
1742
1743				if B::CHECK_PARAMETERS {
1744					diagnostics
1745						.errors
1746						.push(FunctionCallingError::ExcessArguments { count, position });
1747				}
1748			}
1749		}
1750
1751		// Set restrictions as arguments IF not set already. So can
1752		if let Some(_call_site_type_arguments) = call_site_type_arguments {
1753			crate::utilities::notify!("TODO type_arguments.covariants");
1754			// for (on, (arg, _)) in call_site_type_arguments {
1755			// 	if type_arguments.arguments.get(&on).is_none() {
1756			// 		type_arguments.arguments.insert(on, arg)
1757			// 	}
1758			// }
1759		}
1760	}
1761}
1762
1763fn check_parameter_type(
1764	parameter_type: TypeId,
1765	call_site_type_arguments: Option<&CallSiteTypeArguments>,
1766	parent: Option<&GenericArguments>,
1767	argument: &SynthesisedArgument,
1768	type_arguments: &mut SubstitutionArguments,
1769	environment: &mut Environment,
1770	types: &mut TypeStore,
1771) -> SubTypeResult {
1772	// crate::utilities::notify!("Argument is {:?}, parent generics {:?}", value, parent);
1773
1774	// TODO properties
1775	let contributions = Contributions {
1776		parent,
1777		call_site_type_arguments,
1778		staging_covariant: Default::default(),
1779		staging_contravariant: Default::default(),
1780	};
1781
1782	// TODO WIP
1783	let mut state = State {
1784		already_checked: Vec::new(),
1785		mode: Default::default(),
1786		contributions: Some(contributions),
1787		others: SubTypingOptions { allow_errors: true },
1788		object_constraints: None,
1789	};
1790
1791	let result = type_is_subtype_with_generics(
1792		(parameter_type, GenericChain::None),
1793		(argument.value, GenericChain::None),
1794		&mut state,
1795		environment,
1796		types,
1797	);
1798
1799	if result.is_subtype() {
1800		// mut parameter_constraint_request,
1801		let Contributions { staging_covariant, staging_contravariant, .. } =
1802			state.contributions.unwrap();
1803
1804		for (on, (value, _)) in staging_covariant.iter() {
1805			crate::utilities::notify!("TODO pick highest covariant?");
1806			if let Some(value) = type_arguments.arguments.get_mut(on) {
1807				*value = types.new_or_type(*value, *value);
1808			} else {
1809				type_arguments.arguments.insert(*on, *value);
1810			}
1811		}
1812
1813		for (idx, (on, (value, depth))) in staging_contravariant.iter().enumerate() {
1814			// This is TSC behavior
1815			// If there exists another argument that is in a deeper than the current don't add it
1816			// TODO only if explicit generic
1817			let is_weaker =
1818				staging_contravariant.iter().enumerate().any(|(idx2, (on2, (_, other_depth)))| {
1819					(on == on2) && idx != idx2 && depth < other_depth
1820				});
1821
1822			if is_weaker {
1823				crate::utilities::notify!("Here skipping as weaker");
1824				continue;
1825			}
1826
1827			// TODO maybe this should have entry api
1828			let value = value.clone().into_type(types);
1829			if let Some(existing) = type_arguments.arguments.get_mut(on) {
1830				crate::utilities::notify!("Here");
1831				*existing = types.new_or_type(*existing, value);
1832			} else {
1833				type_arguments.arguments.insert(*on, value);
1834			}
1835		}
1836
1837		// TODO
1838		let no_events = false;
1839		if no_events {
1840			mark_possible_mutation(argument, parameter_type, types, environment);
1841		}
1842	}
1843
1844	// environment.context_type.requests.append(&mut parameter_constraint_request);
1845
1846	result
1847}
1848
1849#[allow(clippy::type_complexity)]
1850fn synthesise_argument_expressions_wrt_parameters<T: ReadFromFS, A: crate::ASTImplementation>(
1851	callable: &Logical<FunctionLike>,
1852	arguments: &[UnsynthesisedArgument<A>],
1853	(call_site_type_arguments, parent_arguments): (
1854		Option<Vec<(TypeId, SpanWithSource)>>,
1855		Option<&GenericArguments>,
1856	),
1857	environment: &mut Environment,
1858	checking_data: &mut crate::CheckingData<T, A>,
1859) -> (Vec<SynthesisedArgument>, Option<TypeRestrictions>) {
1860	fn synthesise_call_site_type_argument_hints(
1861		type_parameters: &GenericTypeParameters,
1862		call_site_type_arguments: Vec<(TypeId, SpanWithSource)>,
1863		types: &crate::types::TypeStore,
1864		environment: &mut Environment,
1865	) -> TypeRestrictions {
1866		crate::utilities::notify!("call_site_type_arguments {:?}", call_site_type_arguments);
1867
1868		type_parameters
1869			.0
1870			.iter()
1871			.zip(call_site_type_arguments)
1872			.map(|(param, (ty, position))| {
1873				// StructureGeneric for classes
1874				if let Type::RootPolyType(
1875					PolyNature::FunctionGeneric { extends, .. }
1876					| PolyNature::StructureGeneric { extends, .. },
1877				) = types.get_type_by_id(param.type_id)
1878				{
1879					let mut state = State {
1880						already_checked: Default::default(),
1881						mode: SubTypingMode::default(),
1882						contributions: None,
1883						others: Default::default(),
1884						object_constraints: None,
1885					};
1886					let type_is_subtype =
1887						type_is_subtype(*extends, ty, &mut state, environment, types);
1888
1889					if let SubTypeResult::IsNotSubType(_) = type_is_subtype {
1890						todo!("generic argument does not match restriction")
1891					}
1892				} else {
1893					todo!();
1894					// crate::utilities::notify!("Generic parameter with no aliasing restriction, I think this fine on internals");
1895				};
1896
1897				(param.type_id, (ty, position))
1898			})
1899			.collect()
1900	}
1901
1902	match callable {
1903		Logical::Pure(function) => {
1904			let function = checking_data.types.get_function_from_id(function.function);
1905
1906			let type_arguments_restrictions =
1907				match (&function.type_parameters, call_site_type_arguments) {
1908					(None, Some(call_site_type_arguments)) => {
1909						let first_excess_type_parameter = &call_site_type_arguments[0];
1910						checking_data.diagnostics_container.add_error(
1911							TypeCheckError::FunctionCallingError(
1912								FunctionCallingError::ExcessTypeArguments {
1913									expected_count: 0,
1914									count: call_site_type_arguments.len(),
1915									position: first_excess_type_parameter.1,
1916								},
1917							),
1918						);
1919
1920						None
1921					}
1922					(Some(ref function_type_parameters), Some(call_site_type_arguments)) => {
1923						let expected_parameters_length = function_type_parameters.0.len();
1924						let provided_parameters_length = call_site_type_arguments.len();
1925						if provided_parameters_length > expected_parameters_length {
1926							let first_excess_type_parameter =
1927								&call_site_type_arguments[expected_parameters_length];
1928							let last_excess_type_parameter = call_site_type_arguments
1929								.last()
1930								.unwrap_or(first_excess_type_parameter);
1931
1932							let error_position = first_excess_type_parameter
1933								.1
1934								.without_source()
1935								.union(last_excess_type_parameter.1.without_source())
1936								.with_source(first_excess_type_parameter.1.source);
1937
1938							checking_data.diagnostics_container.add_error(
1939								TypeCheckError::FunctionCallingError(
1940									FunctionCallingError::ExcessTypeArguments {
1941										expected_count: expected_parameters_length,
1942										count: provided_parameters_length,
1943										position: error_position,
1944									},
1945								),
1946							);
1947						}
1948						Some(synthesise_call_site_type_argument_hints(
1949							function_type_parameters,
1950							call_site_type_arguments,
1951							&checking_data.types,
1952							environment,
1953						))
1954					}
1955
1956					_ => None,
1957				};
1958
1959			let parameters = function.parameters.clone();
1960
1961			let type_arguments = if type_arguments_restrictions.is_some()
1962				|| parent_arguments.is_some()
1963			{
1964				let mut arguments = type_arguments_restrictions.clone().unwrap_or_default();
1965				match parent_arguments {
1966					Some(GenericArguments::LookUp { on }) => {
1967						// TODO copied from somewhere
1968						let prototype = environment
1969							.get_chain_of_info()
1970							.find_map(|env| env.prototypes.get(on))
1971							.unwrap();
1972
1973						crate::utilities::notify!(
1974							"Here calculating lookup argument prototype={:?} {:?}",
1975							prototype,
1976							on
1977						);
1978						let map =
1979							checking_data.types.lookup_generic_map.get(prototype).unwrap().clone();
1980
1981						for (under, lookup) in map.iter() {
1982							let entries =
1983								lookup.calculate_lookup(environment, &checking_data.types, *on);
1984							let mut iter = entries.into_iter();
1985							let mut ty = iter.next().unwrap_or(TypeId::NEVER_TYPE);
1986							for other in iter {
1987								// crate::utilities::notify!("here {:?}", checking_data.types.get_type_by_id(other));
1988								ty = checking_data.types.new_or_type(ty, other);
1989							}
1990
1991							arguments.insert(*under, (ty, BaseSpan::NULL));
1992						}
1993					}
1994					Some(GenericArguments::ExplicitRestrictions(ers)) => {
1995						arguments.extend(ers.iter().map(|(k, v)| (*k, *v)));
1996					}
1997					Some(GenericArguments::Closure(..)) | None => {}
1998				}
1999
2000				Some(arguments)
2001			} else {
2002				None
2003			};
2004
2005			let arguments = arguments
2006				.iter()
2007				.enumerate()
2008				.map(|(idx, argument)| {
2009					let expected_type = parameters.get_parameter_type_at_index(idx).map_or(
2010						TypeId::ANY_TYPE,
2011						|(parameter_type, _)| {
2012							// crate::utilities::notify!("(pairing explicit generics for) Generic parameter = {:?}", parameter_type);
2013
2014							let ty = checking_data.types.get_type_by_id(parameter_type);
2015							let parameter_type = if let Type::RootPolyType(
2016								PolyNature::Parameter { fixed_to },
2017							) = ty
2018							{
2019								*fixed_to
2020							} else {
2021								crate::utilities::notify!(
2022									"Parameter is not `PolyNature::Parameter`? {:?}",
2023									ty
2024								);
2025								parameter_type
2026							};
2027
2028							if let Some(arguments) = type_arguments.clone() {
2029								if let Some((ty, _)) = arguments.get(&parameter_type) {
2030									*ty
2031								} else {
2032									// Unfortunately this seems the best way to pass down to expected type
2033									crate::utilities::notify!("Here");
2034									checking_data.types.register_type(
2035										Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics {
2036											on: parameter_type,
2037											arguments: GenericArguments::ExplicitRestrictions(
2038												arguments,
2039											),
2040										}),
2041									)
2042								}
2043							} else {
2044								parameter_type
2045							}
2046						},
2047					);
2048
2049					// {
2050					// 	let debug = true;
2051					// 	crate::utilities::notify!(
2052					// 		"Here!, expected = {}",
2053					// 		crate::types::printing::print_type(
2054					// 			expected_type,
2055					// 			&checking_data.types,
2056					// 			environment,
2057					// 			debug
2058					// 		)
2059					// 	);
2060					// }
2061
2062					let value = A::synthesise_expression(
2063						argument.expression,
2064						expected_type,
2065						environment,
2066						checking_data,
2067					);
2068
2069					let position = A::expression_position(argument.expression)
2070						.with_source(environment.get_source());
2071
2072					SynthesisedArgument { spread: argument.spread, position, value }
2073				})
2074				.collect();
2075
2076			(arguments, type_arguments_restrictions)
2077		}
2078		Logical::Implies { on, antecedent } => synthesise_argument_expressions_wrt_parameters(
2079			on,
2080			arguments,
2081			// TODO chain
2082			(call_site_type_arguments, Some(antecedent)),
2083			environment,
2084			checking_data,
2085		),
2086		Logical::Or { .. } => (
2087			arguments
2088				.iter()
2089				.map(|argument| SynthesisedArgument {
2090					spread: argument.spread,
2091					position: A::expression_position(argument.expression)
2092						.with_source(environment.get_source()),
2093					value: A::synthesise_expression(
2094						argument.expression,
2095						// TODO union
2096						TypeId::ANY_TYPE,
2097						environment,
2098						checking_data,
2099					),
2100				})
2101				.collect(),
2102			None,
2103		),
2104		Logical::BasedOnKey { .. } => todo!(),
2105	}
2106}