Skip to main content

ezno_checker/features/
functions.rs

1use std::{borrow::Cow, collections::hash_map::Entry};
2
3use source_map::{Nullable, SourceId, SpanWithSource};
4
5use crate::{
6	context::{
7		environment::{ContextLocation, ExpectedReturnType, FunctionScope},
8		get_on_ctx,
9		information::{merge_info, LocalInformation},
10		ContextType, Syntax,
11	},
12	diagnostics::{TypeCheckError, TypeStringRepresentation},
13	events::RootReference,
14	features::{create_closed_over_references, objects::ObjectBuilder},
15	subtyping::{type_is_subtype_object, SubTypeResult},
16	types::{
17		self,
18		calling::Callable,
19		classes::ClassValue,
20		functions::{FunctionBehavior, SynthesisedParameters},
21		generics::GenericTypeParameters,
22		logical::{Logical, LogicalOrValid},
23		printing::print_type,
24		properties::{get_property_unbound, PropertyKey, PropertyValue, Publicity},
25		substitute, Constructor, FunctionEffect, FunctionType, InternalFunctionEffect,
26		PartiallyAppliedGenerics, PolyNature, SubstitutionArguments, SynthesisedParameter,
27		SynthesisedRestParameter, TypeStore,
28	},
29	ASTImplementation, CheckingData, Constant, Environment, FunctionId, GeneralContext, Map,
30	ReadFromFS, Scope, Type, TypeId, VariableId,
31};
32
33#[derive(Debug, Clone, Copy)]
34pub enum GetterSetter {
35	Getter,
36	Setter,
37}
38
39pub fn register_arrow_function<T: crate::ReadFromFS, A: crate::ASTImplementation>(
40	expecting: TypeId,
41	is_async: bool,
42	function: &impl SynthesisableFunction<A>,
43	environment: &mut Environment,
44	checking_data: &mut CheckingData<T, A>,
45) -> TypeId {
46	let function_type = synthesise_function(
47		function,
48		FunctionRegisterBehavior::ArrowFunction { expecting, is_async },
49		environment,
50		checking_data,
51	);
52	checking_data.types.new_function_type(function_type)
53}
54
55#[allow(clippy::too_many_arguments)]
56pub fn register_expression_function<T: crate::ReadFromFS, A: crate::ASTImplementation>(
57	expected: TypeId,
58	is_async: bool,
59	is_generator: bool,
60	location: ContextLocation,
61	name: Option<String>,
62	function: &impl SynthesisableFunction<A>,
63	environment: &mut Environment,
64	checking_data: &mut CheckingData<T, A>,
65) -> TypeId {
66	let name = if let Some(name) = name {
67		checking_data.types.new_constant_type(Constant::String(name))
68	} else {
69		extract_name(expected, &checking_data.types, environment)
70	};
71	let function_type = synthesise_function(
72		function,
73		FunctionRegisterBehavior::ExpressionFunction {
74			expecting: expected,
75			is_async,
76			is_generator,
77			location,
78			name,
79		},
80		environment,
81		checking_data,
82	);
83	checking_data.types.new_function_type(function_type)
84}
85
86#[allow(clippy::too_many_arguments)]
87pub fn synthesise_hoisted_statement_function<T: crate::ReadFromFS, A: crate::ASTImplementation>(
88	variable_id: crate::VariableId,
89	overloaded: bool,
90	is_async: bool,
91	is_generator: bool,
92	location: ContextLocation,
93	name: String,
94	function: &impl SynthesisableFunction<A>,
95	environment: &mut Environment,
96	checking_data: &mut CheckingData<T, A>,
97) -> TypeId {
98	let name = checking_data.types.new_constant_type(Constant::String(name));
99	let behavior = FunctionRegisterBehavior::StatementFunction {
100		variable_id,
101		is_async,
102		is_generator,
103		location,
104		internal_marker: None,
105		name,
106	};
107
108	let function = synthesise_function(function, behavior, environment, checking_data);
109
110	if overloaded {
111		let _last_type = checking_data
112			.local_type_mappings
113			.variables_to_constraints
114			.0
115			.get(&variable_id)
116			.expect("variable constraint not set when trying to unify overload");
117
118		crate::utilities::notify!("TODO check that the result is the same");
119	}
120
121	// crate::utilities::notify!("function.effect={:?}", function.effect);
122
123	let v = checking_data.types.new_function_type(function);
124	environment.info.variable_current_value.insert(variable_id, v);
125	v
126}
127
128#[allow(clippy::too_many_arguments)]
129pub fn synthesise_declare_statement_function<T: crate::ReadFromFS, A: crate::ASTImplementation>(
130	variable_id: crate::VariableId,
131	_overloaded: bool,
132	is_async: bool,
133	is_generator: bool,
134	location: ContextLocation,
135	name: String,
136	internal_marker: Option<InternalFunctionEffect>,
137	function: &impl SynthesisableFunction<A>,
138	environment: &mut Environment,
139	checking_data: &mut CheckingData<T, A>,
140) -> TypeId {
141	let name = checking_data.types.new_constant_type(Constant::String(name));
142	let behavior = FunctionRegisterBehavior::StatementFunction {
143		variable_id,
144		is_async,
145		is_generator,
146		location,
147		internal_marker,
148		name,
149	};
150
151	let function = synthesise_function(function, behavior, environment, checking_data);
152	let ty = checking_data.types.new_function_type(function);
153	environment.info.variable_current_value.insert(variable_id, ty);
154	ty
155}
156
157pub fn function_to_property(
158	getter_setter: Option<GetterSetter>,
159	function: FunctionType,
160	types: &mut TypeStore,
161	is_declare: bool,
162) -> PropertyValue {
163	match getter_setter {
164		Some(GetterSetter::Getter) => {
165			PropertyValue::Getter(Callable::new_from_function(function, types))
166		}
167		Some(GetterSetter::Setter) => {
168			PropertyValue::Setter(Callable::new_from_function(function, types))
169		}
170		None => PropertyValue::Value(
171			if is_declare && matches!(function.effect, FunctionEffect::Unknown) {
172				types.new_hoisted_function_type(function)
173			} else {
174				types.new_function_type(function)
175			},
176		),
177	}
178}
179
180pub fn synthesise_function_default_value<'a, T: crate::ReadFromFS, A: ASTImplementation>(
181	parameter_ty: TypeId,
182	parameter_constraint: TypeId,
183	environment: &mut Environment,
184	checking_data: &mut CheckingData<T, A>,
185	expression: &'a A::Expression<'a>,
186) -> TypeId {
187	let (value, out, ..) = environment.new_lexical_environment_fold_into_parent(
188		Scope::DefaultFunctionParameter {},
189		checking_data,
190		|environment, checking_data| {
191			A::synthesise_expression(expression, parameter_constraint, environment, checking_data)
192		},
193	);
194
195	let at = A::expression_position(expression).with_source(environment.get_source());
196
197	{
198		let result = type_is_subtype_object(
199			parameter_constraint,
200			value,
201			environment,
202			&mut checking_data.types,
203		);
204
205		if let SubTypeResult::IsNotSubType(_) = result {
206			let expected = TypeStringRepresentation::from_type_id(
207				parameter_ty,
208				environment,
209				&checking_data.types,
210				false,
211			);
212
213			let found = TypeStringRepresentation::from_type_id(
214				value,
215				environment,
216				&checking_data.types,
217				false,
218			);
219
220			checking_data
221				.diagnostics_container
222				.add_error(TypeCheckError::InvalidDefaultParameter { at, expected, found });
223		}
224	}
225
226	// Abstraction of `typeof parameter === "undefined"` to generate less types.
227	let is_undefined_condition =
228		checking_data.types.register_type(Type::Constructor(Constructor::TypeExtends(
229			types::TypeExtends { item: parameter_ty, extends: TypeId::UNDEFINED_TYPE },
230		)));
231
232	// TODO is this needed
233	// let union = checking_data.types.new_or_type(parameter_ty, value);
234
235	let result =
236		checking_data.types.register_type(Type::Constructor(Constructor::ConditionalResult {
237			condition: is_undefined_condition,
238			truthy_result: value,
239			otherwise_result: parameter_ty,
240			result_union: parameter_constraint,
241		}));
242
243	// TODO don't share parent
244	let Some(GeneralContext::Syntax(parent)) = environment.context_type.get_parent() else {
245		unreachable!()
246	};
247	merge_info(
248		*parent,
249		&mut environment.info,
250		is_undefined_condition,
251		out.unwrap().0,
252		None,
253		&mut checking_data.types,
254		at,
255	);
256
257	result
258}
259
260#[derive(Clone, Copy)]
261pub struct ReturnType(pub TypeId, pub SpanWithSource);
262
263pub struct PartialFunction(
264	pub Option<GenericTypeParameters>,
265	pub SynthesisedParameters,
266	pub Option<ReturnType>,
267);
268
269/// Covers both actual functions and
270pub trait SynthesisableFunction<A: crate::ASTImplementation> {
271	fn id(&self, source_id: SourceId) -> FunctionId {
272		FunctionId(source_id, self.get_position().start)
273	}
274
275	/// For debugging only
276	fn get_name(&self) -> Option<&str>;
277
278	/// For debugging only
279	fn get_position(&self) -> source_map::Span;
280
281	// TODO temp
282	fn has_body(&self) -> bool;
283
284	// /// For detecting what is inside
285	// fn get_body_span(&self) -> source_map::Span;
286
287	/// **THIS FUNCTION IS EXPECTED TO PUT THE TYPE PARAMETERS INTO THE ENVIRONMENT WHILE SYNTHESISING THEM**
288	fn type_parameters<T: ReadFromFS>(
289		&self,
290		environment: &mut Environment,
291		checking_data: &mut CheckingData<T, A>,
292	) -> Option<GenericTypeParameters>;
293
294	fn this_constraint<T: ReadFromFS>(
295		&self,
296		environment: &mut Environment,
297		checking_data: &mut CheckingData<T, A>,
298	) -> Option<TypeId>;
299
300	/// For object literals
301	fn super_constraint<T: ReadFromFS>(
302		&self,
303		environment: &mut Environment,
304		checking_data: &mut CheckingData<T, A>,
305	) -> Option<TypeId>;
306
307	/// **THIS FUNCTION IS EXPECTED TO PUT THE PARAMETERS INTO THE ENVIRONMENT WHILE SYNTHESISING THEM**
308	fn parameters<T: ReadFromFS>(
309		&self,
310		environment: &mut Environment,
311		checking_data: &mut CheckingData<T, A>,
312		expected_parameters: Option<&SynthesisedParameters>,
313	) -> SynthesisedParameters;
314
315	fn return_type_annotation<T: ReadFromFS>(
316		&self,
317		environment: &mut Environment,
318		checking_data: &mut CheckingData<T, A>,
319	) -> Option<ReturnType>;
320
321	/// Returned type is extracted from events, thus doesn't expect anything in return
322	fn body<T: ReadFromFS>(
323		&self,
324		environment: &mut Environment,
325		checking_data: &mut CheckingData<T, A>,
326	);
327}
328
329/// TODO might be generic if [`FunctionBehavior`] becomes generic
330pub enum FunctionRegisterBehavior<'a, A: crate::ASTImplementation> {
331	ArrowFunction {
332		expecting: TypeId,
333		is_async: bool,
334	},
335	ExpressionFunction {
336		expecting: TypeId,
337		is_async: bool,
338		is_generator: bool,
339		location: ContextLocation,
340		name: TypeId,
341	},
342	StatementFunction {
343		/// For hoisting cases
344		variable_id: VariableId,
345		is_async: bool,
346		is_generator: bool,
347		location: ContextLocation,
348		internal_marker: Option<InternalFunctionEffect>,
349		name: TypeId,
350	},
351	ObjectMethod {
352		// TODO this will take PartialFunction from hoisted?
353		expecting: TypeId,
354		is_async: bool,
355		is_generator: bool,
356		name: TypeId, // location: ContextLocation,
357	},
358	ClassMethod {
359		// TODO this will take PartialFunction from hoisted?
360		expecting: TypeId,
361		is_async: bool,
362		is_generator: bool,
363		super_type: Option<TypeId>,
364		internal_marker: Option<InternalFunctionEffect>,
365		/// Used for shape of `this`
366		this_shape: TypeId,
367		name: TypeId,
368	},
369	Constructor {
370		prototype: TypeId,
371		/// Is this [`Option::is_some`] then can use `super()`
372		super_type: Option<TypeId>,
373		properties: ClassPropertiesToRegister<'a, A>,
374		internal_marker: Option<InternalFunctionEffect>,
375		/// Name of the class
376		name: TypeId,
377	},
378}
379
380pub struct ClassPropertiesToRegister<'a, A: crate::ASTImplementation> {
381	pub properties: Vec<ClassValue<'a, A>>,
382}
383
384impl<'a, A: crate::ASTImplementation> FunctionRegisterBehavior<'a, A> {
385	#[must_use]
386	pub fn is_async(&self) -> bool {
387		match self {
388			FunctionRegisterBehavior::ArrowFunction { is_async, .. }
389			| FunctionRegisterBehavior::ExpressionFunction { is_async, .. }
390			| FunctionRegisterBehavior::StatementFunction { is_async, .. }
391			| FunctionRegisterBehavior::ObjectMethod { is_async, .. }
392			| FunctionRegisterBehavior::ClassMethod { is_async, .. } => *is_async,
393			FunctionRegisterBehavior::Constructor { .. } => false,
394		}
395	}
396
397	#[must_use]
398	pub fn is_generator(&self) -> bool {
399		match self {
400			FunctionRegisterBehavior::ExpressionFunction { is_generator, .. }
401			| FunctionRegisterBehavior::StatementFunction { is_generator, .. }
402			| FunctionRegisterBehavior::ObjectMethod { is_generator, .. }
403			| FunctionRegisterBehavior::ClassMethod { is_generator, .. } => *is_generator,
404			FunctionRegisterBehavior::ArrowFunction { .. }
405			| FunctionRegisterBehavior::Constructor { .. } => false,
406		}
407	}
408}
409
410#[derive(Clone, Debug, Default, binary_serialize_derive::BinarySerializable)]
411pub struct ClosedOverVariables(pub(crate) Map<VariableId, TypeId>);
412
413#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, binary_serialize_derive::BinarySerializable)]
414pub struct ClosureId(pub(crate) u32);
415
416pub trait ClosureChain {
417	fn get_fact_from_closure<T, R>(&self, fact: &LocalInformation, cb: T) -> Option<R>
418	where
419		T: Fn(ClosureId) -> Option<R>;
420}
421
422pub(crate) fn synthesise_function<T, A, F>(
423	function: &F,
424	behavior: FunctionRegisterBehavior<A>,
425	base_environment: &mut Environment,
426	checking_data: &mut CheckingData<T, A>,
427) -> FunctionType
428where
429	T: crate::ReadFromFS,
430	A: crate::ASTImplementation,
431	F: SynthesisableFunction<A>,
432{
433	struct FunctionKind<'a, A: crate::ASTImplementation> {
434		pub(super) behavior: FunctionBehavior,
435		pub(super) scope: FunctionScope,
436		pub(super) internal: Option<InternalFunctionEffect>,
437		/// TODO wip
438		pub(super) constructor: Option<(TypeId, ClassPropertiesToRegister<'a, A>)>,
439		pub(super) expected_parameters: Option<SynthesisedParameters>,
440		pub(super) this_shape: Option<TypeId>,
441	}
442
443	let _is_async = behavior.is_async();
444	let _is_generator = behavior.is_generator();
445
446	// unfold information from the behavior
447	let kind: FunctionKind<A> = match behavior {
448		FunctionRegisterBehavior::Constructor {
449			super_type,
450			prototype,
451			properties,
452			internal_marker,
453			name,
454		} => {
455			FunctionKind {
456				behavior: FunctionBehavior::Constructor {
457					prototype,
458					this_object_type: TypeId::IS_ASSIGNED_VALUE_LATER,
459					name,
460				},
461				scope: FunctionScope::Constructor {
462					extends: super_type.is_some(),
463					type_of_super: super_type,
464					this_object_type: TypeId::IS_ASSIGNED_VALUE_LATER,
465				},
466				internal: internal_marker,
467				constructor: Some((prototype, properties)),
468				expected_parameters: None,
469				// TODO
470				this_shape: None,
471			}
472		}
473		FunctionRegisterBehavior::ArrowFunction { expecting, is_async } => {
474			let (expected_parameters, expected_return) = get_expected_parameters_from_type(
475				expecting,
476				&mut checking_data.types,
477				base_environment,
478			);
479
480			if let Some((or, _)) =
481				expected_parameters.as_ref().and_then(|a| a.get_parameter_type_at_index(0))
482			{
483				crate::utilities::notify!(
484					"First expected parameter {:?}",
485					print_type(or, &checking_data.types, base_environment, true)
486				);
487			}
488
489			FunctionKind {
490				behavior: FunctionBehavior::ArrowFunction { is_async },
491				scope: FunctionScope::ArrowFunction {
492					free_this_type: TypeId::IS_ASSIGNED_VALUE_LATER,
493					is_async,
494					expected_return: expected_return.map(ExpectedReturnType::Inferred),
495				},
496				internal: None,
497				constructor: None,
498				expected_parameters,
499				this_shape: None,
500			}
501		}
502		FunctionRegisterBehavior::ExpressionFunction {
503			expecting,
504			is_async,
505			is_generator,
506			location,
507			name,
508		} => {
509			let (expected_parameters, expected_return) = get_expected_parameters_from_type(
510				expecting,
511				&mut checking_data.types,
512				base_environment,
513			);
514
515			let prototype = checking_data
516				.types
517				.register_type(Type::Object(crate::types::ObjectNature::RealDeal));
518
519			FunctionKind {
520				behavior: FunctionBehavior::Function {
521					is_async,
522					is_generator,
523					this_id: TypeId::IS_ASSIGNED_VALUE_LATER,
524					prototype,
525					name,
526				},
527				scope: FunctionScope::Function {
528					is_generator,
529					is_async,
530					// to set
531					this_type: TypeId::IS_ASSIGNED_VALUE_LATER,
532					type_of_super: TypeId::ANY_TYPE,
533					expected_return: expected_return.map(ExpectedReturnType::Inferred),
534					location,
535				},
536				internal: None,
537				constructor: None,
538				expected_parameters,
539				this_shape: None,
540			}
541		}
542		FunctionRegisterBehavior::StatementFunction {
543			variable_id: _variable_id,
544			is_async,
545			is_generator,
546			location,
547			internal_marker,
548			name,
549		} => {
550			let prototype = checking_data
551				.types
552				.register_type(Type::Object(crate::types::ObjectNature::RealDeal));
553
554			FunctionKind {
555				behavior: FunctionBehavior::Function {
556					is_async,
557					is_generator,
558					prototype,
559					this_id: TypeId::IS_ASSIGNED_VALUE_LATER,
560					name,
561				},
562				scope: FunctionScope::Function {
563					is_generator,
564					is_async,
565					this_type: TypeId::IS_ASSIGNED_VALUE_LATER,
566					type_of_super: TypeId::IS_ASSIGNED_VALUE_LATER,
567					expected_return: None,
568					location,
569				},
570				internal: internal_marker,
571				constructor: None,
572				expected_parameters: None,
573				this_shape: None,
574			}
575		}
576		FunctionRegisterBehavior::ClassMethod {
577			is_async,
578			is_generator,
579			super_type: _,
580			expecting,
581			internal_marker,
582			this_shape,
583			name,
584		} => {
585			let (expected_parameters, expected_return) = get_expected_parameters_from_type(
586				expecting,
587				&mut checking_data.types,
588				base_environment,
589			);
590
591			FunctionKind {
592				behavior: FunctionBehavior::Method {
593					is_async,
594					is_generator,
595					free_this_id: this_shape,
596					name,
597				},
598				scope: FunctionScope::MethodFunction {
599					free_this_type: this_shape,
600					is_async,
601					is_generator,
602					expected_return: expected_return.map(ExpectedReturnType::Inferred),
603				},
604				internal: internal_marker,
605				constructor: None,
606				expected_parameters,
607				this_shape: Some(this_shape),
608			}
609		}
610		FunctionRegisterBehavior::ObjectMethod { is_async, is_generator, expecting, name } => {
611			let (expected_parameters, expected_return) = get_expected_parameters_from_type(
612				expecting,
613				&mut checking_data.types,
614				base_environment,
615			);
616
617			FunctionKind {
618				behavior: FunctionBehavior::Method {
619					is_async,
620					is_generator,
621					free_this_id: TypeId::IS_ASSIGNED_VALUE_LATER,
622					name,
623				},
624				scope: FunctionScope::MethodFunction {
625					free_this_type: TypeId::IS_ASSIGNED_VALUE_LATER,
626					is_async,
627					is_generator,
628					expected_return: expected_return.map(ExpectedReturnType::Inferred),
629				},
630				internal: None,
631				constructor: None,
632				expected_parameters,
633				// TODO could be something in the future
634				this_shape: None,
635			}
636		}
637	};
638
639	let id = function.id(base_environment.get_source());
640	let FunctionKind {
641		mut behavior,
642		scope,
643		internal,
644		constructor,
645		expected_parameters,
646		this_shape,
647	} = kind;
648
649	let mut function_environment = base_environment.new_lexical_environment(Scope::Function(scope));
650
651	if function.has_body() {
652		let type_parameters = if let Some((ref prototype, _)) = constructor {
653			// Class generics here
654			class_generics_to_function_generics(*prototype, &checking_data.types)
655		} else {
656			function.type_parameters(&mut function_environment, checking_data)
657		};
658
659		// TODO should be in function, but then requires mutable environment :(
660		let this_constraint =
661			function.this_constraint(&mut function_environment, checking_data).or(this_shape);
662
663		let position = function.get_position().with_source(base_environment.get_source());
664		// `this` changes stuff
665		if let Scope::Function(ref mut scope) = function_environment.context_type.scope {
666			match scope {
667				FunctionScope::ArrowFunction { ref mut free_this_type, .. }
668				| FunctionScope::MethodFunction { ref mut free_this_type, .. } => {
669					let type_id = if let Some(tc) = this_constraint {
670						checking_data.types.register_type(Type::RootPolyType(
671							PolyNature::FreeVariable {
672								reference: RootReference::This,
673								based_on: tc,
674							},
675						))
676					} else {
677						TypeId::ANY_INFERRED_FREE_THIS
678					};
679
680					if let FunctionBehavior::Method { ref mut free_this_id, .. } = behavior {
681						*free_this_id = type_id;
682					}
683					*free_this_type = type_id;
684				}
685				FunctionScope::Function { ref mut this_type, .. } => {
686					// TODO temp to reduce types
687
688					// TODO this could be done conditionally to create less objects, but also doesn't introduce any bad side effects so
689					// TODO prototype needs to be a poly based on this.prototype. This also fixes inference
690
691					let (this_free_variable, this_constructed_object) =
692						if let Some(this_constraint) = this_constraint {
693							// TODO I don't whether NEW_TARGET_ARG should have a backer
694							let prototype = checking_data.types.register_type(Type::Constructor(
695								Constructor::Property {
696									on: TypeId::NEW_TARGET_ARG,
697									under: PropertyKey::String(Cow::Owned("value".to_owned())),
698									result: this_constraint,
699									mode: types::properties::AccessMode::Regular,
700								},
701							));
702
703							let this_constructed_object = function_environment.info.new_object(
704								Some(prototype),
705								&mut checking_data.types,
706								position,
707								false,
708							);
709
710							let ty = Type::RootPolyType(PolyNature::FreeVariable {
711								reference: RootReference::This,
712								based_on: this_constraint,
713							});
714
715							let this_free_variable = checking_data.types.register_type(ty);
716
717							(this_free_variable, this_constructed_object)
718						} else {
719							// TODO inferred prototype
720							let this_constructed_object = function_environment.info.new_object(
721								None,
722								&mut checking_data.types,
723								position,
724								false,
725							);
726							(TypeId::ANY_INFERRED_FREE_THIS, this_constructed_object)
727						};
728
729					let new_conditional_type = checking_data.types.new_conditional_type(
730						TypeId::NEW_TARGET_ARG,
731						this_constructed_object,
732						this_free_variable,
733					);
734
735					// TODO ahhhh
736					if let FunctionBehavior::Function { this_id: ref mut free_this_id, .. } =
737						behavior
738					{
739						*free_this_id = new_conditional_type;
740					}
741
742					// TODO set super type as well
743					// TODO what is the union, shouldn't it be the this_constraint?
744
745					*this_type = new_conditional_type;
746				}
747				FunctionScope::Constructor {
748					extends: _,
749					type_of_super: _,
750					ref mut this_object_type,
751				} => {
752					crate::utilities::notify!("Setting 'this' type here");
753					if let Some((prototype, properties)) = constructor {
754						let this_constructed_object = function_environment.info.new_object(
755							Some(prototype),
756							&mut checking_data.types,
757							// TODO this is cheating to not queue event
758							position,
759							false,
760						);
761
762						*this_object_type = this_constructed_object;
763
764						// TODO super/derived behavior
765						types::classes::register_properties_into_environment(
766							&mut function_environment,
767							this_constructed_object,
768							checking_data,
769							properties,
770							position,
771						);
772
773						// function_environment.can_reference_this =
774						// 	CanReferenceThis::ConstructorCalled;
775
776						if let FunctionBehavior::Constructor { ref mut this_object_type, .. } =
777							behavior
778						{
779							crate::utilities::notify!("Set this object type");
780							*this_object_type = this_constructed_object;
781						} else {
782							unreachable!()
783						}
784					} else {
785						unreachable!()
786					}
787				}
788			}
789		} else {
790			unreachable!()
791		}
792
793		// TODO reuse existing if hoisted or can be sent down
794		let synthesised_parameters = function.parameters(
795			&mut function_environment,
796			checking_data,
797			expected_parameters.as_ref(),
798		);
799
800		let return_type_annotation =
801			function.return_type_annotation(&mut function_environment, checking_data);
802
803		{
804			// Add expected return type
805			if let Scope::Function(ref mut scope) = function_environment.context_type.scope {
806				if !matches!(scope, FunctionScope::Constructor { .. }) {
807					if let (expect @ None, Some(ReturnType(return_type_annotation, pos))) =
808						(scope.get_expected_return_type_mut(), return_type_annotation)
809					{
810						*expect = Some(ExpectedReturnType::FromReturnAnnotation(
811							return_type_annotation,
812							// TODO lol
813							pos.without_source(),
814						));
815					}
816				}
817			}
818		}
819
820		function.body(&mut function_environment, checking_data);
821
822		let closes_over = create_closed_over_references(
823			&function_environment.context_type.closed_over_references,
824			&function_environment,
825			&checking_data.types,
826		);
827
828		let Syntax {
829			free_variables,
830			closed_over_references: function_closes_over,
831			requests: _,
832			..
833		} = function_environment.context_type;
834
835		// crate::utilities::notify!(
836		// 	"closes_over {:?}, free_variable {:?}, in {:?}",
837		// 	closes_over,
838		// 	free_variables,
839		// 	function.get_name()
840		// );
841
842		let info = function_environment.info;
843		let variable_names = function_environment.variable_names;
844
845		let returned = if function.has_body() {
846			let returned = info.state.get_returned(&mut checking_data.types);
847
848			// TODO temp fix for predicates. This should be really be done in `environment.throw` ()?
849			if let Some(ReturnType(expected, _)) = return_type_annotation {
850				if crate::types::type_is_assert_is_type(expected, &checking_data.types).is_ok() {
851					let mut state = crate::subtyping::State {
852						already_checked: Default::default(),
853						mode: Default::default(),
854						contributions: Default::default(),
855						others: crate::subtyping::SubTypingOptions::default(),
856						// TODO don't think there is much case in constraining it here
857						object_constraints: None,
858					};
859
860					let result = crate::subtyping::type_is_subtype(
861						expected,
862						returned,
863						&mut state,
864						base_environment,
865						&checking_data.types,
866					);
867
868					if let crate::subtyping::SubTypeResult::IsNotSubType(_) = result {
869						checking_data.diagnostics_container.add_error(
870							TypeCheckError::ReturnedTypeDoesNotMatch {
871								expected_return_type: TypeStringRepresentation::from_type_id(
872									expected,
873									base_environment,
874									&checking_data.types,
875									checking_data.options.debug_types,
876								),
877								returned_type: TypeStringRepresentation::from_type_id(
878									returned,
879									base_environment,
880									&checking_data.types,
881									checking_data.options.debug_types,
882								),
883								annotation_position: position,
884								returned_position: function
885									.get_position()
886									.with_source(base_environment.get_source()),
887							},
888						);
889					}
890				}
891			}
892
893			returned
894		} else if let Some(ReturnType(ty, _)) = return_type_annotation {
895			ty
896		} else {
897			TypeId::UNDEFINED_TYPE
898		};
899
900		{
901			// let mut _back_requests = HashMap::<(), ()>::new();
902			// for (on, to) in requests {
903			// 	let type_to_alter = checking_data.types.get_type_by_id(on);
904			// 	if let Type::RootPolyType(
905			// 		PolyNature::Parameter { .. } | PolyNature::FreeVariable { .. },
906			// 	) = type_to_alter
907			// 	{
908			// 		checking_data.types.set_inferred_constraint(on, to);
909			// 	} else if let Type::Constructor(constructor) = type_to_alter {
910			// 		crate::utilities::notify!("TODO constructor {:?}", constructor);
911			// 	} else {
912			// 		crate::utilities::notify!("TODO {:?}", type_to_alter);
913			// 	}
914			// 	crate::utilities::notify!("TODO temp, setting inferred constraint. No nesting");
915			// }
916		}
917
918		// TODO this fixes prototypes and properties being lost during printing and subtyping of the return type
919		{
920			for (k, v) in &info.prototypes {
921				base_environment.info.prototypes.insert(*k, *v);
922			}
923
924			for (on, properties) in info.current_properties {
925				match base_environment.info.current_properties.entry(on) {
926					Entry::Occupied(_occupied) => {}
927					Entry::Vacant(vacant) => {
928						vacant.insert(properties);
929					}
930				}
931			}
932
933			// TODO explain
934			for (on, properties) in info.closure_current_values {
935				match base_environment.info.closure_current_values.entry(on) {
936					Entry::Occupied(_occupied) => {}
937					Entry::Vacant(vacant) => {
938						vacant.insert(properties);
939					}
940				}
941			}
942		}
943
944		// TODO collect here because of lifetime mutation issues from closed over
945		let continues_to_close_over = function_closes_over
946			.into_iter()
947			.filter(|r| match r {
948				RootReference::Variable(id) => {
949					// Keep if body does not contain id
950					let contains = base_environment
951						.parents_iter()
952						.any(|c| get_on_ctx!(&c.variable_names).contains_key(id));
953
954					crate::utilities::notify!("v-id {:?} con {:?}", id, contains);
955					contains
956				}
957				RootReference::This => !behavior.can_be_bound(),
958			})
959			.collect::<Vec<_>>();
960
961		if let Some(closed_over_variables) =
962			base_environment.context_type.get_closed_over_references_mut()
963		{
964			closed_over_variables.extend(free_variables.iter().cloned());
965			closed_over_variables.extend(continues_to_close_over);
966		}
967
968		// TODO should references used in the function be counted in this scope
969		// might break the checking though
970
971		let free_variables = free_variables
972			.into_iter()
973			.map(|reference| {
974				// TODO get the restriction from the context type
975				(reference, TypeId::ANY_TYPE)
976			})
977			.collect();
978
979		// TODO why
980		base_environment.variable_names.extend(variable_names);
981
982		// While could just use returned, if it uses the annotation as the return type
983		let return_type = return_type_annotation.map_or(returned, |ReturnType(ty, _)| ty);
984
985		let effect = FunctionEffect::SideEffects {
986			events: info.events,
987			free_variables,
988			closed_over_variables: closes_over,
989		};
990
991		FunctionType {
992			id,
993			behavior,
994			type_parameters,
995			parameters: synthesised_parameters,
996			return_type,
997			effect,
998		}
999	} else {
1000		// TODO this might not need to create a new environment if no type parameters AND the parameters don't get added to environment
1001		let type_parameters = function.type_parameters(&mut function_environment, checking_data);
1002
1003		// TODO DO NOT ASSIGN
1004		let parameters = function.parameters(
1005			&mut function_environment,
1006			checking_data,
1007			expected_parameters.as_ref(),
1008		);
1009
1010		let return_type = function
1011			.return_type_annotation(&mut function_environment, checking_data)
1012			.map_or(TypeId::ANY_TYPE, |ReturnType(ty, _)| ty);
1013
1014		for (on, properties) in function_environment.info.current_properties {
1015			match base_environment.info.current_properties.entry(on) {
1016				Entry::Occupied(_occupied) => {}
1017				Entry::Vacant(vacant) => {
1018					vacant.insert(properties);
1019				}
1020			}
1021		}
1022
1023		let effect = internal.map_or(FunctionEffect::Unknown, Into::into);
1024
1025		FunctionType { id, type_parameters, parameters, return_type, behavior, effect }
1026	}
1027}
1028
1029fn get_expected_parameters_from_type(
1030	expecting: TypeId,
1031	types: &mut TypeStore,
1032	environment: &mut Environment,
1033) -> (Option<SynthesisedParameters>, Option<TypeId>) {
1034	let ty = types.get_type_by_id(expecting);
1035	if let Type::FunctionReference(func_id) = ty {
1036		let f = types.get_function_from_id(*func_id);
1037		(Some(f.parameters.clone()), Some(f.return_type))
1038	} else if let Type::PartiallyAppliedGenerics(PartiallyAppliedGenerics { arguments, on }) = ty {
1039		let structure_generic_arguments = arguments.clone();
1040
1041		// TODO abstract done too many times
1042		let type_arguments = match structure_generic_arguments {
1043			types::generics::generic_type_arguments::GenericArguments::ExplicitRestrictions(
1044				explicit_restrictions,
1045			) => SubstitutionArguments {
1046				parent: None,
1047				arguments: explicit_restrictions.into_iter().map(|(l, (r, _))| (l, r)).collect(),
1048				closures: Default::default(),
1049			},
1050			types::generics::generic_type_arguments::GenericArguments::Closure(_) => todo!(),
1051			types::generics::generic_type_arguments::GenericArguments::LookUp { .. } => todo!(),
1052		};
1053
1054		let (expected_parameters, expected_return_type) =
1055			get_expected_parameters_from_type(*on, types, environment);
1056
1057		(
1058			expected_parameters.map(|e| {
1059				let parameters = e
1060					.parameters
1061					.into_iter()
1062					.map(|p| SynthesisedParameter {
1063						ty: substitute(
1064							if let Type::RootPolyType(PolyNature::Parameter { fixed_to }) =
1065								types.get_type_by_id(p.ty)
1066							{
1067								*fixed_to
1068							} else {
1069								p.ty
1070							},
1071							&type_arguments,
1072							environment,
1073							types,
1074						),
1075						..p
1076					})
1077					.collect();
1078
1079				let rest_parameter = e.rest_parameter.map(|rp| SynthesisedRestParameter {
1080					item_type: substitute(rp.item_type, &type_arguments, environment, types),
1081					..rp
1082				});
1083
1084				SynthesisedParameters { parameters, rest_parameter }
1085			}),
1086			expected_return_type.map(|rt| substitute(rt, &type_arguments, environment, types)),
1087		)
1088	} else {
1089		crate::utilities::notify!("(un)Expected = {:?}", ty);
1090		(None, None)
1091	}
1092}
1093
1094/// This is to implement <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name>
1095///
1096/// Creates (expected & { name }) intersection object
1097///
1098/// Little bit complex ...
1099pub fn new_name_expected_object(
1100	name: TypeId,
1101	expected: TypeId,
1102	types: &mut TypeStore,
1103	environment: &mut Environment,
1104) -> TypeId {
1105	let mut name_object =
1106		ObjectBuilder::new(None, types, SpanWithSource::NULL, &mut environment.info);
1107
1108	name_object.append(
1109		types::properties::Publicity::Public,
1110		PropertyKey::String(Cow::Borrowed("name")),
1111		PropertyValue::Value(name),
1112		SpanWithSource::NULL,
1113		&mut environment.info,
1114	);
1115
1116	types.new_and_type(expected, name_object.build_object()) // .unwrap()
1117}
1118
1119/// Reverse of the above
1120#[must_use]
1121pub fn extract_name(expecting: TypeId, types: &TypeStore, environment: &Environment) -> TypeId {
1122	if let Type::And(_, rhs) = types.get_type_by_id(expecting) {
1123		if let Ok(LogicalOrValid::Logical(Logical::Pure(PropertyValue::Value(ty)))) =
1124			get_property_unbound(
1125				(*rhs, None),
1126				(Publicity::Public, &PropertyKey::String(Cow::Borrowed("name")), None),
1127				false,
1128				environment,
1129				types,
1130			) {
1131			ty
1132		} else {
1133			crate::utilities::notify!("Here");
1134			TypeId::EMPTY_STRING
1135		}
1136	} else {
1137		TypeId::EMPTY_STRING
1138	}
1139}
1140
1141#[must_use]
1142pub fn class_generics_to_function_generics(
1143	prototype: TypeId,
1144	types: &TypeStore,
1145) -> Option<GenericTypeParameters> {
1146	types.get_type_by_id(prototype).get_parameters().map(|parameters| {
1147		parameters
1148			.into_iter()
1149			.map(|ty| {
1150				let Type::RootPolyType(PolyNature::StructureGeneric { name, .. }) =
1151					types.get_type_by_id(ty)
1152				else {
1153					unreachable!()
1154				};
1155				crate::types::generics::GenericTypeParameter {
1156					name: name.clone(),
1157					// Using its associated [`Type`], its restriction can be found
1158					type_id: ty,
1159					default: None,
1160				}
1161			})
1162			.collect()
1163	})
1164}