1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
use std::collections::HashMap;

use source_map::SpanWithSource;

use crate::{
	behavior::functions::{ClassPropertiesToRegister, FunctionBehavior},
	context::{environment::FunctionScope, ContextType},
	events::{Event, RootReference},
	CheckingData, Facts, FunctionId, GenericTypeParameters, Scope, Type, TypeId,
};

use super::{classes::register_properties_into_environment, TypeStore};

/// This is a mesh of annotation and actually defined functions
#[derive(Clone, Debug, binary_serialize_derive::BinarySerializable)]
pub struct FunctionType {
	/// Syntax defined pointer
	pub id: FunctionId,

	pub constant_function: Option<String>,

	/// If async, generator and what to do with `this`
	pub behavior: FunctionBehavior,

	/// TODO not sure about this field and how it tails with Pi Types
	pub type_parameters: Option<GenericTypeParameters>,
	pub parameters: SynthesisedParameters,
	/// This is just aesthetic TODO also throw
	pub return_type: TypeId,

	/// Side effects of the function
	pub effects: Vec<Event>,

	/// Things that this function pulls in. Converse of closed over which is where results below use
	/// variables in this scope.
	pub free_variables: HashMap<RootReference, TypeId>,

	/// References it needs to retain for returning / other effects where things go out.
	///
	/// The type is the initial value of the closure variable when this is called
	pub closed_over_variables: HashMap<RootReference, TypeId>,
}

impl FunctionType {
	pub(crate) fn new_auto_constructor<
		T: crate::ReadFromFS,
		A: crate::ASTImplementation,
		S: ContextType,
	>(
		class_prototype: TypeId,
		properties: ClassPropertiesToRegister<A>,
		// TODO S overkill
		context: &mut crate::context::Context<S>,
		checking_data: &mut CheckingData<T, A>,
	) -> Self {
		let scope = Scope::Function(FunctionScope::Constructor {
			extends: false,
			type_of_super: None,
			this_object_type: TypeId::ERROR_TYPE,
		});

		let (on, env_data, _) = context.new_lexical_environment_fold_into_parent(
			scope,
			checking_data,
			|environment, checking_data| {
				let on = create_this_before_function_synthesis(
					&mut checking_data.types,
					&mut environment.facts,
					class_prototype,
				);
				if let Scope::Function(FunctionScope::Constructor {
					ref mut this_object_type,
					..
				}) = environment.context_type.scope
				{
					*this_object_type = on;
				}

				register_properties_into_environment(environment, on, checking_data, properties);
				on
			},
		);
		// TODO think Some fine
		// TODO
		let behavior =
			FunctionBehavior::Constructor { non_super_prototype: None, this_object_type: on };

		let (facts, _free_variables) = env_data.unwrap();
		Self {
			id: crate::FunctionId::AUTO_CONSTRUCTOR,
			constant_function: None,
			type_parameters: None,
			parameters: SynthesisedParameters::default(),
			// Only needed for printing
			return_type: on,
			effects: facts.events,
			behavior,
			// TODO ???
			free_variables: Default::default(),
			closed_over_variables: Default::default(),
		}
	}
}

/// For inside the function
pub(crate) fn create_this_before_function_synthesis(
	types: &mut TypeStore,
	facts: &mut Facts,
	prototype: TypeId,
) -> TypeId {
	let ty = types.register_type(Type::Object(crate::types::ObjectNature::RealDeal));

	crate::utils::notify!("Registered 'this' type as {:?}", ty);
	let value = Event::CreateObject {
		referenced_in_scope_as: ty,
		prototype: crate::events::PrototypeArgument::Yeah(prototype),
		position: None,
		// TODO right?
		is_function_this: true,
	};
	facts.events.push(value);

	ty
}

/// TODO temp
#[derive(Clone, Copy, Debug, binary_serialize_derive::BinarySerializable)]
pub enum GetSet {
	Get,
	Set,
}

/// Optionality is indicated by what vector it is in [`SynthesisedParameters`]
#[derive(Clone, Debug, binary_serialize_derive::BinarySerializable)]
pub struct SynthesisedParameter {
	pub name: String,
	/// This is the generic parameter type, not the restriction
	pub ty: TypeId,
	pub position: SpanWithSource,
	/// For optional parameters this is [TypeId::UNDEFINED_TYPE] else some type
	pub missing_value: Option<TypeId>,
}

/// **Note that the [Type] here is not array like**
#[derive(Clone, Debug, binary_serialize_derive::BinarySerializable)]
pub struct SynthesisedRestParameter {
	pub name: String,
	/// This is the T, of Array<T>
	pub item_type: TypeId,
	pub position: SpanWithSource,
}

/// A type of a collection of function parameters
///
/// This applies for source functions
#[derive(Clone, Debug, Default, binary_serialize_derive::BinarySerializable)]
pub struct SynthesisedParameters {
	// Even though these vectors are the same type, the latter allows for elided arguments
	pub parameters: Vec<SynthesisedParameter>,
	pub rest_parameter: Option<SynthesisedRestParameter>,
}

impl SynthesisedParameters {
	// TODO should be aware of undefined in optionals possibly
	pub(crate) fn get_type_constraint_at_index(&self, idx: usize) -> Option<TypeId> {
		if let Some(param) = self.parameters.get(idx) {
			Some(param.ty)
		} else {
			self.rest_parameter.as_ref().map(|rest| rest.item_type)
		}
	}
}

/// TODO spread should of tuples should expand into `NonSpread`
/// TODO spread for non heterogenous things
#[derive(Clone, Debug, binary_serialize_derive::BinarySerializable)]
#[non_exhaustive]
pub enum SynthesisedArgument {
	/// This is the get value of a argument
	NonSpread { ty: TypeId, position: SpanWithSource },
	// TODO
	// Spread(Instance),
}

impl SynthesisedArgument {
	pub(crate) fn get_position(&self) -> SpanWithSource {
		match self {
			SynthesisedArgument::NonSpread { ty: _, position } => *position,
		}
	}

	// TODO: Remove when error is added
	#[allow(clippy::unnecessary_wraps)]
	pub(crate) fn to_type(&self) -> Result<TypeId, ()> {
		match self {
			SynthesisedArgument::NonSpread { ty, position: _ } => Ok(*ty),
		}
	}
}