Просмотр исходного кода

Merge branch 'development' into constrained_monomorphs

# Conflicts:
#	src/core/type.ml
#	src/typing/fields.ml
#	src/typing/generic.ml
Simon Krajewski 5 лет назад
Родитель
Сommit
bf94564451
100 измененных файлов с 4359 добавлено и 3566 удалено
  1. 19 1
      Makefile
  2. 1 1
      Makefile.win
  3. 16 3
      azure-pipelines.yml
  4. 17 0
      extra/CHANGES.txt
  5. 2 1
      extra/azure-pipelines/build-linux.yml
  6. 2 1
      extra/azure-pipelines/build-mac.yml
  7. 2 1
      extra/azure-pipelines/build-windows.yml
  8. 1 0
      extra/release-checklist.txt
  9. 1 0
      libs/ilib/dune
  10. 2 1
      libs/ttflib/dune
  11. 2 0
      plugins/.gitignore
  12. 1 0
      plugins/example/.gitignore
  13. 24 0
      plugins/example/README.md
  14. 7 0
      plugins/example/dune
  15. 12 0
      plugins/example/haxelib.json
  16. 32 0
      plugins/example/hx/Example.macro.hx
  17. 70 0
      plugins/example/ml/example.ml
  18. 11 0
      src-json/define.json
  19. 13 2
      src/codegen/gencommon/castDetect.ml
  20. 3 5
      src/codegen/gencommon/normalize.ml
  21. 3 1
      src/compiler/haxe.ml
  22. 2 22
      src/compiler/server.ml
  23. 0 11
      src/context/compilationServer.ml
  24. 2 1
      src/context/typecore.ml
  25. 4 1
      src/core/abstract.ml
  26. 8 4
      src/core/error.ml
  27. 2 2
      src/core/globals.ml
  28. 733 0
      src/core/tFunctions.ml
  29. 322 0
      src/core/tOther.ml
  30. 656 0
      src/core/tPrinting.ml
  31. 376 0
      src/core/tType.ml
  32. 925 0
      src/core/tUnification.ml
  33. 251 4
      src/core/texpr.ml
  34. 4 0
      src/core/timer.ml
  35. 6 3250
      src/core/type.ml
  36. 11 3
      src/dune
  37. 2 2
      src/filters/filters.ml
  38. 3 6
      src/generators/genjs.ml
  39. 6 4
      src/generators/genjvm.ml
  40. 6 3
      src/generators/genlua.ml
  41. 24 18
      src/generators/genphp7.ml
  42. 11 9
      src/generators/genpy.ml
  43. 34 12
      src/macro/eval/evalDebugSocket.ml
  44. 5 0
      src/macro/macroApi.ml
  45. 2 2
      src/optimization/analyzerTexprTransformer.ml
  46. 14 5
      src/optimization/inline.ml
  47. 1 0
      src/syntax/grammar.mly
  48. 42 1
      src/typing/typeloadFields.ml
  49. 1 0
      src/typing/typeloadFunction.ml
  50. 16 10
      src/typing/typerDisplay.ml
  51. 4 2
      std/cs/Boot.hx
  52. 1 0
      std/flash/_std/haxe/Http.hx
  53. 22 7
      std/haxe/CallStack.hx
  54. 17 7
      std/haxe/EntryPoint.hx
  55. 4 4
      std/haxe/MainLoop.hx
  56. 1 0
      std/haxe/http/HttpBase.hx
  57. 18 0
      std/haxe/macro/Context.hx
  58. 1 1
      std/haxe/macro/Printer.hx
  59. 3 0
      std/haxe/zip/Huffman.hx
  60. 1 1
      std/hl/_std/sys/db/Sqlite.hx
  61. 2 0
      std/java/Init.hx
  62. 3 1
      std/java/_std/EReg.hx
  63. 1 1
      std/js/Lib.hx
  64. 2 1
      std/js/lib/Float32Array.hx
  65. 2 1
      std/js/lib/Float64Array.hx
  66. 2 0
      std/js/lib/Map.hx
  67. 1 11
      std/lua/Boot.hx
  68. 12 0
      std/lua/NativeStringTools.hx
  69. 7 2
      std/lua/_lua/_hx_bit.lua
  70. 25 9
      std/lua/_lua/_hx_bit_clamp.lua
  71. 37 6
      std/lua/_std/EReg.hx
  72. 3 3
      std/lua/_std/Sys.hx
  73. 0 6
      std/lua/lib/luv/Misc.hx
  74. 43 0
      std/lua/lib/luv/Os.hx
  75. 57 0
      std/php/ArrayIterator.hx
  76. 1 1
      std/php/Boot.hx
  77. 31 0
      std/php/Countable.hx
  78. 5 3
      std/php/IteratorAggregate.hx
  79. 31 0
      std/php/JsonSerializable.hx
  80. 36 0
      std/php/NativeIterator.hx
  81. 8 0
      std/php/NativeStructArray.hx
  82. 31 0
      std/php/SeekableIterator.hx
  83. 32 0
      std/php/Serializable.hx
  84. 15 4
      std/php/_std/Array.hx
  85. 25 25
      std/php/_std/EReg.hx
  86. 3 1
      std/php/_std/haxe/ds/IntMap.hx
  87. 3 1
      std/php/_std/haxe/ds/ObjectMap.hx
  88. 3 1
      std/php/_std/haxe/ds/StringMap.hx
  89. 64 64
      std/php/db/PDO.hx
  90. 3 3
      std/php/reflection/ReflectionClass.hx
  91. 6 6
      std/php/reflection/ReflectionMethod.hx
  92. 4 4
      std/php/reflection/ReflectionProperty.hx
  93. 24 2
      std/python/internal/HxOverrides.hx
  94. 23 0
      tests/display/src/cases/Issue8789.hx
  95. 5 0
      tests/misc/php/projects/Issue8851/compile.hxml
  96. 16 0
      tests/misc/php/projects/Issue8851/src/Main.hx
  97. 1 1
      tests/misc/projects/Issue7526/compile-fail.hxml.stderr
  98. 7 0
      tests/misc/projects/Issue8819/Main.hx
  99. 1 0
      tests/misc/projects/Issue8819/compile-fail.hxml
  100. 2 0
      tests/misc/projects/Issue8819/compile-fail.hxml.stderr

+ 19 - 1
Makefile

@@ -29,6 +29,19 @@ EXTENSION=
 LFLAGS=
 STATICLINK?=0
 
+SYSTEM_NAME=Unknown
+ifeq ($(OS),Windows_NT)
+	SYSTEM_NAME=Windows
+else
+	UNAME_S := $(shell uname -s)
+	ifeq ($(UNAME_S),Linux)
+		SYSTEM_NAME=Linux
+	endif
+	ifeq ($(UNAME_S),Darwin)
+		SYSTEM_NAME=Mac
+	endif
+endif
+
 # Configuration
 
 ADD_REVISION?=0
@@ -61,6 +74,11 @@ haxe:
 	$(DUNE_COMMAND) build --workspace dune-workspace.dev src/haxe.exe
 	cp -f _build/default/src/haxe.exe ./${HAXE_OUTPUT}
 
+plugin: haxe
+	$(DUNE_COMMAND) build --workspace dune-workspace.dev plugins/$(PLUGIN)/$(PLUGIN).cmxs
+	mkdir -p plugins/$(PLUGIN)/cmxs/$(SYSTEM_NAME)
+	cp -f _build/default/plugins/$(PLUGIN)/$(PLUGIN).cmxs plugins/$(PLUGIN)/cmxs/$(SYSTEM_NAME)/plugin.cmxs
+
 kill_exe_win:
 ifdef SYSTEMROOT
 	-@taskkill /F /IM haxe.exe 2>/dev/null
@@ -143,7 +161,7 @@ $(INSTALLER_TMP_DIR):
 	mkdir -p $(INSTALLER_TMP_DIR)
 
 $(INSTALLER_TMP_DIR)/neko-osx64.tar.gz: $(INSTALLER_TMP_DIR)
-	wget -nv http://nekovm.org/media/neko-2.1.0-osx64.tar.gz -O installer/neko-osx64.tar.gz
+	wget -nv https://github.com/HaxeFoundation/neko/releases/download/v2-3-0/neko-2.3.0-osx64.tar.gz -O installer/neko-osx64.tar.gz
 
 # Installer
 

+ 1 - 1
Makefile.win

@@ -72,7 +72,7 @@ package_choco:
 	rm -rf out/choco
 
 $(INSTALLER_TMP_DIR)/neko-win.zip: $(INSTALLER_TMP_DIR)
-	wget -nv https://nekovm.org/media/neko-2.2.0-win$(NEKO_ARCH_STR).zip -O installer/neko-win.zip
+	wget -nv https://github.com/HaxeFoundation/neko/releases/download/v2-3-0/neko-2.3.0-win$(NEKO_ARCH_STR).zip -O installer/neko-win.zip
 
 package_installer_win: $(INSTALLER_TMP_DIR)/neko-win.zip package_win
 	$(eval OUTFILE := $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_installer.zip)

+ 16 - 3
azure-pipelines.yml

@@ -5,6 +5,14 @@ variables:
   - name: AZURE_PIPELINES_BRANCH
     value: $(Build.SourceBranchName)
 
+trigger:
+  branches:
+    include:
+      - '*'
+  tags:
+    include:
+      - '*'
+
 stages:
   - stage: StageTest
     jobs:
@@ -63,6 +71,7 @@ stages:
               TEST: python
             lua:
               TEST: lua
+              APT_PACKAGES: ncurses-dev
         steps:
           - checkout: self
             fetchDepth: 20
@@ -96,11 +105,15 @@ stages:
             condition: and(succeeded(), variables['APT_PACKAGES'])
             displayName: Install apt packages
           - script: haxe RunCi.hxml
+            condition: and(succeeded(), not(and(variables['SAUCE'], variables['SAUCE_ACCESS_KEY'])))
             workingDirectory: $(Build.SourcesDirectory)/tests
-            env:
-              ${{ if variables['SAUCE_ACCESS_KEY'] }}:
-                SAUCE_ACCESS_KEY: $(SAUCE_ACCESS_KEY)
             displayName: Test
+          - script: haxe RunCi.hxml
+            condition: and(succeeded(), variables['SAUCE'], variables['SAUCE_ACCESS_KEY'])
+            workingDirectory: $(Build.SourcesDirectory)/tests
+            env:
+              SAUCE_ACCESS_KEY: $(SAUCE_ACCESS_KEY)
+            displayName: Test (with SauceLabs)
 
       - job: TestMac
         dependsOn: BuildMac

+ 17 - 0
extra/CHANGES.txt

@@ -1,3 +1,20 @@
+2019-10-26: 4.0.0
+
+	General improvements:
+
+	js : updated externs for `Float32Array` and `Float64Array` (#8864)
+	php : added array access to `php.NativeStructArray` (#8893)
+
+	Bugfixes:
+
+	cs : fixed "This expression may be invalid" false warning (#8589)
+	php : fixed iterator fields on maps being removed (#8851)
+	php : fixed `-2147483648` as init value for static vars (#5289)
+	python : fixed modulo by a negative number (#8845)
+	java : fixed backslash escaping on `EReg.replace` (#3430)
+	lua : fixed `EReg.map` for unicode (#8861)
+	hl : fixed sqlite connection on OSX/Linux (#8878)
+
 2019-09-12: 4.0.0-rc.5
 
 	General improvements and optimizations:

+ 2 - 1
extra/azure-pipelines/build-linux.yml

@@ -8,7 +8,8 @@ jobs:
       vmImage: ${{ parameters.vmImage }}
     variables:
       OPAMYES: 1
-      ADD_REVISION: 1
+      ${{ if not(startsWith(variables['Build.SourceBranch'], 'refs/tags/')) }}:
+        ADD_REVISION: 1
     steps:
       - checkout: self
         submodules: recursive

+ 2 - 1
extra/azure-pipelines/build-mac.yml

@@ -8,7 +8,8 @@ jobs:
       vmImage: ${{ parameters.vmImage }}
     variables:
       OPAMYES: 1
-      ADD_REVISION: 1
+      ${{ if not(startsWith(variables['Build.SourceBranch'], 'refs/tags/')) }}:
+        ADD_REVISION: 1
     steps:
       - checkout: self
         submodules: recursive

+ 2 - 1
extra/azure-pipelines/build-windows.yml

@@ -9,7 +9,8 @@ jobs:
       vmImage: ${{ parameters.vmImage }}
     variables:
       OPAMYES: 1
-      ADD_REVISION: 1
+      ${{ if not(startsWith(variables['Build.SourceBranch'], 'refs/tags/')) }}:
+        ADD_REVISION: 1
       CYG_MIRROR: http://mirrors.kernel.org/sourceware/cygwin/
       ${{ if eq(parameters.arch, '64') }}:
         ARCH: 64

+ 1 - 0
extra/release-checklist.txt

@@ -3,6 +3,7 @@
 - Check that haxelib is working
 - Make sure to update the haxelib submodule
 - Check that the run-time haxelibs are ready for release: hxcpp, hxjava, hxcs
+- Check that the osx & windows installers has the latest neko release in "Makefile" and "Makefile.win" files
 
 # Making the release
 

+ 1 - 0
libs/ilib/dune

@@ -3,6 +3,7 @@
 (library
 	(name ilib)
 	(modules_without_implementation ilData ilMeta)
+	(modules (:standard \ dump))
 	(libraries extlib)
 	(wrapped false)
 )

+ 2 - 1
libs/ttflib/dune

@@ -2,6 +2,7 @@
 
 (library
 	(name ttflib)
-	(libraries extlib extlib_leftovers swflib)
+	(libraries extlib extlib_leftovers swflib unix)
+	(modules (:standard \ main))
 	(wrapped false)
 )

+ 2 - 0
plugins/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

+ 1 - 0
plugins/example/.gitignore

@@ -0,0 +1 @@
+.merlin

+ 24 - 0
plugins/example/README.md

@@ -0,0 +1,24 @@
+# How to build a plugin
+
+```
+$ make plugin PLUGIN=example
+```
+This command builds plugin for current OS only.
+
+# How to use your plugins in a Haxe project
+
+Setup your plugin as a haxe library:
+```
+$ haxelib dev example path/to/haxe/plugins/example
+```
+And then access it inside of a macro:
+```haxe
+macro static public function testPlugin() {
+	Example.plugin.hello();
+	return macro {}
+}
+```
+
+# How to start a new plugin
+
+Just make a copy of an "example" plugin directory and replace all occurrences of "example" word with your own plugin name.

+ 7 - 0
plugins/example/dune

@@ -0,0 +1,7 @@
+(data_only_dirs cmxs hx)
+(include_subdirs unqualified)
+
+(library
+	(name example)
+	(libraries haxe)
+)

+ 12 - 0
plugins/example/haxelib.json

@@ -0,0 +1,12 @@
+{
+	"name" : "example",
+	"url" : "http://haxe.org",
+	"license" : "MIT",
+	"description" : "Example Plugin",
+	"version" : "0.1.0",
+	"releasenote" : "Initial release",
+	"classPath": "hx",
+	"contributors" : ["example"],
+	"tags": ["plugin"],
+	"dependencies" : {}
+}

+ 32 - 0
plugins/example/hx/Example.macro.hx

@@ -0,0 +1,32 @@
+import haxe.PosInfos;
+
+using haxe.io.Path;
+
+typedef ExamplePluginApi = {
+	function hello():Void;
+	function stringifyPosition(p:haxe.macro.Expr.Position):String;
+	function hijackStaticTest():Void;
+}
+
+class Example {
+	/** Access plugin API */
+	static public var plugin(get,never):ExamplePluginApi;
+
+	static var _plugin:ExamplePluginApi;
+	static function get_plugin():ExamplePluginApi {
+		if(_plugin == null) {
+			try {
+				_plugin = eval.vm.Context.loadPlugin(getPluginPath());
+			} catch(e:Dynamic) {
+				throw 'Failed to load plugin: $e';
+			}
+		}
+		return _plugin;
+	}
+
+	static function getPluginPath():String {
+		var currentFile = (function(?p:PosInfos) return p.fileName)();
+		var srcDir = currentFile.directory().directory();
+		return Path.join([srcDir, 'cmxs', Sys.systemName(), 'plugin.cmxs']);
+	}
+}

+ 70 - 0
plugins/example/ml/example.ml

@@ -0,0 +1,70 @@
+open EvalValue
+open Type
+
+class plugin =
+	object (self)
+		(**
+			Prints greeting to stdout.
+			Takes no arguments, returns Void.
+		*)
+		method hello () : value =
+			print_endline "Hello from plugin";
+			(*
+				Plugin architecture requires to return something even for methods typed Void on Haxe side.
+				Return `null`
+			*)
+			vnull
+		(**
+			Takes `haxe.macro.Position` and returns a string of that position in the same format used for
+			compiler errors
+		*)
+		method stringify_position (pos:value) : value =
+			let pos = EvalDecode.decode_pos pos in
+			let str = Lexer.get_error_pos (Printf.sprintf "%s:%d:") pos in
+			EvalEncode.encode_string str
+		(**
+			Change all static methods named "test" to throw "Hello from plugin".
+			This is an example how to modify typed syntax tree.
+		*)
+		method hijack_static_test () : value =
+			let compiler = (EvalContext.get_ctx()).curapi in
+			(**
+				Add a callback like `haxe.macro.Context.onAfterTyping`
+			*)
+			compiler.after_typing (fun haxe_types ->
+				List.iter
+					(fun hx_type ->
+						match hx_type with
+							| TClassDecl cls ->
+								List.iter
+									(fun field ->
+										match field.cf_name, field.cf_expr with
+											| "test", Some e ->
+												let hello = {
+													eexpr = TConst (TString "Hello from plugin");
+													etype = (compiler.get_com()).basic.tstring;
+													epos = Globals.null_pos;
+												} in
+												field.cf_expr <- Some { e with eexpr = TThrow hello }
+											| _ -> ()
+									)
+									cls.cl_ordered_statics
+							| _ -> ()
+					)
+					haxe_types
+			);
+			vnull
+	end
+;;
+
+let api = new plugin in
+
+(**
+	Register our plugin API.
+	This code is executed upon `eval.vm.Context.loadPlugin` call.
+*)
+EvalStdLib.StdContext.register [
+	("hello", EvalEncode.vfun0 api#hello);
+	("stringifyPosition", EvalEncode.vfun1 api#stringify_position);
+	("hijackStaticTest", EvalEncode.vfun0 api#hijack_static_test);
+]

+ 11 - 0
src-json/define.json

@@ -163,6 +163,11 @@
 		"doc": "Record per-method execution times in macro/interp mode. Implies eval_stack.",
 		"platforms": ["eval"]
 	},
+	{
+		"name": "FilterTimes",
+		"define": "filter_times",
+		"doc": "Record per-filter execution times upon --times."
+	},
 	{
 		"name": "FastCast",
 		"define": "fast_cast",
@@ -550,6 +555,12 @@
 		"define": "static",
 		"doc": "Defined if the current target is static."
 	},
+	{
+		"name": "StdEncodingUtf8",
+		"define": "std-encoding-utf8",
+		"doc": "Force utf8 encoding for stdin, stdout and stderr",
+		"platforms": ["java", "cs", "python"]
+	},
 	{
 		"name": "Swc",
 		"define": "swc",

+ 13 - 2
src/codegen/gencommon/castDetect.ml

@@ -708,8 +708,19 @@ let handle_type_parameter gen e e1 ef ~clean_ef ~overloads_cast_to_base f elist
 				(* unify applied original *)
 			with
 				| Unify_error el ->
-						(* List.iter (fun el -> gen.gcon.warning (Typecore.unify_error_msg (print_context()) el) pos) el; *)
-						gen.gcon.warning ("This expression may be invalid") pos
+						(match el with
+						(*
+							Don't emit a warning for abstracts if underlying type is the same as the second type.
+							This situation is caused by `Normalize.filter_param` not "unpacking" abstracts.
+						*)
+						| [Cannot_unify (TAbstract(a,params), b)]
+						| [Cannot_unify (b, TAbstract(a,params))] ->
+							let a = apply_params a.a_params params a.a_this in
+							if not (shallow_eq a b) then
+								gen.gcon.warning ("This expression may be invalid") pos
+						| _ ->
+							gen.gcon.warning ("This expression may be invalid") pos
+						)
 				| Invalid_argument _ ->
 						gen.gcon.warning ("This expression may be invalid") pos
 			);

+ 3 - 5
src/codegen/gencommon/normalize.ml

@@ -29,7 +29,7 @@ open Gencommon
 		as it will help normalize the AST
 *)
 
-let rec filter_param stack t =
+let rec filter_param (stack:t list) t =
 	match t with
 	| TInst({ cl_kind = KTypeParameter _ } as c,_) when Meta.has Meta.EnumConstructorParam c.cl_meta ->
 		t_dynamic
@@ -39,10 +39,8 @@ let rec filter_param stack t =
 		| Some t -> filter_param stack t)
 	| TInst(_,[]) | TEnum(_,[]) | TAbstract(_,[]) ->
 		t
-	| TType(td,tl) when Meta.has Meta.Semantics td.t_meta || Meta.has Meta.Strict td.t_meta || List.memq t stack ->
-		TType(td,List.map (filter_param stack) tl)
 	| TType(td,tl) ->
-		filter_param (t :: stack) (apply_params td.t_params tl td.t_type)
+		TType(td,List.map (filter_param stack) tl)
 	| TInst(c,tl) ->
 		TInst(c,List.map (filter_param stack) tl)
 	| TEnum(e,tl) ->
@@ -51,7 +49,7 @@ let rec filter_param stack t =
 		TAbstract(a, List.map (filter_param stack) tl)
 	| TAbstract({a_path = [],"Null"} as a,[t]) ->
 		TAbstract(a,[filter_param stack t])
-	| TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) ->
+	| TAbstract(a,tl) when (Meta.has Meta.MultiType a.a_meta) ->
 		filter_param stack (Abstract.get_underlying_type a tl)
 	| TAbstract(a,tl) ->
 		TAbstract(a, List.map (filter_param stack) tl)

+ 3 - 1
src/compiler/haxe.ml

@@ -104,9 +104,10 @@ let expand_env ?(h=None) path  =
 	) path
 
 let add_libs com libs =
+	let global_repo = List.exists (fun a -> a = "--haxelib-global") com.args in
 	let call_haxelib() =
 		let t = Timer.timer ["haxelib"] in
-		let cmd = "haxelib path " ^ String.concat " " libs in
+		let cmd = "haxelib" ^ (if global_repo then " --global" else "") ^ " path " ^ String.concat " " libs in
 		let pin, pout, perr = Unix.open_process_full cmd (Unix.environment()) in
 		let lines = Std.input_list pin in
 		let err = Std.input_list perr in
@@ -939,6 +940,7 @@ try
 		("Compilation",["-C";"--cwd"],[], Arg.String (fun dir ->
 			assert false
 		),"<dir>","set current working directory");
+		("Compilation",["--haxelib-global"],[], Arg.Unit (fun () -> ()),"","pass --global argument to haxelib");
 	] in
 	let args_callback cl =
 		begin try

+ 2 - 22
src/compiler/server.ml

@@ -183,8 +183,6 @@ module ServerCompilationContext = struct
 		mutable compilation_mark : int;
 		(* A list of delays which are run after compilation *)
 		mutable delays : (unit -> unit) list;
-		(* A list of modules which were (perhaps temporarily) removed from the cache *)
-		mutable removed_modules : (context_cache * path * module_def) list;
 		(* True if it's an actual compilation, false if it's a display operation *)
 		mutable was_compilation : bool;
 	}
@@ -198,7 +196,6 @@ module ServerCompilationContext = struct
 		compilation_mark = 0;
 		mark_loop = 0;
 		delays = [];
-		removed_modules = [];
 		was_compilation = false;
 	}
 
@@ -210,9 +207,6 @@ module ServerCompilationContext = struct
 		sctx.delays <- [];
 		List.iter (fun f -> f()) fl
 
-	let is_removed_module sctx m =
-		List.exists (fun (_,_,m') -> m == m') sctx.removed_modules
-
 	let reset sctx =
 		Hashtbl.clear sctx.changed_directories;
 		sctx.was_compilation <- false
@@ -417,8 +411,6 @@ let add_modules sctx ctx m p =
 			| MCode, MMacro | MMacro, MCode ->
 				(* this was just a dependency to check : do not add to the context *)
 				PMap.iter (Hashtbl.replace com.resources) m.m_extra.m_binded_res;
-			| _ when is_removed_module sctx m ->
-				()
 			| _ ->
 				ServerMessage.reusing com tabs m;
 				m.m_extra.m_added <- sctx.compilation_step;
@@ -476,22 +468,11 @@ let type_module sctx (ctx:Typecore.typer) mpath p =
 (* Sets up the per-compilation context. *)
 let create sctx write params =
 	let cs = sctx.cs in
-	let recache_removed_modules () =
-		List.iter (fun (cc,k,m) ->
-			try
-				ignore(cc#find_module k);
-			with Not_found ->
-				cc#cache_module k m
-		) sctx.removed_modules;
-		sctx.removed_modules <- []
-	in
 	let maybe_cache_context com =
 		if com.display.dms_full_typing then begin
 			CommonCache.cache_context sctx.cs com;
 			ServerMessage.cached_modules com "" (List.length com.modules);
-			sctx.removed_modules <- [];
-		end else
-			recache_removed_modules ()
+		end
 	in
 	let ctx = create_context params in
 	ctx.flush <- (fun() ->
@@ -523,8 +504,7 @@ let create sctx write params =
 			let file = (DisplayPosition.display_position#get).pfile in
 			(* force parsing again : if the completion point have been changed *)
 			cs#remove_files file;
-			sctx.removed_modules <- cs#filter_modules file;
-			add_delay sctx recache_removed_modules;
+			cs#taint_modules file;
 		end;
 		try
 			if (Hashtbl.find sctx.class_paths sign) <> ctx.com.class_path then begin

+ 0 - 11
src/context/compilationServer.ml

@@ -164,17 +164,6 @@ class cache = object(self)
 			) cc#get_modules
 		) contexts
 
-	method filter_modules file =
-		let removed = DynArray.create () in
-		(* TODO: Using filter_map_inplace would be better, but we can't move to OCaml 4.03 yet *)
-		Hashtbl.iter (fun _ cc ->
-			Hashtbl.iter (fun k m ->
-				if m.m_extra.m_file = file then DynArray.add removed (cc,k,m);
-			) cc#get_modules
-		) contexts;
-		DynArray.iter (fun (cc,k,_) -> Hashtbl.remove cc#get_modules k) removed;
-		DynArray.to_list removed
-
 	(* haxelibs *)
 
 	method find_haxelib key =

+ 2 - 1
src/context/typecore.ml

@@ -383,8 +383,9 @@ let rec can_access ctx ?(in_overload=false) c cf stat =
 		true
 	else if not in_overload && ctx.com.config.pf_overload && Meta.has Meta.Overload cf.cf_meta then
 		true
+	else if c == ctx.curclass then
+		true
 	else
-	(* TODO: should we add a c == ctx.curclass short check here? *)
 	(* has metadata path *)
 	let rec make_path c f = match c.cl_kind with
 		| KAbstractImpl a -> fst a.a_path @ [snd a.a_path; f.cf_name]

+ 4 - 1
src/core/abstract.ml

@@ -1,5 +1,8 @@
 open Meta
-open Type
+open TType
+open TFunctions
+open TPrinting
+open TUnification
 open Error
 
 let build_abstract a = match a.a_impl with

+ 8 - 4
src/core/error.ml

@@ -1,5 +1,9 @@
 open Globals
-open Type
+open TType
+open TUnification
+open TFunctions
+open TPrinting
+open TOther
 
 type call_error =
 	| Not_enough_arguments of (string * bool * t) list
@@ -30,7 +34,7 @@ let short_type ctx t =
 	let tstr = s_type ctx t in
 	if String.length tstr > 150 then String.sub tstr 0 147 ^ "..." else tstr
 
-let unify_error_msg ctx = function
+let unify_error_msg ctx err = match err with
 	| Cannot_unify (t1,t2) ->
 		s_type ctx t1 ^ " should be " ^ s_type ctx t2
 	| Invalid_field_type s ->
@@ -85,8 +89,8 @@ module BetterErrors = struct
 
 	type access = {
 		acc_kind : access_kind;
-		mutable acc_expected : Type.t;
-		mutable acc_actual : Type.t;
+		mutable acc_expected : TType.t;
+		mutable acc_actual : TType.t;
 		mutable acc_messages : unify_error list;
 		mutable acc_next : access option;
 	}

+ 2 - 2
src/core/globals.ml

@@ -24,11 +24,11 @@ type platform =
 	| Hl
 	| Eval
 
-let version = 4000
+let version = 4100
 let version_major = version / 1000
 let version_minor = (version mod 1000) / 100
 let version_revision = (version mod 100)
-let version_pre = Some "rc.5"
+let version_pre = Some "rc.1"
 
 let macro_platform = ref Neko
 

+ 733 - 0
src/core/tFunctions.ml

@@ -0,0 +1,733 @@
+open Globals
+open Ast
+open TType
+
+let monomorph_create_ref : (unit -> tmono) ref = ref (fun _ -> assert false)
+let monomorph_bind_ref : (tmono -> t -> unit) ref = ref (fun _ _ -> assert false)
+
+(* Flags *)
+
+let has_flag flags flag =
+	flags land (1 lsl flag) > 0
+
+let set_flag flags flag =
+	flags lor (1 lsl flag)
+
+let unset_flag flags flag =
+	flags land (lnot (1 lsl flag))
+
+let int_of_class_field_flag (flag : flag_tclass_field) =
+	Obj.magic flag
+
+let add_class_field_flag cf (flag : flag_tclass_field) =
+	cf.cf_flags <- set_flag cf.cf_flags (int_of_class_field_flag flag)
+
+let remove_class_field_flag cf (flag : flag_tclass_field) =
+	cf.cf_flags <- unset_flag cf.cf_flags (int_of_class_field_flag flag)
+
+let has_class_field_flag cf (flag : flag_tclass_field) =
+	has_flag cf.cf_flags (int_of_class_field_flag flag)
+
+(* ======= General utility ======= *)
+
+let alloc_var =
+	let uid = ref 0 in
+	(fun kind n t p ->
+		incr uid;
+		{
+			v_kind = kind;
+			v_name = n;
+			v_type = t;
+			v_id = !uid;
+			v_capture = false;
+			v_final = (match kind with VUser TVOLocalFunction -> true | _ -> false);
+			v_extra = None;
+			v_meta = [];
+			v_pos = p
+		}
+	)
+
+let alloc_mid =
+	let mid = ref 0 in
+	(fun() -> incr mid; !mid)
+
+let mk e t p = { eexpr = e; etype = t; epos = p }
+
+let mk_block e =
+	match e.eexpr with
+	| TBlock _ -> e
+	| _ -> mk (TBlock [e]) e.etype e.epos
+
+let mk_cast e t p = mk (TCast(e,None)) t p
+
+let null t p = mk (TConst TNull) t p
+
+let mk_mono() = TMono (!monomorph_create_ref ())
+
+let rec t_dynamic = TDynamic t_dynamic
+
+let mk_anon fl = TAnon { a_fields = fl; a_status = ref Closed; }
+
+(* We use this for display purposes because otherwise we never see the Dynamic type that
+   is defined in StdTypes.hx. This is set each time a typer is created, but this is fine
+   because Dynamic is the same in all contexts. If this ever changes we'll have to review
+   how we handle this. *)
+let t_dynamic_def = ref t_dynamic
+
+let tfun pl r = TFun (List.map (fun t -> "",false,t) pl,r)
+
+let fun_args l = List.map (fun (a,c,t) -> a, c <> None, t) l
+
+let mk_class m path pos name_pos =
+	{
+		cl_path = path;
+		cl_module = m;
+		cl_pos = pos;
+		cl_name_pos = name_pos;
+		cl_doc = None;
+		cl_meta = [];
+		cl_private = false;
+		cl_kind = KNormal;
+		cl_extern = false;
+		cl_final = false;
+		cl_interface = false;
+		cl_params = [];
+		cl_using = [];
+		cl_super = None;
+		cl_implements = [];
+		cl_fields = PMap.empty;
+		cl_ordered_statics = [];
+		cl_ordered_fields = [];
+		cl_statics = PMap.empty;
+		cl_dynamic = None;
+		cl_array_access = None;
+		cl_constructor = None;
+		cl_init = None;
+		cl_overrides = [];
+		cl_build = (fun() -> Built);
+		cl_restore = (fun() -> ());
+		cl_descendants = [];
+	}
+
+let module_extra file sign time kind policy =
+	{
+		m_file = file;
+		m_sign = sign;
+		m_display = {
+			m_inline_calls = [];
+			m_type_hints = [];
+		};
+		m_dirty = None;
+		m_added = 0;
+		m_mark = 0;
+		m_time = time;
+		m_processed = 0;
+		m_deps = PMap.empty;
+		m_kind = kind;
+		m_binded_res = PMap.empty;
+		m_if_feature = [];
+		m_features = Hashtbl.create 0;
+		m_check_policy = policy;
+	}
+
+
+let mk_field name ?(public = true) t p name_pos = {
+	cf_name = name;
+	cf_type = t;
+	cf_pos = p;
+	cf_name_pos = name_pos;
+	cf_doc = None;
+	cf_meta = [];
+	cf_kind = Var { v_read = AccNormal; v_write = AccNormal };
+	cf_expr = None;
+	cf_expr_unoptimized = None;
+	cf_params = [];
+	cf_overloads = [];
+	cf_flags = if public then set_flag 0 (int_of_class_field_flag CfPublic) else 0;
+}
+
+let null_module = {
+		m_id = alloc_mid();
+		m_path = [] , "";
+		m_types = [];
+		m_extra = module_extra "" "" 0. MFake [];
+	}
+
+let null_class =
+	let c = mk_class null_module ([],"") null_pos null_pos in
+	c.cl_private <- true;
+	c
+
+let null_field = mk_field "" t_dynamic null_pos null_pos
+
+let null_abstract = {
+	a_path = ([],"");
+	a_module = null_module;
+	a_pos = null_pos;
+	a_name_pos = null_pos;
+	a_private = true;
+	a_doc = None;
+	a_meta = [];
+	a_params = [];
+	a_using = [];
+	a_ops = [];
+	a_unops = [];
+	a_impl = None;
+	a_this = t_dynamic;
+	a_from = [];
+	a_from_field = [];
+	a_to = [];
+	a_to_field = [];
+	a_array = [];
+	a_read = None;
+	a_write = None;
+}
+
+let add_dependency m mdep =
+	if m != null_module && m != mdep then m.m_extra.m_deps <- PMap.add mdep.m_id mdep m.m_extra.m_deps
+
+let arg_name (a,_) = a.v_name
+
+let t_infos t : tinfos =
+	match t with
+	| TClassDecl c -> Obj.magic c
+	| TEnumDecl e -> Obj.magic e
+	| TTypeDecl t -> Obj.magic t
+	| TAbstractDecl a -> Obj.magic a
+
+let t_path t = (t_infos t).mt_path
+
+let rec is_parent csup c =
+	if c == csup || List.exists (fun (i,_) -> is_parent csup i) c.cl_implements then
+		true
+	else match c.cl_super with
+		| None -> false
+		| Some (c,_) -> is_parent csup c
+
+let add_descendant c descendant =
+	c.cl_descendants <- descendant :: c.cl_descendants
+
+let lazy_type f =
+	match !f with
+	| LAvailable t -> t
+	| LProcessing f | LWait f -> f()
+
+let lazy_available t = LAvailable t
+let lazy_processing f = LProcessing f
+let lazy_wait f = LWait f
+
+let map loop t =
+	match t with
+	| TMono r ->
+		(match r.tm_type with
+		| None -> t
+		| Some t -> loop t) (* erase*)
+	| TEnum (_,[]) | TInst (_,[]) | TType (_,[]) ->
+		t
+	| TEnum (e,tl) ->
+		TEnum (e, List.map loop tl)
+	| TInst (c,tl) ->
+		TInst (c, List.map loop tl)
+	| TType (t2,tl) ->
+		TType (t2,List.map loop tl)
+	| TAbstract (a,tl) ->
+		TAbstract (a,List.map loop tl)
+	| TFun (tl,r) ->
+		TFun (List.map (fun (s,o,t) -> s, o, loop t) tl,loop r)
+	| TAnon a ->
+		let fields = PMap.map (fun f -> { f with cf_type = loop f.cf_type }) a.a_fields in
+		begin match !(a.a_status) with
+			| Opened ->
+				a.a_fields <- fields;
+				t
+			| _ ->
+				TAnon {
+					a_fields = fields;
+					a_status = a.a_status;
+				}
+		end
+	| TLazy f ->
+		let ft = lazy_type f in
+		let ft2 = loop ft in
+		if ft == ft2 then t else ft2
+	| TDynamic t2 ->
+		if t == t2 then	t else TDynamic (loop t2)
+
+let duplicate t =
+	let monos = ref [] in
+	let rec loop t =
+		match t with
+		| TMono { tm_type = None } ->
+			(try
+				List.assq t !monos
+			with Not_found ->
+				let m = mk_mono() in
+				monos := (t,m) :: !monos;
+				m)
+		| _ ->
+			map loop t
+	in
+	loop t
+
+exception ApplyParamsRecursion
+
+(* substitute parameters with other types *)
+let apply_params ?stack cparams params t =
+	match cparams with
+	| [] -> t
+	| _ ->
+	let rec loop l1 l2 =
+		match l1, l2 with
+		| [] , [] -> []
+		| (x,TLazy f) :: l1, _ -> loop ((x,lazy_type f) :: l1) l2
+		| (_,t1) :: l1 , t2 :: l2 -> (t1,t2) :: loop l1 l2
+		| _ -> assert false
+	in
+	let subst = loop cparams params in
+	let rec loop t =
+		try
+			List.assq t subst
+		with Not_found ->
+		match t with
+		| TMono r ->
+			(match r.tm_type with
+			| None -> t
+			| Some t -> loop t)
+		| TEnum (e,tl) ->
+			(match tl with
+			| [] -> t
+			| _ -> TEnum (e,List.map loop tl))
+		| TType (t2,tl) ->
+			(match tl with
+			| [] -> t
+			| _ ->
+				let new_applied_params = List.map loop tl in
+				(match stack with
+				| None -> ()
+				| Some stack ->
+					List.iter (fun (subject, old_applied_params) ->
+						(*
+							E.g.:
+							```
+							typedef Rec<T> = { function method():Rec<Array<T>> }
+							```
+							We need to make sure that we are not applying the result of previous
+							application to the same place, which would mean the result of current
+							application would go into `apply_params` again and then again and so on.
+
+							Argument `stack` holds all previous results of `apply_params` to typedefs in current
+							unification process.
+
+							Imagine we are trying to unify `Rec<Int>` with something.
+
+							Once `apply_params Array<T> Int Rec<Array<T>>` is called for the first time the result
+							will be `Rec< Array<Int> >`. Store `Array<Int>` into `stack`
+
+							Then the next params application looks like this:
+								`apply_params Array<T> Array<Int> Rec<Array<T>>`
+							Notice the second argument is actually the result of a previous `apply_params` call.
+							And the result of the current call is `Rec< Array<Array<Int>> >`.
+
+							The third call would be:
+								`apply_params Array<T> Array<Array<Int>> Rec<Array<T>>`
+							and so on.
+
+							To stop infinite params application we need to check that we are trying to apply params
+							produced by the previous `apply_params Array<Int> _ Rec<Array<T>>` to the same `Rec<Array<T>>`
+						*)
+						if
+							subject == t (* Check the place that we're applying to is the same `Rec<Array<T>>` *)
+							&& old_applied_params == params (* Check that params we're applying are the same params
+																produced by the previous call to
+																`apply_params Array<T> _ Rec<Array<T>>` *)
+						then
+							raise ApplyParamsRecursion
+					) !stack;
+					stack := (t, new_applied_params) :: !stack;
+				);
+				TType (t2,new_applied_params))
+		| TAbstract (a,tl) ->
+			(match tl with
+			| [] -> t
+			| _ -> TAbstract (a,List.map loop tl))
+		| TInst (c,tl) ->
+			(match tl with
+			| [] ->
+				t
+			| [TMono r] ->
+				(match r.tm_type with
+				| Some tt when t == tt ->
+					(* for dynamic *)
+					let pt = mk_mono() in
+					let t = TInst (c,[pt]) in
+					(match pt with TMono r -> !monomorph_bind_ref r t | _ -> assert false);
+					t
+				| _ -> TInst (c,List.map loop tl))
+			| _ ->
+				TInst (c,List.map loop tl))
+		| TFun (tl,r) ->
+			TFun (List.map (fun (s,o,t) -> s, o, loop t) tl,loop r)
+		| TAnon a ->
+			let fields = PMap.map (fun f -> { f with cf_type = loop f.cf_type }) a.a_fields in
+			begin match !(a.a_status) with
+				| Opened ->
+					a.a_fields <- fields;
+					t
+				| _ ->
+					TAnon {
+						a_fields = fields;
+						a_status = a.a_status;
+					}
+			end
+		| TLazy f ->
+			let ft = lazy_type f in
+			let ft2 = loop ft in
+			if ft == ft2 then
+				t
+			else
+				ft2
+		| TDynamic t2 ->
+			if t == t2 then
+				t
+			else
+				TDynamic (loop t2)
+	in
+	loop t
+
+let monomorphs eparams t =
+	apply_params eparams (List.map (fun _ -> mk_mono()) eparams) t
+
+let apply_params_stack = ref []
+
+let try_apply_params_rec cparams params t success =
+	let old_stack = !apply_params_stack in
+	try
+		let result = success (apply_params ~stack:apply_params_stack cparams params t) in
+		apply_params_stack := old_stack;
+		result
+	with
+		| ApplyParamsRecursion ->
+			apply_params_stack := old_stack;
+		| err ->
+			apply_params_stack := old_stack;
+			raise err
+
+let rec follow t =
+	match t with
+	| TMono r ->
+		(match r.tm_type with
+		| Some t -> follow t
+		| _ -> t)
+	| TLazy f ->
+		follow (lazy_type f)
+	| TType (t,tl) ->
+		follow (apply_params t.t_params tl t.t_type)
+	| TAbstract({a_path = [],"Null"},[t]) ->
+		follow t
+	| _ -> t
+
+let follow_once t =
+	match t with
+	| TMono r ->
+		(match r.tm_type with
+		| None -> t
+		| Some t -> t)
+	| TAbstract _ | TEnum _ | TInst _ | TFun _ | TAnon _ | TDynamic _ ->
+		t
+	| TType (t,tl) ->
+		apply_params t.t_params tl t.t_type
+	| TLazy f ->
+		lazy_type f
+
+let rec follow_without_null t =
+	match t with
+	| TMono r ->
+		(match r.tm_type with
+		| Some t -> follow_without_null t
+		| _ -> t)
+	| TLazy f ->
+		follow_without_null (lazy_type f)
+	| TType (t,tl) ->
+		follow_without_null (apply_params t.t_params tl t.t_type)
+	| _ -> t
+
+(** Assumes `follow` has already been applied *)
+let rec ambiguate_funs t =
+	match t with
+	| TFun _ -> TFun ([], t_dynamic)
+	| TMono r ->
+		(match r.tm_type with
+		| Some _ -> assert false
+		| _ -> t)
+	| TInst (a, pl) ->
+	    TInst (a, List.map ambiguate_funs pl)
+	| TEnum (a, pl) ->
+	    TEnum (a, List.map ambiguate_funs pl)
+	| TAbstract (a, pl) ->
+	    TAbstract (a, List.map ambiguate_funs pl)
+	| TType (a, pl) ->
+	    TType (a, List.map ambiguate_funs pl)
+	| TDynamic _ -> t
+	| TAnon a ->
+	    TAnon { a with a_fields =
+		    PMap.map (fun af -> { af with cf_type =
+				ambiguate_funs af.cf_type }) a.a_fields }
+	| TLazy _ -> assert false
+
+let rec is_nullable = function
+	| TMono r ->
+		(match r.tm_type with None -> false | Some t -> is_nullable t)
+	| TAbstract ({ a_path = ([],"Null") },[_]) ->
+		true
+	| TLazy f ->
+		is_nullable (lazy_type f)
+	| TType (t,tl) ->
+		is_nullable (apply_params t.t_params tl t.t_type)
+	| TFun _ ->
+		false
+(*
+	Type parameters will most of the time be nullable objects, so we don't want to make it hard for users
+	to have to specify Null<T> all over the place, so while they could be a basic type, let's assume they will not.
+
+	This will still cause issues with inlining and haxe.rtti.Generic. In that case proper explicit Null<T> is required to
+	work correctly with basic types. This could still be fixed by redoing a nullability inference on the typed AST.
+
+	| TInst ({ cl_kind = KTypeParameter },_) -> false
+*)
+	| TAbstract (a,_) when Meta.has Meta.CoreType a.a_meta ->
+		not (Meta.has Meta.NotNull a.a_meta)
+	| TAbstract (a,tl) ->
+		not (Meta.has Meta.NotNull a.a_meta) && is_nullable (apply_params a.a_params tl a.a_this)
+	| _ ->
+		true
+
+let rec is_null ?(no_lazy=false) = function
+	| TMono r ->
+		(match r.tm_type with None -> false | Some t -> is_null t)
+	| TAbstract ({ a_path = ([],"Null") },[t]) ->
+		not (is_nullable (follow t))
+	| TLazy f ->
+		if no_lazy then raise Exit else is_null (lazy_type f)
+	| TType (t,tl) ->
+		is_null (apply_params t.t_params tl t.t_type)
+	| _ ->
+		false
+
+(* Determines if we have a Null<T>. Unlike is_null, this returns true even if the wrapped type is nullable itself. *)
+let rec is_explicit_null = function
+	| TMono r ->
+		(match r.tm_type with None -> false | Some t -> is_explicit_null t)
+	| TAbstract ({ a_path = ([],"Null") },[t]) ->
+		true
+	| TLazy f ->
+		is_explicit_null (lazy_type f)
+	| TType (t,tl) ->
+		is_explicit_null (apply_params t.t_params tl t.t_type)
+	| _ ->
+		false
+
+let rec has_mono t = match t with
+	| TMono r ->
+		(match r.tm_type with None -> true | Some t -> has_mono t)
+	| TInst(_,pl) | TEnum(_,pl) | TAbstract(_,pl) | TType(_,pl) ->
+		List.exists has_mono pl
+	| TDynamic _ ->
+		false
+	| TFun(args,r) ->
+		has_mono r || List.exists (fun (_,_,t) -> has_mono t) args
+	| TAnon a ->
+		PMap.fold (fun cf b -> has_mono cf.cf_type || b) a.a_fields false
+	| TLazy f ->
+		has_mono (lazy_type f)
+
+let concat e1 e2 =
+	let e = (match e1.eexpr, e2.eexpr with
+		| TBlock el1, TBlock el2 -> TBlock (el1@el2)
+		| TBlock el, _ -> TBlock (el @ [e2])
+		| _, TBlock el -> TBlock (e1 :: el)
+		| _ , _ -> TBlock [e1;e2]
+	) in
+	mk e e2.etype (punion e1.epos e2.epos)
+
+let is_closed a = !(a.a_status) <> Opened
+
+let type_of_module_type = function
+	| TClassDecl c -> TInst (c,List.map snd c.cl_params)
+	| TEnumDecl e -> TEnum (e,List.map snd e.e_params)
+	| TTypeDecl t -> TType (t,List.map snd t.t_params)
+	| TAbstractDecl a -> TAbstract (a,List.map snd a.a_params)
+
+let rec module_type_of_type = function
+	| TInst(c,_) -> TClassDecl c
+	| TEnum(en,_) -> TEnumDecl en
+	| TType(t,_) -> TTypeDecl t
+	| TAbstract(a,_) -> TAbstractDecl a
+	| TLazy f -> module_type_of_type (lazy_type f)
+	| TMono r ->
+		(match r.tm_type with
+		| Some t -> module_type_of_type t
+		| _ -> raise Exit)
+	| _ ->
+		raise Exit
+
+let tconst_to_const = function
+	| TInt i -> Int (Int32.to_string i)
+	| TFloat s -> Float s
+	| TString s -> String(s,SDoubleQuotes)
+	| TBool b -> Ident (if b then "true" else "false")
+	| TNull -> Ident "null"
+	| TThis -> Ident "this"
+	| TSuper -> Ident "super"
+
+let has_ctor_constraint c = match c.cl_kind with
+	| KTypeParameter tl ->
+		List.exists (fun t -> match follow t with
+			| TAnon a when PMap.mem "new" a.a_fields -> true
+			| TAbstract({a_path=["haxe"],"Constructible"},_) -> true
+			| _ -> false
+		) tl;
+	| _ -> false
+
+(* ======= Field utility ======= *)
+
+let field_name f =
+	match f with
+	| FAnon f | FInstance (_,_,f) | FStatic (_,f) | FClosure (_,f) -> f.cf_name
+	| FEnum (_,f) -> f.ef_name
+	| FDynamic n -> n
+
+let extract_field = function
+	| FAnon f | FInstance (_,_,f) | FStatic (_,f) | FClosure (_,f) -> Some f
+	| _ -> None
+
+let is_physical_var_field f =
+	match f.cf_kind with
+	| Var { v_read = AccNormal | AccInline | AccNo } | Var { v_write = AccNormal | AccNo } -> true
+	| Var _ -> Meta.has Meta.IsVar f.cf_meta
+	| _ -> false
+
+let is_physical_field f =
+	match f.cf_kind with
+	| Method _ -> true
+	| _ -> is_physical_var_field f
+
+let field_type f =
+	match f.cf_params with
+	| [] -> f.cf_type
+	| l -> monomorphs l f.cf_type
+
+let rec raw_class_field build_type c tl i =
+	let apply = apply_params c.cl_params tl in
+	try
+		let f = PMap.find i c.cl_fields in
+		Some (c,tl), build_type f , f
+	with Not_found -> try (match c.cl_constructor with
+		| Some ctor when i = "new" -> Some (c,tl), build_type ctor,ctor
+		| _ -> raise Not_found)
+	with Not_found -> try
+		match c.cl_super with
+		| None ->
+			raise Not_found
+		| Some (c,tl) ->
+			let c2 , t , f = raw_class_field build_type c (List.map apply tl) i in
+			c2, apply_params c.cl_params tl t , f
+	with Not_found ->
+		match c.cl_kind with
+		| KTypeParameter tl ->
+			let rec loop = function
+				| [] ->
+					raise Not_found
+				| t :: ctl ->
+					match follow t with
+					| TAnon a ->
+						(try
+							let f = PMap.find i a.a_fields in
+							None, build_type f, f
+						with
+							Not_found -> loop ctl)
+					| TInst (c,tl) ->
+						(try
+							let c2, t , f = raw_class_field build_type c (List.map apply tl) i in
+							c2, apply_params c.cl_params tl t, f
+						with
+							Not_found -> loop ctl)
+					| _ ->
+						loop ctl
+			in
+			loop tl
+		| _ ->
+			if not c.cl_interface then raise Not_found;
+			(*
+				an interface can implements other interfaces without
+				having to redeclare its fields
+			*)
+			let rec loop = function
+				| [] ->
+					raise Not_found
+				| (c,tl) :: l ->
+					try
+						let c2, t , f = raw_class_field build_type c (List.map apply tl) i in
+						c2, apply_params c.cl_params tl t, f
+					with
+						Not_found -> loop l
+			in
+			loop c.cl_implements
+
+let class_field = raw_class_field field_type
+
+let quick_field t n =
+	match follow t with
+	| TInst (c,tl) ->
+		let c, _, f = raw_class_field (fun f -> f.cf_type) c tl n in
+		(match c with None -> FAnon f | Some (c,tl) -> FInstance (c,tl,f))
+	| TAnon a ->
+		(match !(a.a_status) with
+		| EnumStatics e ->
+			let ef = PMap.find n e.e_constrs in
+			FEnum(e,ef)
+		| Statics c ->
+			FStatic (c,PMap.find n c.cl_statics)
+		| AbstractStatics a ->
+			begin match a.a_impl with
+				| Some c ->
+					let cf = PMap.find n c.cl_statics in
+					FStatic(c,cf) (* is that right? *)
+				| _ ->
+					raise Not_found
+			end
+		| _ ->
+			FAnon (PMap.find n a.a_fields))
+	| TDynamic _ ->
+		FDynamic n
+	| TEnum _  | TMono _ | TAbstract _ | TFun _ ->
+		raise Not_found
+	| TLazy _ | TType _ ->
+		assert false
+
+let quick_field_dynamic t s =
+	try quick_field t s
+	with Not_found -> FDynamic s
+
+let rec get_constructor build_type c =
+	match c.cl_constructor, c.cl_super with
+	| Some c, _ -> build_type c, c
+	| None, None -> raise Not_found
+	| None, Some (csup,cparams) ->
+		let t, c = get_constructor build_type csup in
+		apply_params csup.cl_params cparams t, c
+
+let has_constructor c =
+	try
+		ignore(get_constructor (fun cf -> cf.cf_type) c);
+		true
+	with Not_found -> false
+
+let resolve_typedef t =
+	match t with
+	| TClassDecl _ | TEnumDecl _ | TAbstractDecl _ -> t
+	| TTypeDecl td ->
+		match follow td.t_type with
+		| TEnum (e,_) -> TEnumDecl e
+		| TInst (c,_) -> TClassDecl c
+		| TAbstract (a,_) -> TAbstractDecl a
+		| _ -> t

+ 322 - 0
src/core/tOther.ml

@@ -0,0 +1,322 @@
+open Globals
+open Ast
+open TType
+open TFunctions
+open TPrinting
+
+module TExprToExpr = struct
+	let tpath p mp pl =
+		if snd mp = snd p then
+			CTPath {
+				tpackage = fst p;
+				tname = snd p;
+				tparams = pl;
+				tsub = None;
+			}
+		else CTPath {
+				tpackage = fst mp;
+				tname = snd mp;
+				tparams = pl;
+				tsub = Some (snd p);
+			}
+
+	let rec convert_type = function
+		| TMono r ->
+			(match r.tm_type with
+			| None -> raise Exit
+			| Some t -> convert_type t)
+		| TInst ({cl_private = true; cl_path=_,name},tl)
+		| TEnum ({e_private = true; e_path=_,name},tl)
+		| TType ({t_private = true; t_path=_,name},tl)
+		| TAbstract ({a_private = true; a_path=_,name},tl) ->
+			CTPath {
+				tpackage = [];
+				tname = name;
+				tparams = List.map tparam tl;
+				tsub = None;
+			}
+		| TEnum (e,pl) ->
+			tpath e.e_path e.e_module.m_path (List.map tparam pl)
+		| TInst({cl_kind = KExpr e} as c,pl) ->
+			tpath ([],snd c.cl_path) ([],snd c.cl_path) (List.map tparam pl)
+		| TInst({cl_kind = KTypeParameter _} as c,pl) ->
+			tpath ([],snd c.cl_path) ([],snd c.cl_path) (List.map tparam pl)
+		| TInst (c,pl) ->
+			tpath c.cl_path c.cl_module.m_path (List.map tparam pl)
+		| TType (t,pl) as tf ->
+			(* recurse on type-type *)
+			if (snd t.t_path).[0] = '#' then convert_type (follow tf) else tpath t.t_path t.t_module.m_path (List.map tparam pl)
+		| TAbstract (a,pl) ->
+			tpath a.a_path a.a_module.m_path (List.map tparam pl)
+		| TFun (args,ret) ->
+			CTFunction (List.map (fun (_,_,t) -> convert_type' t) args, (convert_type' ret))
+		| TAnon a ->
+			begin match !(a.a_status) with
+			| Statics c -> tpath ([],"Class") ([],"Class") [TPType (tpath c.cl_path c.cl_path [],null_pos)]
+			| EnumStatics e -> tpath ([],"Enum") ([],"Enum") [TPType (tpath e.e_path e.e_path [],null_pos)]
+			| _ ->
+				CTAnonymous (PMap.foldi (fun _ f acc ->
+					{
+						cff_name = f.cf_name,null_pos;
+						cff_kind = FVar (mk_type_hint f.cf_type null_pos,None);
+						cff_pos = f.cf_pos;
+						cff_doc = f.cf_doc;
+						cff_meta = f.cf_meta;
+						cff_access = [];
+					} :: acc
+				) a.a_fields [])
+			end
+		| (TDynamic t2) as t ->
+			tpath ([],"Dynamic") ([],"Dynamic") (if t == t_dynamic then [] else [tparam t2])
+		| TLazy f ->
+			convert_type (lazy_type f)
+
+	and convert_type' t =
+		convert_type t,null_pos
+
+	and tparam = function
+		| TInst ({cl_kind = KExpr e}, _) -> TPExpr e
+		| t -> TPType (convert_type' t)
+
+	and mk_type_hint t p =
+		match follow t with
+		| TMono _ -> None
+		| _ -> (try Some (convert_type t,p) with Exit -> None)
+
+	let rec convert_expr e =
+		let full_type_path t =
+			let mp,p = match t with
+			| TClassDecl c -> c.cl_module.m_path,c.cl_path
+			| TEnumDecl en -> en.e_module.m_path,en.e_path
+			| TAbstractDecl a -> a.a_module.m_path,a.a_path
+			| TTypeDecl t -> t.t_module.m_path,t.t_path
+			in
+			if snd mp = snd p then p else (fst mp) @ [snd mp],snd p
+		in
+		let mk_path = expr_of_type_path in
+		let mk_ident = function
+			| "`trace" -> Ident "trace"
+			| n -> Ident n
+		in
+		let eopt = function None -> None | Some e -> Some (convert_expr e) in
+		((match e.eexpr with
+		| TConst c ->
+			EConst (tconst_to_const c)
+		| TLocal v -> EConst (mk_ident v.v_name)
+		| TArray (e1,e2) -> EArray (convert_expr e1,convert_expr e2)
+		| TBinop (op,e1,e2) -> EBinop (op, convert_expr e1, convert_expr e2)
+		| TField (e,f) -> EField (convert_expr e, field_name f)
+		| TTypeExpr t -> fst (mk_path (full_type_path t) e.epos)
+		| TParenthesis e -> EParenthesis (convert_expr e)
+		| TObjectDecl fl -> EObjectDecl (List.map (fun (k,e) -> k, convert_expr e) fl)
+		| TArrayDecl el -> EArrayDecl (List.map convert_expr el)
+		| TCall (e,el) -> ECall (convert_expr e,List.map convert_expr el)
+		| TNew (c,pl,el) -> ENew ((match (try convert_type (TInst (c,pl)) with Exit -> convert_type (TInst (c,[]))) with CTPath p -> p,null_pos | _ -> assert false),List.map convert_expr el)
+		| TUnop (op,p,e) -> EUnop (op,p,convert_expr e)
+		| TFunction f ->
+			let arg (v,c) = (v.v_name,v.v_pos), false, v.v_meta, mk_type_hint v.v_type null_pos, (match c with None -> None | Some c -> Some (convert_expr c)) in
+			EFunction (FKAnonymous,{ f_params = []; f_args = List.map arg f.tf_args; f_type = mk_type_hint f.tf_type null_pos; f_expr = Some (convert_expr f.tf_expr) })
+		| TVar (v,eo) ->
+			EVars ([(v.v_name,v.v_pos), v.v_final, mk_type_hint v.v_type v.v_pos, eopt eo])
+		| TBlock el -> EBlock (List.map convert_expr el)
+		| TFor (v,it,e) ->
+			let ein = (EBinop (OpIn,(EConst (Ident v.v_name),it.epos),convert_expr it),it.epos) in
+			EFor (ein,convert_expr e)
+		| TIf (e,e1,e2) -> EIf (convert_expr e,convert_expr e1,eopt e2)
+		| TWhile (e1,e2,flag) -> EWhile (convert_expr e1, convert_expr e2, flag)
+		| TSwitch (e,cases,def) ->
+			let cases = List.map (fun (vl,e) ->
+				List.map convert_expr vl,None,(match e.eexpr with TBlock [] -> None | _ -> Some (convert_expr e)),e.epos
+			) cases in
+			let def = match eopt def with None -> None | Some (EBlock [],_) -> Some (None,null_pos) | Some e -> Some (Some e,pos e) in
+			ESwitch (convert_expr e,cases,def)
+		| TEnumIndex _
+		| TEnumParameter _ ->
+			(* these are considered complex, so the AST is handled in TMeta(Meta.Ast) *)
+			assert false
+		| TTry (e,catches) ->
+			let e1 = convert_expr e in
+			let catches = List.map (fun (v,e) ->
+				let ct = try convert_type v.v_type,null_pos with Exit -> assert false in
+				let e = convert_expr e in
+				(v.v_name,v.v_pos),ct,e,(pos e)
+			) catches in
+			ETry (e1,catches)
+		| TReturn e -> EReturn (eopt e)
+		| TBreak -> EBreak
+		| TContinue -> EContinue
+		| TThrow e -> EThrow (convert_expr e)
+		| TCast (e,t) ->
+			let t = (match t with
+				| None -> None
+				| Some t ->
+					let t = (match t with TClassDecl c -> TInst (c,[]) | TEnumDecl e -> TEnum (e,[]) | TTypeDecl t -> TType (t,[]) | TAbstractDecl a -> TAbstract (a,[])) in
+					Some (try convert_type t,null_pos with Exit -> assert false)
+			) in
+			ECast (convert_expr e,t)
+		| TMeta ((Meta.Ast,[e1,_],_),_) -> e1
+		| TMeta (m,e) -> EMeta(m,convert_expr e)
+		| TIdent s -> EConst (Ident s))
+		,e.epos)
+
+end
+
+module ExtType = struct
+	let is_mono = function
+		| TMono { tm_type = None } -> true
+		| _ -> false
+
+	let is_void = function
+		| TAbstract({a_path=[],"Void"},_) -> true
+		| _ -> false
+
+	let is_int t = match t with
+		| TAbstract({a_path=[],"Int"},_) -> true
+		| _ -> false
+
+	let is_float t = match t with
+		| TAbstract({a_path=[],"Float"},_) -> true
+		| _ -> false
+
+	let is_numeric t = match t with
+		| TAbstract({a_path=[],"Float"},_) -> true
+		| TAbstract({a_path=[],"Int"},_) -> true
+		| _ -> false
+
+	let is_string t = match t with
+		| TInst({cl_path=[],"String"},_) -> true
+		| _ -> false
+
+	let is_bool t = match t with
+		| TAbstract({a_path=[],"Bool"},_) -> true
+		| _ -> false
+
+	type semantics =
+		| VariableSemantics
+		| ReferenceSemantics
+		| ValueSemantics
+
+	let semantics_name = function
+		| VariableSemantics -> "variable"
+		| ReferenceSemantics -> "reference"
+		| ValueSemantics -> "value"
+
+	let has_semantics t sem =
+		let name = semantics_name sem in
+		let check meta =
+			has_meta_option meta Meta.Semantics name
+		in
+		let rec loop t = match t with
+			| TInst(c,_) -> check c.cl_meta
+			| TEnum(en,_) -> check en.e_meta
+			| TType(t,tl) -> check t.t_meta || (loop (apply_params t.t_params tl t.t_type))
+			| TAbstract(a,_) -> check a.a_meta
+			| TLazy f -> loop (lazy_type f)
+			| TMono r ->
+				(match r.tm_type with
+				| Some t -> loop t
+				| _ -> false)
+			| _ ->
+				false
+		in
+		loop t
+
+	let has_variable_semantics t = has_semantics t VariableSemantics
+	let has_reference_semantics t = has_semantics t ReferenceSemantics
+	let has_value_semantics t = has_semantics t ValueSemantics
+end
+
+let no_meta = []
+
+let class_module_type c = {
+	t_path = [],"Class<" ^ (s_type_path c.cl_path) ^ ">" ;
+	t_module = c.cl_module;
+	t_doc = None;
+	t_pos = c.cl_pos;
+	t_name_pos = null_pos;
+	t_type = TAnon {
+		a_fields = c.cl_statics;
+		a_status = ref (Statics c);
+	};
+	t_private = true;
+	t_params = [];
+	t_using = [];
+	t_meta = no_meta;
+}
+
+let enum_module_type m path p  = {
+	t_path = [], "Enum<" ^ (s_type_path path) ^ ">";
+	t_module = m;
+	t_doc = None;
+	t_pos = p;
+	t_name_pos = null_pos;
+	t_type = mk_mono();
+	t_private = true;
+	t_params = [];
+	t_using = [];
+	t_meta = [];
+}
+
+let abstract_module_type a tl = {
+	t_path = [],Printf.sprintf "Abstract<%s%s>" (s_type_path a.a_path) (s_type_params (ref []) tl);
+	t_module = a.a_module;
+	t_doc = None;
+	t_pos = a.a_pos;
+	t_name_pos = null_pos;
+	t_type = TAnon {
+		a_fields = PMap.empty;
+		a_status = ref (AbstractStatics a);
+	};
+	t_private = true;
+	t_params = [];
+	t_using = [];
+	t_meta = no_meta;
+}
+
+module TClass = struct
+	let get_member_fields' self_too c0 tl =
+		let rec loop acc c tl =
+			let apply = apply_params c.cl_params tl in
+			let maybe_add acc cf =
+				if not (PMap.mem cf.cf_name acc) then begin
+					let cf = if tl = [] then cf else {cf with cf_type = apply cf.cf_type} in
+					PMap.add cf.cf_name (c,cf) acc
+				end else acc
+			in
+			let acc = if self_too || c != c0 then List.fold_left maybe_add acc c.cl_ordered_fields else acc in
+			if c.cl_interface then
+				List.fold_left (fun acc (i,tl) -> loop acc i (List.map apply tl)) acc c.cl_implements
+			else
+				match c.cl_super with
+				| Some(c,tl) -> loop acc c (List.map apply tl)
+				| None -> acc
+		in
+		loop PMap.empty c0 tl
+
+	let get_all_super_fields c =
+		get_member_fields' false c (List.map snd c.cl_params)
+
+	let get_all_fields c tl =
+		get_member_fields' true c tl
+
+	let get_overridden_fields c cf =
+		let rec loop acc c = match c.cl_super with
+			| None ->
+				acc
+			| Some(c,_) ->
+				begin try
+					let cf' = PMap.find cf.cf_name c.cl_fields in
+					loop (cf' :: acc) c
+				with Not_found ->
+					loop acc c
+				end
+		in
+		loop [] c
+end
+
+let s_class_path c =
+	let path = match c.cl_kind with
+		| KAbstractImpl a -> a.a_path
+		| _ -> c.cl_path
+	in
+	s_type_path path

+ 656 - 0
src/core/tPrinting.ml

@@ -0,0 +1,656 @@
+open Globals
+open Ast
+open TType
+open TFunctions
+
+let is_simn = false
+
+let print_context() = ref []
+
+let rec s_type_kind t =
+	let map tl = String.concat ", " (List.map s_type_kind tl) in
+	match t with
+	| TMono r ->
+		begin match r.tm_type with
+			| None -> Printf.sprintf "TMono (None)"
+			| Some t -> "TMono (Some (" ^ (s_type_kind t) ^ "))"
+		end
+	| TEnum(en,tl) -> Printf.sprintf "TEnum(%s, [%s])" (s_type_path en.e_path) (map tl)
+	| TInst(c,tl) -> Printf.sprintf "TInst(%s, [%s])" (s_type_path c.cl_path) (map tl)
+	| TType(t,tl) -> Printf.sprintf "TType(%s, [%s])" (s_type_path t.t_path) (map tl)
+	| TAbstract(a,tl) -> Printf.sprintf "TAbstract(%s, [%s])" (s_type_path a.a_path) (map tl)
+	| TFun(tl,r) -> Printf.sprintf "TFun([%s], %s)" (String.concat ", " (List.map (fun (n,b,t) -> Printf.sprintf "%s%s:%s" (if b then "?" else "") n (s_type_kind t)) tl)) (s_type_kind r)
+	| TAnon an -> "TAnon"
+	| TDynamic t2 -> "TDynamic"
+	| TLazy _ -> "TLazy"
+
+let s_module_type_kind = function
+	| TClassDecl c -> "TClassDecl(" ^ (s_type_path c.cl_path) ^ ")"
+	| TEnumDecl en -> "TEnumDecl(" ^ (s_type_path en.e_path) ^ ")"
+	| TAbstractDecl a -> "TAbstractDecl(" ^ (s_type_path a.a_path) ^ ")"
+	| TTypeDecl t -> "TTypeDecl(" ^ (s_type_path t.t_path) ^ ")"
+
+let rec s_type ctx t =
+	match t with
+	| TMono r ->
+		(match r.tm_type with
+		| None ->
+			begin try
+				let id = List.assq t (!ctx) in
+				Printf.sprintf "Unknown<%d>" id
+			with Not_found ->
+				let id = List.length !ctx in
+				ctx := (t,id) :: !ctx;
+				begin match r.tm_constraint with
+				| Some (cstr,_,_) when is_simn ->
+					let s_constraints = match cstr with
+						| CStructure(t,_) -> s_type ctx t
+						| CTypes tl -> String.concat ", " (List.map (s_type ctx) tl)
+					in
+					Printf.sprintf "(Unknown<%d> : %s)" id s_constraints
+				| _ ->
+					Printf.sprintf "Unknown<%d>" id
+				end
+			end
+		| Some t -> s_type ctx t)
+	| TEnum (e,tl) ->
+		s_type_path e.e_path ^ s_type_params ctx tl
+	| TInst (c,tl) ->
+		(match c.cl_kind with
+		| KExpr e -> Ast.Printer.s_expr e
+		| _ -> s_type_path c.cl_path ^ s_type_params ctx tl)
+	| TType ({ t_type = TAnon { a_status = { contents = Statics { cl_kind = KAbstractImpl a }}}}, _) ->
+		"Abstract<" ^ (s_type_path a.a_path) ^ ">"
+	| TType (t,tl) ->
+		s_type_path t.t_path ^ s_type_params ctx tl
+	| TAbstract (a,tl) ->
+		s_type_path a.a_path ^ s_type_params ctx tl
+	| TFun ([],t) ->
+		"Void -> " ^ s_fun ctx t false
+	| TFun (l,t) ->
+		let args = match l with
+			| [] -> "()"
+			| ["",b,t] -> Printf.sprintf "%s%s" (if b then "?" else "") (s_fun ctx t true)
+			| _ ->
+				let args = String.concat ", " (List.map (fun (s,b,t) ->
+					(if b then "?" else "") ^ (if s = "" then "" else s ^ " : ") ^ s_fun ctx t true
+				) l) in
+				"(" ^ args ^ ")"
+		in
+		Printf.sprintf "%s -> %s" args (s_fun ctx t false)
+	| TAnon a ->
+		begin
+			match !(a.a_status) with
+			| Statics c -> Printf.sprintf "{ Statics %s }" (s_type_path c.cl_path)
+			| EnumStatics e -> Printf.sprintf "{ EnumStatics %s }" (s_type_path e.e_path)
+			| AbstractStatics a -> Printf.sprintf "{ AbstractStatics %s }" (s_type_path a.a_path)
+			| _ ->
+				let fl = PMap.fold (fun f acc -> ((if Meta.has Meta.Optional f.cf_meta then " ?" else " ") ^ f.cf_name ^ " : " ^ s_type ctx f.cf_type) :: acc) a.a_fields [] in
+				"{" ^ (if not (is_closed a) then "+" else "") ^  String.concat "," fl ^ " }"
+		end
+	| TDynamic t2 ->
+		"Dynamic" ^ s_type_params ctx (if t == t2 then [] else [t2])
+	| TLazy f ->
+		s_type ctx (lazy_type f)
+
+and s_fun ctx t void =
+	match t with
+	| TFun _ ->
+		"(" ^ s_type ctx t ^ ")"
+	| TAbstract ({ a_path = ([],"Void") },[]) when void ->
+		"(" ^ s_type ctx t ^ ")"
+	| TMono r ->
+		(match r.tm_type with
+		| None -> s_type ctx t
+		| Some t -> s_fun ctx t void)
+	| TLazy f ->
+		s_fun ctx (lazy_type f) void
+	| _ ->
+		s_type ctx t
+
+and s_type_params ctx = function
+	| [] -> ""
+	| l -> "<" ^ String.concat ", " (List.map (s_type ctx) l) ^ ">"
+
+let s_access is_read = function
+	| AccNormal -> "default"
+	| AccNo -> "null"
+	| AccNever -> "never"
+	| AccResolve -> "resolve"
+	| AccCall -> if is_read then "get" else "set"
+	| AccInline	-> "inline"
+	| AccRequire (n,_) -> "require " ^ n
+	| AccCtor -> "ctor"
+
+let s_kind = function
+	| Var { v_read = AccNormal; v_write = AccNormal } -> "var"
+	| Var v -> "(" ^ s_access true v.v_read ^ "," ^ s_access false v.v_write ^ ")"
+	| Method m ->
+		match m with
+		| MethNormal -> "method"
+		| MethDynamic -> "dynamic method"
+		| MethInline -> "inline method"
+		| MethMacro -> "macro method"
+
+let s_expr_kind e =
+	match e.eexpr with
+	| TConst _ -> "Const"
+	| TLocal _ -> "Local"
+	| TArray (_,_) -> "Array"
+	| TBinop (_,_,_) -> "Binop"
+	| TEnumParameter (_,_,_) -> "EnumParameter"
+	| TEnumIndex _ -> "EnumIndex"
+	| TField (_,_) -> "Field"
+	| TTypeExpr _ -> "TypeExpr"
+	| TParenthesis _ -> "Parenthesis"
+	| TObjectDecl _ -> "ObjectDecl"
+	| TArrayDecl _ -> "ArrayDecl"
+	| TCall (_,_) -> "Call"
+	| TNew (_,_,_) -> "New"
+	| TUnop (_,_,_) -> "Unop"
+	| TFunction _ -> "Function"
+	| TVar _ -> "Vars"
+	| TBlock _ -> "Block"
+	| TFor (_,_,_) -> "For"
+	| TIf (_,_,_) -> "If"
+	| TWhile (_,_,_) -> "While"
+	| TSwitch (_,_,_) -> "Switch"
+	| TTry (_,_) -> "Try"
+	| TReturn _ -> "Return"
+	| TBreak -> "Break"
+	| TContinue -> "Continue"
+	| TThrow _ -> "Throw"
+	| TCast _ -> "Cast"
+	| TMeta _ -> "Meta"
+	| TIdent _ -> "Ident"
+
+let s_const = function
+	| TInt i -> Int32.to_string i
+	| TFloat s -> s
+	| TString s -> Printf.sprintf "\"%s\"" (StringHelper.s_escape s)
+	| TBool b -> if b then "true" else "false"
+	| TNull -> "null"
+	| TThis -> "this"
+	| TSuper -> "super"
+
+let s_field_access s_type fa = match fa with
+	| FStatic (c,f) -> "static(" ^ s_type_path c.cl_path ^ "." ^ f.cf_name ^ ")"
+	| FInstance (c,_,f) -> "inst(" ^ s_type_path c.cl_path ^ "." ^ f.cf_name ^ " : " ^ s_type f.cf_type ^ ")"
+	| FClosure (c,f) -> "closure(" ^ (match c with None -> f.cf_name | Some (c,_) -> s_type_path c.cl_path ^ "." ^ f.cf_name)  ^ ")"
+	| FAnon f -> "anon(" ^ f.cf_name ^ ")"
+	| FEnum (en,f) -> "enum(" ^ s_type_path en.e_path ^ "." ^ f.ef_name ^ ")"
+	| FDynamic f -> "dynamic(" ^ f ^ ")"
+
+let rec s_expr s_type e =
+	let sprintf = Printf.sprintf in
+	let slist f l = String.concat "," (List.map f l) in
+	let loop = s_expr s_type in
+	let s_var v = v.v_name ^ ":" ^ string_of_int v.v_id ^ if v.v_capture then "[c]" else "" in
+	let str = (match e.eexpr with
+	| TConst c ->
+		"Const " ^ s_const c
+	| TLocal v ->
+		"Local " ^ s_var v
+	| TArray (e1,e2) ->
+		sprintf "%s[%s]" (loop e1) (loop e2)
+	| TBinop (op,e1,e2) ->
+		sprintf "(%s %s %s)" (loop e1) (s_binop op) (loop e2)
+	| TEnumIndex e1 ->
+		sprintf "EnumIndex %s" (loop e1)
+	| TEnumParameter (e1,_,i) ->
+		sprintf "%s[%i]" (loop e1) i
+	| TField (e,f) ->
+		let fstr = s_field_access s_type f in
+		sprintf "%s.%s" (loop e) fstr
+	| TTypeExpr m ->
+		sprintf "TypeExpr %s" (s_type_path (t_path m))
+	| TParenthesis e ->
+		sprintf "Parenthesis %s" (loop e)
+	| TObjectDecl fl ->
+		sprintf "ObjectDecl {%s}" (slist (fun ((f,_,qs),e) -> sprintf "%s : %s" (s_object_key_name f qs) (loop e)) fl)
+	| TArrayDecl el ->
+		sprintf "ArrayDecl [%s]" (slist loop el)
+	| TCall (e,el) ->
+		sprintf "Call %s(%s)" (loop e) (slist loop el)
+	| TNew (c,pl,el) ->
+		sprintf "New %s%s(%s)" (s_type_path c.cl_path) (match pl with [] -> "" | l -> sprintf "<%s>" (slist s_type l)) (slist loop el)
+	| TUnop (op,f,e) ->
+		(match f with
+		| Prefix -> sprintf "(%s %s)" (s_unop op) (loop e)
+		| Postfix -> sprintf "(%s %s)" (loop e) (s_unop op))
+	| TFunction f ->
+		let args = slist (fun (v,o) -> sprintf "%s : %s%s" (s_var v) (s_type v.v_type) (match o with None -> "" | Some c -> " = " ^ loop c)) f.tf_args in
+		sprintf "Function(%s) : %s = %s" args (s_type f.tf_type) (loop f.tf_expr)
+	| TVar (v,eo) ->
+		sprintf "Vars %s" (sprintf "%s : %s%s" (s_var v) (s_type v.v_type) (match eo with None -> "" | Some e -> " = " ^ loop e))
+	| TBlock el ->
+		sprintf "Block {\n%s}" (String.concat "" (List.map (fun e -> sprintf "%s;\n" (loop e)) el))
+	| TFor (v,econd,e) ->
+		sprintf "For (%s : %s in %s,%s)" (s_var v) (s_type v.v_type) (loop econd) (loop e)
+	| TIf (e,e1,e2) ->
+		sprintf "If (%s,%s%s)" (loop e) (loop e1) (match e2 with None -> "" | Some e -> "," ^ loop e)
+	| TWhile (econd,e,flag) ->
+		(match flag with
+		| NormalWhile -> sprintf "While (%s,%s)" (loop econd) (loop e)
+		| DoWhile -> sprintf "DoWhile (%s,%s)" (loop e) (loop econd))
+	| TSwitch (e,cases,def) ->
+		sprintf "Switch (%s,(%s)%s)" (loop e) (slist (fun (cl,e) -> sprintf "case %s: %s" (slist loop cl) (loop e)) cases) (match def with None -> "" | Some e -> "," ^ loop e)
+	| TTry (e,cl) ->
+		sprintf "Try %s(%s) " (loop e) (slist (fun (v,e) -> sprintf "catch( %s : %s ) %s" (s_var v) (s_type v.v_type) (loop e)) cl)
+	| TReturn None ->
+		"Return"
+	| TReturn (Some e) ->
+		sprintf "Return %s" (loop e)
+	| TBreak ->
+		"Break"
+	| TContinue ->
+		"Continue"
+	| TThrow e ->
+		"Throw " ^ (loop e)
+	| TCast (e,t) ->
+		sprintf "Cast %s%s" (match t with None -> "" | Some t -> s_type_path (t_path t) ^ ": ") (loop e)
+	| TMeta ((n,el,_),e) ->
+		sprintf "@%s%s %s" (Meta.to_string n) (match el with [] -> "" | _ -> "(" ^ (String.concat ", " (List.map Ast.Printer.s_expr el)) ^ ")") (loop e)
+	| TIdent s ->
+		"Ident " ^ s
+	) in
+	sprintf "(%s : %s)" str (s_type e.etype)
+
+let rec s_expr_pretty print_var_ids tabs top_level s_type e =
+	let sprintf = Printf.sprintf in
+	let loop = s_expr_pretty print_var_ids tabs false s_type in
+	let slist c f l = String.concat c (List.map f l) in
+	let clist f l = slist ", " f l in
+	let local v = if print_var_ids then sprintf "%s<%i>" v.v_name v.v_id else v.v_name in
+	match e.eexpr with
+	| TConst c -> s_const c
+	| TLocal v -> local v
+	| TArray (e1,e2) -> sprintf "%s[%s]" (loop e1) (loop e2)
+	| TBinop (op,e1,e2) -> sprintf "%s %s %s" (loop e1) (s_binop op) (loop e2)
+	| TEnumParameter (e1,_,i) -> sprintf "%s[%i]" (loop e1) i
+	| TEnumIndex e1 -> sprintf "enumIndex %s" (loop e1)
+	| TField (e1,s) -> sprintf "%s.%s" (loop e1) (field_name s)
+	| TTypeExpr mt -> (s_type_path (t_path mt))
+	| TParenthesis e1 -> sprintf "(%s)" (loop e1)
+	| TObjectDecl fl -> sprintf "{%s}" (clist (fun ((f,_,qs),e) -> sprintf "%s : %s" (s_object_key_name f qs) (loop e)) fl)
+	| TArrayDecl el -> sprintf "[%s]" (clist loop el)
+	| TCall (e1,el) -> sprintf "%s(%s)" (loop e1) (clist loop el)
+	| TNew (c,pl,el) ->
+		sprintf "new %s(%s)" (s_type_path c.cl_path) (clist loop el)
+	| TUnop (op,f,e) ->
+		(match f with
+		| Prefix -> sprintf "%s %s" (s_unop op) (loop e)
+		| Postfix -> sprintf "%s %s" (loop e) (s_unop op))
+	| TFunction f ->
+		let args = clist (fun (v,o) -> sprintf "%s:%s%s" (local v) (s_type v.v_type) (match o with None -> "" | Some c -> " = " ^ loop c)) f.tf_args in
+		sprintf "%s(%s) %s" (if top_level then "" else "function") args (loop f.tf_expr)
+	| TVar (v,eo) ->
+		sprintf "var %s" (sprintf "%s%s" (local v) (match eo with None -> "" | Some e -> " = " ^ loop e))
+	| TBlock el ->
+		let ntabs = tabs ^ "\t" in
+		let s = sprintf "{\n%s" (String.concat "" (List.map (fun e -> sprintf "%s%s;\n" ntabs (s_expr_pretty print_var_ids ntabs top_level s_type e)) el)) in
+		(match el with
+			| [] -> "{}"
+			| _ ->  s ^ tabs ^ "}")
+	| TFor (v,econd,e) ->
+		sprintf "for (%s in %s) %s" (local v) (loop econd) (loop e)
+	| TIf (e,e1,e2) ->
+		sprintf "if (%s) %s%s" (loop e) (loop e1) (match e2 with None -> "" | Some e -> " else " ^ loop e)
+	| TWhile (econd,e,flag) ->
+		(match flag with
+		| NormalWhile -> sprintf "while (%s) %s" (loop econd) (loop e)
+		| DoWhile -> sprintf "do (%s) while(%s)" (loop e) (loop econd))
+	| TSwitch (e,cases,def) ->
+		let ntabs = tabs ^ "\t" in
+		let s = sprintf "switch (%s) {\n%s%s" (loop e) (slist "" (fun (cl,e) -> sprintf "%scase %s: %s;\n" ntabs (clist loop cl) (s_expr_pretty print_var_ids ntabs top_level s_type e)) cases) (match def with None -> "" | Some e -> ntabs ^ "default: " ^ (s_expr_pretty print_var_ids ntabs top_level s_type e) ^ "\n") in
+		s ^ tabs ^ "}"
+	| TTry (e,cl) ->
+		sprintf "try %s%s" (loop e) (clist (fun (v,e) -> sprintf " catch (%s:%s) %s" (local v) (s_type v.v_type) (loop e)) cl)
+	| TReturn None ->
+		"return"
+	| TReturn (Some e) ->
+		sprintf "return %s" (loop e)
+	| TBreak ->
+		"break"
+	| TContinue ->
+		"continue"
+	| TThrow e ->
+		"throw " ^ (loop e)
+	| TCast (e,None) ->
+		sprintf "cast %s" (loop e)
+	| TCast (e,Some mt) ->
+		sprintf "cast (%s,%s)" (loop e) (s_type_path (t_path mt))
+	| TMeta ((n,el,_),e) ->
+		sprintf "@%s%s %s" (Meta.to_string n) (match el with [] -> "" | _ -> "(" ^ (String.concat ", " (List.map Ast.Printer.s_expr el)) ^ ")") (loop e)
+	| TIdent s ->
+		s
+
+let rec s_expr_ast print_var_ids tabs s_type e =
+	let sprintf = Printf.sprintf in
+	let loop ?(extra_tabs="") = s_expr_ast print_var_ids (tabs ^ "\t" ^ extra_tabs) s_type in
+	let tag_args tabs sl = match sl with
+		| [] -> ""
+		| [s] when not (String.contains s '\n') -> " " ^ s
+		| _ ->
+			let tabs = "\n" ^ tabs ^ "\t" in
+			tabs ^ (String.concat tabs sl)
+	in
+	let tag s ?(t=None) ?(extra_tabs="") sl =
+		let st = match t with
+			| None -> s_type e.etype
+			| Some t -> s_type t
+		in
+		sprintf "[%s:%s]%s" s st (tag_args (tabs ^ extra_tabs) sl)
+	in
+	let var_id v = if print_var_ids then v.v_id else 0 in
+	let const c t = tag "Const" ~t [s_const c] in
+	let local v t = sprintf "[Local %s(%i):%s%s]" v.v_name (var_id v) (s_type v.v_type) (match t with None -> "" | Some t -> ":" ^ (s_type t)) in
+	let var v sl = sprintf "[Var %s(%i):%s]%s" v.v_name (var_id v) (s_type v.v_type) (tag_args tabs sl) in
+	let module_type mt = sprintf "[TypeExpr %s:%s]" (s_type_path (t_path mt)) (s_type e.etype) in
+	match e.eexpr with
+	| TConst c -> const c (Some e.etype)
+	| TLocal v -> local v (Some e.etype)
+	| TArray (e1,e2) -> tag "Array" [loop e1; loop e2]
+	| TBinop (op,e1,e2) -> tag "Binop" [loop e1; s_binop op; loop e2]
+	| TUnop (op,flag,e1) -> tag "Unop" [s_unop op; if flag = Postfix then "Postfix" else "Prefix"; loop e1]
+	| TEnumParameter (e1,ef,i) -> tag "EnumParameter" [loop e1; ef.ef_name; string_of_int i]
+	| TEnumIndex e1 -> tag "EnumIndex" [loop e1]
+	| TField (e1,fa) ->
+		let sfa = match fa with
+			| FInstance(c,tl,cf) -> tag "FInstance" ~extra_tabs:"\t" [s_type (TInst(c,tl)); Printf.sprintf "%s:%s" cf.cf_name (s_type cf.cf_type)]
+			| FStatic(c,cf) -> tag "FStatic" ~extra_tabs:"\t" [s_type_path c.cl_path; Printf.sprintf "%s:%s" cf.cf_name (s_type cf.cf_type)]
+			| FClosure(co,cf) -> tag "FClosure" ~extra_tabs:"\t" [(match co with None -> "None" | Some (c,tl) -> s_type (TInst(c,tl))); Printf.sprintf "%s:%s" cf.cf_name (s_type cf.cf_type)]
+			| FAnon cf -> tag "FAnon" ~extra_tabs:"\t" [Printf.sprintf "%s:%s" cf.cf_name (s_type cf.cf_type)]
+			| FDynamic s -> tag "FDynamic" ~extra_tabs:"\t" [s]
+			| FEnum(en,ef) -> tag "FEnum" ~extra_tabs:"\t" [s_type_path en.e_path; ef.ef_name]
+		in
+		tag "Field" [loop e1; sfa]
+	| TTypeExpr mt -> module_type mt
+	| TParenthesis e1 -> tag "Parenthesis" [loop e1]
+	| TObjectDecl fl -> tag "ObjectDecl" (List.map (fun ((s,_,qs),e) -> sprintf "%s: %s" (s_object_key_name s qs) (loop e)) fl)
+	| TArrayDecl el -> tag "ArrayDecl" (List.map loop el)
+	| TCall (e1,el) -> tag "Call" (loop e1 :: (List.map loop el))
+	| TNew (c,tl,el) -> tag "New" ((s_type (TInst(c,tl))) :: (List.map loop el))
+	| TFunction f ->
+		let arg (v,cto) =
+			tag "Arg" ~t:(Some v.v_type) ~extra_tabs:"\t" (match cto with None -> [local v None] | Some ct -> [local v None;loop ct])
+		in
+		tag "Function" ((List.map arg f.tf_args) @ [loop f.tf_expr])
+	| TVar (v,eo) -> var v (match eo with None -> [] | Some e -> [loop e])
+	| TBlock el -> tag "Block" (List.map loop el)
+	| TIf (e,e1,e2) -> tag "If" (loop e :: (Printf.sprintf "[Then:%s] %s" (s_type e1.etype) (loop e1)) :: (match e2 with None -> [] | Some e -> [Printf.sprintf "[Else:%s] %s" (s_type e.etype) (loop e)]))
+	| TCast (e1,None) -> tag "Cast" [loop e1]
+	| TCast (e1,Some mt) -> tag "Cast" [loop e1; module_type mt]
+	| TThrow e1 -> tag "Throw" [loop e1]
+	| TBreak -> tag "Break" []
+	| TContinue -> tag "Continue" []
+	| TReturn None -> tag "Return" []
+	| TReturn (Some e1) -> tag "Return" [loop e1]
+	| TWhile (e1,e2,NormalWhile) -> tag "While" [loop e1; loop e2]
+	| TWhile (e1,e2,DoWhile) -> tag "Do" [loop e1; loop e2]
+	| TFor (v,e1,e2) -> tag "For" [local v None; loop e1; loop e2]
+	| TTry (e1,catches) ->
+		let sl = List.map (fun (v,e) ->
+			sprintf "Catch %s%s" (local v None) (tag_args (tabs ^ "\t") [loop ~extra_tabs:"\t" e]);
+		) catches in
+		tag "Try" ((loop e1) :: sl)
+	| TSwitch (e1,cases,eo) ->
+		let sl = List.map (fun (el,e) ->
+			tag "Case" ~t:(Some e.etype) ~extra_tabs:"\t" ((List.map loop el) @ [loop ~extra_tabs:"\t" e])
+		) cases in
+		let sl = match eo with
+			| None -> sl
+			| Some e -> sl @ [tag "Default" ~t:(Some e.etype) ~extra_tabs:"\t" [loop ~extra_tabs:"\t" e]]
+		in
+		tag "Switch" ((loop e1) :: sl)
+	| TMeta ((m,el,_),e1) ->
+		let s = Meta.to_string m in
+		let s = match el with
+			| [] -> s
+			| _ -> sprintf "%s(%s)" s (String.concat ", " (List.map Ast.Printer.s_expr el))
+		in
+		tag "Meta" [s; loop e1]
+	| TIdent s ->
+		tag "Ident" [s]
+
+let s_types ?(sep = ", ") tl =
+	let pctx = print_context() in
+	String.concat sep (List.map (s_type pctx) tl)
+
+let s_class_kind = function
+	| KNormal ->
+		"KNormal"
+	| KTypeParameter tl ->
+		Printf.sprintf "KTypeParameter [%s]" (s_types tl)
+	| KExpr _ ->
+		"KExpr"
+	| KGeneric ->
+		"KGeneric"
+	| KGenericInstance(c,tl) ->
+		Printf.sprintf "KGenericInstance %s<%s>" (s_type_path c.cl_path) (s_types tl)
+	| KMacroType ->
+		"KMacroType"
+	| KGenericBuild _ ->
+		"KGenericBuild"
+	| KAbstractImpl a ->
+		Printf.sprintf "KAbstractImpl %s" (s_type_path a.a_path)
+
+module Printer = struct
+
+	let s_type t =
+		s_type (print_context()) t
+
+	let s_pair s1 s2 =
+		Printf.sprintf "(%s,%s)" s1 s2
+
+	let s_record_field name value =
+		Printf.sprintf "%s = %s;" name value
+
+	let s_pos p =
+		Printf.sprintf "%s: %i-%i" p.pfile p.pmin p.pmax
+
+	let s_record_fields tabs fields =
+		let sl = List.map (fun (name,value) -> s_record_field name value) fields in
+		Printf.sprintf "{\n%s\t%s\n%s}" tabs (String.concat ("\n\t" ^ tabs) sl) tabs
+
+	let s_list sep f l =
+		"[" ^ (String.concat sep (List.map f l)) ^ "]"
+
+	let s_opt f o = match o with
+		| None -> "None"
+		| Some v -> f v
+
+	let s_pmap fk fv pm =
+		"{" ^ (String.concat ", " (PMap.foldi (fun k v acc -> (Printf.sprintf "%s = %s" (fk k) (fv v)) :: acc) pm [])) ^ "}"
+
+	let s_doc = s_opt (fun s -> s)
+
+	let s_metadata_entry (s,el,_) =
+		Printf.sprintf "@%s%s" (Meta.to_string s) (match el with [] -> "" | el -> "(" ^ (String.concat ", " (List.map Ast.Printer.s_expr el)) ^ ")")
+
+	let s_metadata metadata =
+		s_list " " s_metadata_entry metadata
+
+	let s_type_param (s,t) = match follow t with
+		| TInst({cl_kind = KTypeParameter tl1},tl2) ->
+			begin match tl1 with
+			| [] -> s
+			| _ -> Printf.sprintf "%s:%s" s (String.concat ", " (List.map s_type tl1))
+			end
+		| _ -> assert false
+
+	let s_type_params tl =
+		s_list ", " s_type_param tl
+
+	let s_tclass_field tabs cf =
+		s_record_fields tabs [
+			"cf_name",cf.cf_name;
+			"cf_doc",s_doc cf.cf_doc;
+			"cf_type",s_type_kind (follow cf.cf_type);
+			"cf_pos",s_pos cf.cf_pos;
+			"cf_name_pos",s_pos cf.cf_name_pos;
+			"cf_meta",s_metadata cf.cf_meta;
+			"cf_kind",s_kind cf.cf_kind;
+			"cf_params",s_type_params cf.cf_params;
+			"cf_expr",s_opt (s_expr_ast true "\t\t" s_type) cf.cf_expr;
+		]
+
+	let s_tclass tabs c =
+		s_record_fields tabs [
+			"cl_path",s_type_path c.cl_path;
+			"cl_module",s_type_path c.cl_module.m_path;
+			"cl_pos",s_pos c.cl_pos;
+			"cl_name_pos",s_pos c.cl_name_pos;
+			"cl_private",string_of_bool c.cl_private;
+			"cl_doc",s_doc c.cl_doc;
+			"cl_meta",s_metadata c.cl_meta;
+			"cl_params",s_type_params c.cl_params;
+			"cl_kind",s_class_kind c.cl_kind;
+			"cl_extern",string_of_bool c.cl_extern;
+			"cl_final",string_of_bool c.cl_final;
+			"cl_interface",string_of_bool c.cl_interface;
+			"cl_super",s_opt (fun (c,tl) -> s_type (TInst(c,tl))) c.cl_super;
+			"cl_implements",s_list ", " (fun (c,tl) -> s_type (TInst(c,tl))) c.cl_implements;
+			"cl_array_access",s_opt s_type c.cl_array_access;
+			"cl_overrides",s_list "," (fun cf -> cf.cf_name) c.cl_overrides;
+			"cl_init",s_opt (s_expr_ast true "" s_type) c.cl_init;
+			"cl_constructor",s_opt (s_tclass_field (tabs ^ "\t")) c.cl_constructor;
+			"cl_ordered_fields",s_list "\n\t" (s_tclass_field (tabs ^ "\t")) c.cl_ordered_fields;
+			"cl_ordered_statics",s_list "\n\t" (s_tclass_field (tabs ^ "\t")) c.cl_ordered_statics;
+		]
+
+	let s_tdef tabs t =
+		s_record_fields tabs [
+			"t_path",s_type_path t.t_path;
+			"t_module",s_type_path t.t_module.m_path;
+			"t_pos",s_pos t.t_pos;
+			"t_name_pos",s_pos t.t_name_pos;
+			"t_private",string_of_bool t.t_private;
+			"t_doc",s_doc t.t_doc;
+			"t_meta",s_metadata t.t_meta;
+			"t_params",s_type_params t.t_params;
+			"t_type",s_type_kind t.t_type
+		]
+
+	let s_tenum_field tabs ef =
+		s_record_fields tabs [
+			"ef_name",ef.ef_name;
+			"ef_doc",s_doc ef.ef_doc;
+			"ef_pos",s_pos ef.ef_pos;
+			"ef_name_pos",s_pos ef.ef_name_pos;
+			"ef_type",s_type_kind ef.ef_type;
+			"ef_index",string_of_int ef.ef_index;
+			"ef_params",s_type_params ef.ef_params;
+			"ef_meta",s_metadata ef.ef_meta
+		]
+
+	let s_tenum tabs en =
+		s_record_fields tabs [
+			"e_path",s_type_path en.e_path;
+			"e_module",s_type_path en.e_module.m_path;
+			"e_pos",s_pos en.e_pos;
+			"e_name_pos",s_pos en.e_name_pos;
+			"e_private",string_of_bool en.e_private;
+			"d_doc",s_doc en.e_doc;
+			"e_meta",s_metadata en.e_meta;
+			"e_params",s_type_params en.e_params;
+			"e_type",s_tdef "\t" en.e_type;
+			"e_extern",string_of_bool en.e_extern;
+			"e_constrs",s_list "\n\t" (s_tenum_field (tabs ^ "\t")) (PMap.fold (fun ef acc -> ef :: acc) en.e_constrs []);
+			"e_names",String.concat ", " en.e_names
+		]
+
+	let s_tabstract tabs a =
+		s_record_fields tabs [
+			"a_path",s_type_path a.a_path;
+			"a_modules",s_type_path a.a_module.m_path;
+			"a_pos",s_pos a.a_pos;
+			"a_name_pos",s_pos a.a_name_pos;
+			"a_private",string_of_bool a.a_private;
+			"a_doc",s_doc a.a_doc;
+			"a_meta",s_metadata a.a_meta;
+			"a_params",s_type_params a.a_params;
+			"a_ops",s_list ", " (fun (op,cf) -> Printf.sprintf "%s: %s" (s_binop op) cf.cf_name) a.a_ops;
+			"a_unops",s_list ", " (fun (op,flag,cf) -> Printf.sprintf "%s (%s): %s" (s_unop op) (if flag = Postfix then "postfix" else "prefix") cf.cf_name) a.a_unops;
+			"a_impl",s_opt (fun c -> s_type_path c.cl_path) a.a_impl;
+			"a_this",s_type_kind a.a_this;
+			"a_from",s_list ", " s_type_kind a.a_from;
+			"a_to",s_list ", " s_type_kind a.a_to;
+			"a_from_field",s_list ", " (fun (t,cf) -> Printf.sprintf "%s: %s" (s_type_kind t) cf.cf_name) a.a_from_field;
+			"a_to_field",s_list ", " (fun (t,cf) -> Printf.sprintf "%s: %s" (s_type_kind t) cf.cf_name) a.a_to_field;
+			"a_array",s_list ", " (fun cf -> cf.cf_name) a.a_array;
+			"a_read",s_opt (fun cf -> cf.cf_name) a.a_read;
+			"a_write",s_opt (fun cf -> cf.cf_name) a.a_write;
+		]
+
+	let s_tvar_extra (tl,eo) =
+		Printf.sprintf "Some(%s, %s)" (s_type_params tl) (s_opt (s_expr_ast true "" s_type) eo)
+
+	let s_tvar v =
+		s_record_fields "" [
+			"v_id",string_of_int v.v_id;
+			"v_name",v.v_name;
+			"v_type",s_type v.v_type;
+			"v_capture",string_of_bool v.v_capture;
+			"v_extra",s_opt s_tvar_extra v.v_extra;
+			"v_meta",s_metadata v.v_meta;
+		]
+
+	let s_module_kind = function
+		| MCode -> "MCode"
+		| MMacro -> "MMacro"
+		| MFake -> "MFake"
+		| MExtern -> "MExtern"
+		| MImport -> "MImport"
+
+	let s_module_def_extra tabs me =
+		s_record_fields tabs [
+			"m_file",me.m_file;
+			"m_sign",me.m_sign;
+			"m_time",string_of_float me.m_time;
+			"m_dirty",s_opt s_type_path me.m_dirty;
+			"m_added",string_of_int me.m_added;
+			"m_mark",string_of_int me.m_mark;
+			"m_deps",s_pmap string_of_int (fun m -> snd m.m_path) me.m_deps;
+			"m_processed",string_of_int me.m_processed;
+			"m_kind",s_module_kind me.m_kind;
+			"m_binded_res",""; (* TODO *)
+			"m_if_feature",""; (* TODO *)
+			"m_features",""; (* TODO *)
+		]
+
+	let s_module_def m =
+		s_record_fields "" [
+			"m_id",string_of_int m.m_id;
+			"m_path",s_type_path m.m_path;
+			"m_extra",s_module_def_extra "\t" m.m_extra
+		]
+
+	let s_type_path tp =
+		s_record_fields "" [
+			"tpackage",s_list "." (fun s -> s) tp.tpackage;
+			"tname",tp.tname;
+			"tparams","";
+			"tsub",s_opt (fun s -> s) tp.tsub;
+		]
+
+	let s_class_flag = function
+		| HInterface -> "HInterface"
+		| HExtern -> "HExtern"
+		| HPrivate -> "HPrivate"
+		| HExtends tp -> "HExtends " ^ (s_type_path (fst tp))
+		| HImplements tp -> "HImplements " ^ (s_type_path (fst tp))
+		| HFinal -> "HFinal"
+
+	let s_placed f (x,p) =
+		s_pair (f x) (s_pos p)
+
+	let s_class_field cff =
+		s_record_fields "" [
+			"cff_name",s_placed (fun s -> s) cff.cff_name;
+			"cff_doc",s_opt (fun s -> s) cff.cff_doc;
+			"cff_pos",s_pos cff.cff_pos;
+			"cff_meta",s_metadata cff.cff_meta;
+			"cff_access",s_list ", " Ast.s_placed_access cff.cff_access;
+		]
+end

+ 376 - 0
src/core/tType.ml

@@ -0,0 +1,376 @@
+open Ast
+open Globals
+
+type field_kind =
+	| Var of var_kind
+	| Method of method_kind
+
+and var_kind = {
+	v_read : var_access;
+	v_write : var_access;
+}
+
+and var_access =
+	| AccNormal
+	| AccNo             (* can't be accessed outside of the class itself and its subclasses *)
+	| AccNever          (* can't be accessed, even in subclasses *)
+	| AccCtor           (* can only be accessed from the constructor *)
+	| AccResolve        (* call resolve("field") when accessed *)
+	| AccCall           (* perform a method call when accessed *)
+	| AccInline         (* similar to Normal but inline when accessed *)
+	| AccRequire of string * string option (* set when @:require(cond) fails *)
+
+and method_kind =
+	| MethNormal
+	| MethInline
+	| MethDynamic
+	| MethMacro
+
+type module_check_policy =
+	| NoCheckFileTimeModification
+	| CheckFileContentModification
+	| NoCheckDependencies
+	| NoCheckShadowing
+
+type t =
+	| TMono of tmono
+	| TEnum of tenum * tparams
+	| TInst of tclass * tparams
+	| TType of tdef * tparams
+	| TFun of tsignature
+	| TAnon of tanon
+	| TDynamic of t
+	| TLazy of tlazy ref
+	| TAbstract of tabstract * tparams
+
+and tmono_constraint =
+    | CStructure of t * tanon
+    | CTypes of t list
+
+and tmono = {
+    mutable tm_type : t option;
+    mutable tm_constraint : (tmono_constraint * string * pos) option;
+}
+
+and tlazy =
+	| LAvailable of t
+	| LProcessing of (unit -> t)
+	| LWait of (unit -> t)
+
+and tsignature = (string * bool * t) list * t
+
+and tparams = t list
+
+and type_params = (string * t) list
+
+and tconstant =
+	| TInt of int32
+	| TFloat of string
+	| TString of string
+	| TBool of bool
+	| TNull
+	| TThis
+	| TSuper
+
+and tvar_extra = (type_params * texpr option) option
+
+and tvar_origin =
+	| TVOLocalVariable
+	| TVOArgument
+	| TVOForVariable
+	| TVOPatternVariable
+	| TVOCatchVariable
+	| TVOLocalFunction
+
+and tvar_kind =
+	| VUser of tvar_origin
+	| VGenerated
+	| VInlined
+	| VInlinedConstructorVariable
+	| VExtractorVariable
+
+and tvar = {
+	mutable v_id : int;
+	mutable v_name : string;
+	mutable v_type : t;
+	mutable v_kind : tvar_kind;
+	mutable v_capture : bool;
+	mutable v_final : bool;
+	mutable v_extra : tvar_extra;
+	mutable v_meta : metadata;
+	v_pos : pos;
+}
+
+and tfunc = {
+	tf_args : (tvar * texpr option) list;
+	tf_type : t;
+	tf_expr : texpr;
+}
+
+and anon_status =
+	| Closed
+	| Opened
+	| Const
+	| Extend of t list
+	| Statics of tclass
+	| EnumStatics of tenum
+	| AbstractStatics of tabstract
+
+and tanon = {
+	mutable a_fields : (string, tclass_field) PMap.t;
+	a_status : anon_status ref;
+}
+
+and texpr_expr =
+	| TConst of tconstant
+	| TLocal of tvar
+	| TArray of texpr * texpr
+	| TBinop of Ast.binop * texpr * texpr
+	| TField of texpr * tfield_access
+	| TTypeExpr of module_type
+	| TParenthesis of texpr
+	| TObjectDecl of ((string * pos * quote_status) * texpr) list
+	| TArrayDecl of texpr list
+	| TCall of texpr * texpr list
+	| TNew of tclass * tparams * texpr list
+	| TUnop of Ast.unop * Ast.unop_flag * texpr
+	| TFunction of tfunc
+	| TVar of tvar * texpr option
+	| TBlock of texpr list
+	| TFor of tvar * texpr * texpr
+	| TIf of texpr * texpr * texpr option
+	| TWhile of texpr * texpr * Ast.while_flag
+	| TSwitch of texpr * (texpr list * texpr) list * texpr option
+	| TTry of texpr * (tvar * texpr) list
+	| TReturn of texpr option
+	| TBreak
+	| TContinue
+	| TThrow of texpr
+	| TCast of texpr * module_type option
+	| TMeta of metadata_entry * texpr
+	| TEnumParameter of texpr * tenum_field * int
+	| TEnumIndex of texpr
+	| TIdent of string
+
+and tfield_access =
+	| FInstance of tclass * tparams * tclass_field
+	| FStatic of tclass * tclass_field
+	| FAnon of tclass_field
+	| FDynamic of string
+	| FClosure of (tclass * tparams) option * tclass_field (* None class = TAnon *)
+	| FEnum of tenum * tenum_field
+
+and texpr = {
+	eexpr : texpr_expr;
+	etype : t;
+	epos : pos;
+}
+
+and tclass_field = {
+	mutable cf_name : string;
+	mutable cf_type : t;
+	cf_pos : pos;
+	cf_name_pos : pos;
+	mutable cf_doc : Ast.documentation;
+	mutable cf_meta : metadata;
+	mutable cf_kind : field_kind;
+	mutable cf_params : type_params;
+	mutable cf_expr : texpr option;
+	mutable cf_expr_unoptimized : tfunc option;
+	mutable cf_overloads : tclass_field list;
+	mutable cf_flags : int;
+}
+
+and tclass_kind =
+	| KNormal
+	| KTypeParameter of t list
+	| KExpr of Ast.expr
+	| KGeneric
+	| KGenericInstance of tclass * tparams
+	| KMacroType
+	| KGenericBuild of class_field list
+	| KAbstractImpl of tabstract
+
+and metadata = Ast.metadata
+
+and tinfos = {
+	mt_path : path;
+	mt_module : module_def;
+	mt_pos : pos;
+	mt_name_pos : pos;
+	mt_private : bool;
+	mt_doc : Ast.documentation;
+	mutable mt_meta : metadata;
+	mt_params : type_params;
+	mutable mt_using : (tclass * pos) list;
+}
+
+and tclass = {
+	mutable cl_path : path;
+	mutable cl_module : module_def;
+	mutable cl_pos : pos;
+	mutable cl_name_pos : pos;
+	mutable cl_private : bool;
+	mutable cl_doc : Ast.documentation;
+	mutable cl_meta : metadata;
+	mutable cl_params : type_params;
+	mutable cl_using : (tclass * pos) list;
+	(* do not insert any fields above *)
+	mutable cl_kind : tclass_kind;
+	mutable cl_extern : bool;
+	mutable cl_final : bool;
+	mutable cl_interface : bool;
+	mutable cl_super : (tclass * tparams) option;
+	mutable cl_implements : (tclass * tparams) list;
+	mutable cl_fields : (string, tclass_field) PMap.t;
+	mutable cl_statics : (string, tclass_field) PMap.t;
+	mutable cl_ordered_statics : tclass_field list;
+	mutable cl_ordered_fields : tclass_field list;
+	mutable cl_dynamic : t option;
+	mutable cl_array_access : t option;
+	mutable cl_constructor : tclass_field option;
+	mutable cl_init : texpr option;
+	mutable cl_overrides : tclass_field list;
+
+	mutable cl_build : unit -> build_state;
+	mutable cl_restore : unit -> unit;
+	(*
+		These are classes which directly extend or directly implement this class.
+		Populated automatically in post-processing step (Filters.run)
+	*)
+	mutable cl_descendants : tclass list;
+}
+
+and tenum_field = {
+	ef_name : string;
+	mutable ef_type : t;
+	ef_pos : pos;
+	ef_name_pos : pos;
+	ef_doc : Ast.documentation;
+	ef_index : int;
+	mutable ef_params : type_params;
+	mutable ef_meta : metadata;
+}
+
+and tenum = {
+	mutable e_path : path;
+	e_module : module_def;
+	e_pos : pos;
+	e_name_pos : pos;
+	e_private : bool;
+	e_doc : Ast.documentation;
+	mutable e_meta : metadata;
+	mutable e_params : type_params;
+	mutable e_using : (tclass * pos) list;
+	(* do not insert any fields above *)
+	e_type : tdef;
+	mutable e_extern : bool;
+	mutable e_constrs : (string , tenum_field) PMap.t;
+	mutable e_names : string list;
+}
+
+and tdef = {
+	t_path : path;
+	t_module : module_def;
+	t_pos : pos;
+	t_name_pos : pos;
+	t_private : bool;
+	t_doc : Ast.documentation;
+	mutable t_meta : metadata;
+	mutable t_params : type_params;
+	mutable t_using : (tclass * pos) list;
+	(* do not insert any fields above *)
+	mutable t_type : t;
+}
+
+and tabstract = {
+	mutable a_path : path;
+	a_module : module_def;
+	a_pos : pos;
+	a_name_pos : pos;
+	a_private : bool;
+	a_doc : Ast.documentation;
+	mutable a_meta : metadata;
+	mutable a_params : type_params;
+	mutable a_using : (tclass * pos) list;
+	(* do not insert any fields above *)
+	mutable a_ops : (Ast.binop * tclass_field) list;
+	mutable a_unops : (Ast.unop * unop_flag * tclass_field) list;
+	mutable a_impl : tclass option;
+	mutable a_this : t;
+	mutable a_from : t list;
+	mutable a_from_field : (t * tclass_field) list;
+	mutable a_to : t list;
+	mutable a_to_field : (t * tclass_field) list;
+	mutable a_array : tclass_field list;
+	mutable a_read : tclass_field option;
+	mutable a_write : tclass_field option;
+}
+
+and module_type =
+	| TClassDecl of tclass
+	| TEnumDecl of tenum
+	| TTypeDecl of tdef
+	| TAbstractDecl of tabstract
+
+and module_def = {
+	m_id : int;
+	m_path : path;
+	mutable m_types : module_type list;
+	m_extra : module_def_extra;
+}
+
+and module_def_display = {
+	mutable m_inline_calls : (pos * pos) list; (* calls whatever is at pos1 from pos2 *)
+	mutable m_type_hints : (pos * pos) list;
+}
+
+and module_def_extra = {
+	m_file : string;
+	m_sign : string;
+	m_display : module_def_display;
+	mutable m_check_policy : module_check_policy list;
+	mutable m_time : float;
+	mutable m_dirty : path option;
+	mutable m_added : int;
+	mutable m_mark : int;
+	mutable m_deps : (int,module_def) PMap.t;
+	mutable m_processed : int;
+	mutable m_kind : module_kind;
+	mutable m_binded_res : (string, string) PMap.t;
+	mutable m_if_feature : (string *(tclass * tclass_field * bool)) list;
+	mutable m_features : (string,bool) Hashtbl.t;
+}
+
+and module_kind =
+	| MCode
+	| MMacro
+	| MFake
+	| MExtern
+	| MImport
+
+and build_state =
+	| Built
+	| Building of tclass list
+	| BuildMacro of (unit -> unit) list ref
+
+type basic_types = {
+	mutable tvoid : t;
+	mutable tint : t;
+	mutable tfloat : t;
+	mutable tbool : t;
+	mutable tnull : t -> t;
+	mutable tstring : t;
+	mutable tarray : t -> t;
+}
+
+type class_field_scope =
+	| CFSStatic
+	| CFSMember
+	| CFSConstructor
+
+type flag_tclass_field =
+	| CfPublic
+	| CfExtern (* This is only set if the field itself is extern, not just the class. *)
+	| CfFinal
+	| CfModifiesThis (* This is set for methods which reassign `this`. E.g. `this = value` *)

+ 925 - 0
src/core/tUnification.ml

@@ -0,0 +1,925 @@
+open Globals
+open TType
+open TFunctions
+open TPrinting
+
+type unify_error =
+	| Cannot_unify of t * t
+	| Invalid_field_type of string
+	| Has_no_field of t * string
+	| Has_no_runtime_field of t * string
+	| Has_extra_field of t * string
+	| Invalid_kind of string * field_kind * field_kind
+	| Invalid_visibility of string
+	| Not_matching_optional of string
+	| Cant_force_optional
+	| Invariant_parameter of int
+	| Constraint_failure of string
+	| Missing_overload of tclass_field * t
+	| FinalInvariance (* nice band name *)
+	| Invalid_function_argument of int (* index *) * int (* total *)
+	| Invalid_return_type
+	| Unify_custom of string
+
+exception Unify_error of unify_error list
+
+let error l = raise (Unify_error l)
+
+let unify_ref : (t -> t -> unit) ref = ref (fun _ _ -> ())
+
+let check_constraint name f =
+	try
+		f()
+	with Unify_error l ->
+		raise (Unify_error ((Constraint_failure name) :: l))
+
+module Monomorph = struct
+	let create () = {
+		tm_type = None;
+		tm_constraint = None;
+	}
+
+	let unify_merge a b = match a,b with
+		| TAnon an1,TAnon an2 ->
+			let old1 = !(an1.a_status) in
+			let old2 = !(an2.a_status) in
+			an1.a_status := Opened;
+			an2.a_status := Opened;
+			Std.finally (fun () ->
+				an1.a_status := old1;
+				an1.a_status := old2;
+			) (!unify_ref a) b
+		| _ ->
+			!unify_ref a b
+
+	let set_constraint m path p constr =
+		assert(m.tm_type = None);
+		assert(m.tm_constraint = None);
+		m.tm_constraint <- Some (constr,path,p)
+
+	let constrain_to_object m path p tl = set_constraint m path p (CTypes tl)
+
+	let constrain_to_fields m path p fl =
+		let anon = { a_fields = fl; a_status = ref Opened } in
+		set_constraint m path p (CStructure(TAnon anon,anon))
+
+	let do_bind m t =
+		(* assert(m.tm_type = None); *) (* TODO: should be here, but matcher.ml does some weird bind handling at the moment. *)
+		m.tm_type <- Some t
+
+	let merge_constraints mono_to mono_from = match mono_from.tm_constraint with
+		| None ->
+			()
+		| Some cstr -> match mono_to.tm_constraint with
+			| None ->
+				mono_to.tm_constraint <- Some cstr
+			| Some cstr2 -> match cstr,cstr2 with
+				| (CStructure(t1,anon1),path,p),(CStructure(t2,_),_,_) ->
+					!unify_ref t1 t2;
+					mono_to.tm_constraint <- Some(CStructure(t1,anon1),path,p)
+				| (CTypes tl1,path,p),(CTypes tl2,_,_) ->
+					mono_to.tm_constraint <- Some(CTypes (tl1 @ tl2),path,p);
+				| _ ->
+					error [Unify_custom "Cannot merge constraints"]
+
+	let rec bind m t =
+		begin match t with
+		| TMono m2 ->
+			begin match m2.tm_type with
+			| None ->
+				(* Inherit constraints. This avoids too-early unification. *)
+				merge_constraints m2 m;
+				do_bind m t
+			| Some t ->
+				bind m t
+			end;
+		| _ ->
+			Option.may (fun (cstr,path,p) -> match cstr with
+				| CStructure(tanon,anon) ->
+					if not (PMap.is_empty anon.a_fields) then check_constraint path (fun () ->
+						unify_merge t tanon;
+					)
+				| CTypes tl ->
+					check_constraint path (fun () ->
+						List.iter (unify_merge t) tl
+					)
+			) m.tm_constraint;
+			do_bind m t;
+		end
+
+	let unbind m =
+		m.tm_type <- None
+end
+
+let rec link e a b =
+	(* tell if setting a == b will create a type-loop *)
+	let rec loop t =
+		if t == a then
+			true
+		else match t with
+		| TMono t -> (match t.tm_type with None -> false | Some t -> loop t)
+		| TEnum (_,tl) -> List.exists loop tl
+		| TInst (_,tl) | TType (_,tl) | TAbstract (_,tl) -> List.exists loop tl
+		| TFun (tl,t) -> List.exists (fun (_,_,t) -> loop t) tl || loop t
+		| TDynamic t2 ->
+			if t == t2 then
+				false
+			else
+				loop t2
+		| TLazy f ->
+			loop (lazy_type f)
+		| TAnon a ->
+			try
+				PMap.iter (fun _ f -> if loop f.cf_type then raise Exit) a.a_fields;
+				false
+			with
+				Exit -> true
+	in
+	(* tell is already a ~= b *)
+	if loop b then
+		(follow b) == a
+	else if b == t_dynamic then
+		true
+	else begin
+		Monomorph.bind e b;
+		true
+	end
+
+let would_produce_recursive_anon field_acceptor field_donor =
+	try
+		(match !(field_acceptor.a_status) with
+		| Opened ->
+			PMap.iter (fun n field ->
+				match follow field.cf_type with
+				| TAnon a when field_acceptor == a -> raise Exit
+				| _ -> ()
+			) field_donor.a_fields;
+		| _ -> ());
+		false
+	with Exit -> true
+
+let link_dynamic a b = match follow a,follow b with
+	| TMono r,TDynamic _ -> Monomorph.bind r b
+	| TDynamic _,TMono r -> Monomorph.bind r a
+	| _ -> ()
+
+let fast_eq_check type_param_check a b =
+	if a == b then
+		true
+	else match a , b with
+	| TFun (l1,r1) , TFun (l2,r2) when List.length l1 = List.length l2 ->
+		List.for_all2 (fun (_,_,t1) (_,_,t2) -> type_param_check t1 t2) l1 l2 && type_param_check r1 r2
+	| TType (t1,l1), TType (t2,l2) ->
+		t1 == t2 && List.for_all2 type_param_check l1 l2
+	| TEnum (e1,l1), TEnum (e2,l2) ->
+		e1 == e2 && List.for_all2 type_param_check l1 l2
+	| TInst (c1,l1), TInst (c2,l2) ->
+		c1 == c2 && List.for_all2 type_param_check l1 l2
+	| TAbstract (a1,l1), TAbstract (a2,l2) ->
+		a1 == a2 && List.for_all2 type_param_check l1 l2
+	| _ , _ ->
+		false
+
+let rec fast_eq a b = fast_eq_check fast_eq a b
+
+let rec fast_eq_mono ml a b =
+	if fast_eq_check (fast_eq_mono ml) a b then
+		true
+	else match a , b with
+	| TMono _, _ ->
+		List.memq a ml
+	| _ , _ ->
+		false
+
+let rec shallow_eq a b =
+	a == b
+	|| begin
+		let a = follow a
+		and b = follow b in
+		fast_eq_check shallow_eq a b
+		|| match a , b with
+			| t, TMono { tm_type = None } when t == t_dynamic -> true
+			| TMono { tm_type = None }, t when t == t_dynamic -> true
+			| TMono { tm_type = None }, TMono { tm_type = None } -> true
+			| TAnon a1, TAnon a2 ->
+				let fields_eq() =
+					let rec loop fields1 fields2 =
+						match fields1, fields2 with
+						| [], [] -> true
+						| _, [] | [], _ -> false
+						| f1 :: rest1, f2 :: rest2 ->
+							f1.cf_name = f2.cf_name
+							&& (try shallow_eq f1.cf_type f2.cf_type with Not_found -> false)
+							&& loop rest1 rest2
+					in
+					let fields1 = PMap.fold (fun field fields -> field :: fields) a1.a_fields []
+					and fields2 = PMap.fold (fun field fields -> field :: fields) a2.a_fields []
+					and sort_compare f1 f2 = compare f1.cf_name f2.cf_name in
+					loop (List.sort sort_compare fields1) (List.sort sort_compare fields2)
+				in
+				(match !(a2.a_status), !(a1.a_status) with
+				| Statics c, Statics c2 -> c == c2
+				| EnumStatics e, EnumStatics e2 -> e == e2
+				| AbstractStatics a, AbstractStatics a2 -> a == a2
+				| Extend tl1, Extend tl2 -> fields_eq() && List.for_all2 shallow_eq tl1 tl2
+				| Closed, Closed -> fields_eq()
+				| Opened, Opened -> fields_eq()
+				| Const, Const -> fields_eq()
+				| _ -> false
+				)
+			| _ , _ ->
+				false
+	end
+
+(* perform unification with subtyping.
+   the first type is always the most down in the class hierarchy
+   it's also the one that is pointed by the position.
+   It's actually a typecheck of  A :> B where some mutations can happen *)
+
+let cannot_unify a b = Cannot_unify (a,b)
+let invalid_field n = Invalid_field_type n
+let invalid_kind n a b = Invalid_kind (n,a,b)
+let invalid_visibility n = Invalid_visibility n
+let has_no_field t n = Has_no_field (t,n)
+let has_extra_field t n = Has_extra_field (t,n)
+let has_meta m ml = List.exists (fun (m2,_,_) -> m = m2) ml
+let get_meta m ml = List.find (fun (m2,_,_) -> m = m2) ml
+
+(*
+	we can restrict access as soon as both are runtime-compatible
+*)
+let unify_access a1 a2 =
+	a1 = a2 || match a1, a2 with
+	| _, AccNo | _, AccNever -> true
+	| AccInline, AccNormal -> true
+	| _ -> false
+
+let direct_access = function
+	| AccNo | AccNever | AccNormal | AccInline | AccRequire _ | AccCtor -> true
+	| AccResolve | AccCall -> false
+
+let unify_kind k1 k2 =
+	k1 = k2 || match k1, k2 with
+		| Var v1, Var v2 -> unify_access v1.v_read v2.v_read && unify_access v1.v_write v2.v_write
+		| Var v, Method m ->
+			(match v.v_read, v.v_write, m with
+			| AccNormal, _, MethNormal -> true
+			| AccNormal, AccNormal, MethDynamic -> true
+			| _ -> false)
+		| Method m, Var v ->
+			(match m with
+			| MethDynamic -> direct_access v.v_read && direct_access v.v_write
+			| MethMacro -> false
+			| MethNormal | MethInline ->
+				match v.v_read,v.v_write with
+				| AccNormal,(AccNo | AccNever) -> true
+				| _ -> false)
+		| Method m1, Method m2 ->
+			match m1,m2 with
+			| MethInline, MethNormal
+			| MethDynamic, MethNormal -> true
+			| _ -> false
+
+type 'a rec_stack = {
+	mutable rec_stack : 'a list;
+}
+
+let new_rec_stack() = { rec_stack = [] }
+let rec_stack_exists f s = List.exists f s.rec_stack
+let rec_stack_memq v s = List.memq v s.rec_stack
+let rec_stack_loop stack value f arg =
+	stack.rec_stack <- value :: stack.rec_stack;
+	try
+		let r = f arg in
+		stack.rec_stack <- List.tl stack.rec_stack;
+		r
+	with e ->
+		stack.rec_stack <- List.tl stack.rec_stack;
+		raise e
+
+let eq_stack = new_rec_stack()
+
+let rec_stack stack value fcheck frun ferror =
+	if not (rec_stack_exists fcheck stack) then begin
+		try
+			stack.rec_stack <- value :: stack.rec_stack;
+			let v = frun() in
+			stack.rec_stack <- List.tl stack.rec_stack;
+			v
+		with
+			Unify_error l ->
+				stack.rec_stack <- List.tl stack.rec_stack;
+				ferror l
+			| e ->
+				stack.rec_stack <- List.tl stack.rec_stack;
+				raise e
+	end
+
+let rec_stack_default stack value fcheck frun def =
+	if not (rec_stack_exists fcheck stack) then rec_stack_loop stack value frun () else def
+
+let rec_stack_bool stack value fcheck frun =
+	if (rec_stack_exists fcheck stack) then false else begin
+		try
+			stack.rec_stack <- value :: stack.rec_stack;
+			frun();
+			stack.rec_stack <- List.tl stack.rec_stack;
+			true
+		with
+			Unify_error l ->
+				stack.rec_stack <- List.tl stack.rec_stack;
+				false
+			| e ->
+				stack.rec_stack <- List.tl stack.rec_stack;
+				raise e
+	end
+
+type eq_kind =
+	| EqStrict
+	| EqCoreType
+	| EqRightDynamic
+	| EqBothDynamic
+	| EqDoNotFollowNull (* like EqStrict, but does not follow Null<T> *)
+
+let rec type_eq param a b =
+	let can_follow t = match param with
+		| EqCoreType -> false
+		| EqDoNotFollowNull -> not (is_explicit_null t)
+		| _ -> true
+	in
+	if a == b then
+		()
+	else match a , b with
+	| TLazy f , _ -> type_eq param (lazy_type f) b
+	| _ , TLazy f -> type_eq param a (lazy_type f)
+	| TMono t , _ ->
+		(match t.tm_type with
+		| None -> if param = EqCoreType || not (link t a b) then error [cannot_unify a b]
+		| Some t -> type_eq param t b)
+	| _ , TMono t ->
+		(match t.tm_type with
+		| None -> if param = EqCoreType || not (link t b a) then error [cannot_unify a b]
+		| Some t -> type_eq param a t)
+	| TAbstract ({a_path=[],"Null"},[t1]),TAbstract ({a_path=[],"Null"},[t2]) ->
+		type_eq param t1 t2
+	| TAbstract ({a_path=[],"Null"},[t]),_ when param <> EqDoNotFollowNull ->
+		type_eq param t b
+	| _,TAbstract ({a_path=[],"Null"},[t]) when param <> EqDoNotFollowNull ->
+		type_eq param a t
+	| TType (t1,tl1), TType (t2,tl2) when (t1 == t2 || (param = EqCoreType && t1.t_path = t2.t_path)) && List.length tl1 = List.length tl2 ->
+		type_eq_params param a b tl1 tl2
+	| TType (t,tl) , _ when can_follow a ->
+		type_eq param (apply_params t.t_params tl t.t_type) b
+	| _ , TType (t,tl) when can_follow b ->
+		rec_stack eq_stack (a,b)
+			(fun (a2,b2) -> fast_eq a a2 && fast_eq b b2)
+			(fun() -> type_eq param a (apply_params t.t_params tl t.t_type))
+			(fun l -> error (cannot_unify a b :: l))
+	| TEnum (e1,tl1) , TEnum (e2,tl2) ->
+		if e1 != e2 && not (param = EqCoreType && e1.e_path = e2.e_path) then error [cannot_unify a b];
+		type_eq_params param a b tl1 tl2
+	| TInst (c1,tl1) , TInst (c2,tl2) ->
+		if c1 != c2 && not (param = EqCoreType && c1.cl_path = c2.cl_path) && (match c1.cl_kind, c2.cl_kind with KExpr _, KExpr _ -> false | _ -> true) then error [cannot_unify a b];
+		type_eq_params param a b tl1 tl2
+	| TFun (l1,r1) , TFun (l2,r2) when List.length l1 = List.length l2 ->
+		let i = ref 0 in
+		(try
+			type_eq param r1 r2;
+			List.iter2 (fun (n,o1,t1) (_,o2,t2) ->
+				incr i;
+				if o1 <> o2 then error [Not_matching_optional n];
+				type_eq param t1 t2
+			) l1 l2
+		with
+			Unify_error l ->
+				let msg = if !i = 0 then Invalid_return_type else Invalid_function_argument(!i,List.length l1) in
+				error (cannot_unify a b :: msg :: l)
+		)
+	| TDynamic a , TDynamic b ->
+		type_eq param a b
+	| TAbstract (a1,tl1) , TAbstract (a2,tl2) ->
+		if a1 != a2 && not (param = EqCoreType && a1.a_path = a2.a_path) then error [cannot_unify a b];
+		type_eq_params param a b tl1 tl2
+	| TAnon a1, TAnon a2 ->
+		(try
+			(match !(a2.a_status) with
+			| Statics c -> (match !(a1.a_status) with Statics c2 when c == c2 -> () | _ -> error [])
+			| EnumStatics e -> (match !(a1.a_status) with EnumStatics e2 when e == e2 -> () | _ -> error [])
+			| AbstractStatics a -> (match !(a1.a_status) with AbstractStatics a2 when a == a2 -> () | _ -> error [])
+			| _ -> ()
+			);
+			if would_produce_recursive_anon a1 a2 || would_produce_recursive_anon a2 a1 then error [cannot_unify a b];
+			PMap.iter (fun n f1 ->
+				try
+					let f2 = PMap.find n a2.a_fields in
+					if f1.cf_kind <> f2.cf_kind && (param = EqStrict || param = EqCoreType || not (unify_kind f1.cf_kind f2.cf_kind)) then error [invalid_kind n f1.cf_kind f2.cf_kind];
+					let a = f1.cf_type and b = f2.cf_type in
+					(try type_eq param a b with Unify_error l -> error (invalid_field n :: l));
+					if (has_class_field_flag f1 CfPublic) != (has_class_field_flag f2 CfPublic) then error [invalid_visibility n];
+				with
+					Not_found ->
+						if is_closed a2 then error [has_no_field b n];
+						if not (link (Monomorph.create()) b f1.cf_type) then error [cannot_unify a b];
+						a2.a_fields <- PMap.add n f1 a2.a_fields
+			) a1.a_fields;
+			PMap.iter (fun n f2 ->
+				if not (PMap.mem n a1.a_fields) then begin
+					if is_closed a1 then error [has_no_field a n];
+					if not (link (Monomorph.create()) a f2.cf_type) then error [cannot_unify a b];
+					a1.a_fields <- PMap.add n f2 a1.a_fields
+				end;
+			) a2.a_fields;
+		with
+			Unify_error l -> error (cannot_unify a b :: l))
+	| _ , _ ->
+		if b == t_dynamic && (param = EqRightDynamic || param = EqBothDynamic) then
+			()
+		else if a == t_dynamic && param = EqBothDynamic then
+			()
+		else
+			error [cannot_unify a b]
+
+and type_eq_params param a b tl1 tl2 =
+	let i = ref 0 in
+	List.iter2 (fun t1 t2 ->
+		incr i;
+		try
+			type_eq param t1 t2
+		with Unify_error l ->
+			let err = cannot_unify a b in
+			error (err :: (Invariant_parameter !i) :: l)
+		) tl1 tl2
+
+let type_iseq a b =
+	try
+		type_eq EqStrict a b;
+		true
+	with
+		Unify_error _ -> false
+
+let type_iseq_strict a b =
+	try
+		type_eq EqDoNotFollowNull a b;
+		true
+	with Unify_error _ ->
+		false
+
+let unify_stack = new_rec_stack()
+let abstract_cast_stack = new_rec_stack()
+let unify_new_monos = new_rec_stack()
+
+let print_stacks() =
+	let ctx = print_context() in
+	let st = s_type ctx in
+	print_endline "unify_stack";
+	List.iter (fun (a,b) -> Printf.printf "\t%s , %s\n" (st a) (st b)) unify_stack.rec_stack;
+	print_endline "monos";
+	List.iter (fun m -> print_endline ("\t" ^ st m)) unify_new_monos.rec_stack;
+	print_endline "abstract_cast_stack";
+	List.iter (fun (a,b) -> Printf.printf "\t%s , %s\n" (st a) (st b)) abstract_cast_stack.rec_stack
+
+let rec unify a b =
+	if a == b then
+		()
+	else match a, b with
+	| TLazy f , _ -> unify (lazy_type f) b
+	| _ , TLazy f -> unify a (lazy_type f)
+	| TMono t , _ ->
+		(match t.tm_type with
+		| None -> if not (link t a b) then error [cannot_unify a b]
+		| Some t -> unify t b)
+	| _ , TMono t ->
+		(match t.tm_type with
+		| None -> if not (link t b a) then error [cannot_unify a b]
+		| Some t -> unify a t)
+	| TType (t,tl) , _ ->
+		rec_stack unify_stack (a,b)
+			(fun(a2,b2) -> fast_eq a a2 && fast_eq b b2)
+			(fun() -> try_apply_params_rec t.t_params tl t.t_type (fun a -> unify a b))
+			(fun l -> error (cannot_unify a b :: l))
+	| _ , TType (t,tl) ->
+		rec_stack unify_stack (a,b)
+			(fun(a2,b2) -> fast_eq a a2 && fast_eq b b2)
+			(fun() -> try_apply_params_rec t.t_params tl t.t_type (unify a))
+			(fun l -> error (cannot_unify a b :: l))
+	| TEnum (ea,tl1) , TEnum (eb,tl2) ->
+		if ea != eb then error [cannot_unify a b];
+		unify_type_params a b tl1 tl2
+	| TAbstract ({a_path=[],"Null"},[t]),_ ->
+		begin try unify t b
+		with Unify_error l -> error (cannot_unify a b :: l) end
+	| _,TAbstract ({a_path=[],"Null"},[t]) ->
+		begin try unify a t
+		with Unify_error l -> error (cannot_unify a b :: l) end
+	| TAbstract (a1,tl1) , TAbstract (a2,tl2) when a1 == a2 ->
+		begin try
+			unify_type_params a b tl1 tl2
+		with Unify_error _ as err ->
+			(* the type could still have a from/to relation to itself (issue #3494) *)
+			begin try
+				unify_abstracts a b a1 tl1 a2 tl2
+			with Unify_error _ ->
+				raise err
+			end
+		end
+	| TAbstract ({a_path=[],"Void"},_) , _
+	| _ , TAbstract ({a_path=[],"Void"},_) ->
+		error [cannot_unify a b]
+	| TAbstract (a1,tl1) , TAbstract (a2,tl2) ->
+		unify_abstracts a b a1 tl1 a2 tl2
+	| TInst (c1,tl1) , TInst (c2,tl2) ->
+		let rec loop c tl =
+			if c == c2 then begin
+				unify_type_params a b tl tl2;
+				true
+			end else (match c.cl_super with
+				| None -> false
+				| Some (cs,tls) ->
+					loop cs (List.map (apply_params c.cl_params tl) tls)
+			) || List.exists (fun (cs,tls) ->
+				loop cs (List.map (apply_params c.cl_params tl) tls)
+			) c.cl_implements
+			|| (match c.cl_kind with
+			| KTypeParameter pl -> List.exists (fun t ->
+				match follow t with
+				| TInst (cs,tls) -> loop cs (List.map (apply_params c.cl_params tl) tls)
+				| TAbstract(aa,tl) -> List.exists (unify_to aa tl b) aa.a_to
+				| _ -> false
+			) pl
+			| _ -> false)
+		in
+		if not (loop c1 tl1) then error [cannot_unify a b]
+	| TFun (l1,r1) , TFun (l2,r2) when List.length l1 = List.length l2 ->
+		let i = ref 0 in
+		(try
+			(match follow r2 with
+			| TAbstract ({a_path=[],"Void"},_) -> incr i
+			| _ -> unify r1 r2; incr i);
+			List.iter2 (fun (_,o1,t1) (_,o2,t2) ->
+				if o1 && not o2 then error [Cant_force_optional];
+				unify t1 t2;
+				incr i
+			) l2 l1 (* contravariance *)
+		with
+			Unify_error l ->
+				let msg = if !i = 0 then Invalid_return_type else Invalid_function_argument(!i,List.length l1) in
+				error (cannot_unify a b :: msg :: l))
+	| TInst (c,tl) , TAnon an ->
+		if PMap.is_empty an.a_fields then (match c.cl_kind with
+			| KTypeParameter pl ->
+				(* one of the constraints must unify with { } *)
+				if not (List.exists (fun t -> match follow t with TInst _ | TAnon _ -> true | _ -> false) pl) then error [cannot_unify a b]
+			| _ -> ());
+		(try
+			PMap.iter (fun n f2 ->
+				(*
+					introducing monomorphs while unifying might create infinite loops - see #2315
+					let's store these monomorphs and make sure we reach a fixed point
+				*)
+				let monos = ref [] in
+				let make_type f =
+					match f.cf_params with
+					| [] -> f.cf_type
+					| l ->
+						let ml = List.map (fun _ -> mk_mono()) l in
+						monos := ml;
+						apply_params f.cf_params ml f.cf_type
+				in
+				let _, ft, f1 = (try raw_class_field make_type c tl n with Not_found -> error [has_no_field a n]) in
+				let ft = apply_params c.cl_params tl ft in
+				if not (unify_kind f1.cf_kind f2.cf_kind) then error [invalid_kind n f1.cf_kind f2.cf_kind];
+				if (has_class_field_flag f2 CfPublic) && not (has_class_field_flag f1 CfPublic) then error [invalid_visibility n];
+
+				(match f2.cf_kind with
+				| Var { v_read = AccNo } | Var { v_read = AccNever } ->
+					(* we will do a recursive unification, so let's check for possible recursion *)
+					let old_monos = unify_new_monos.rec_stack in
+					unify_new_monos.rec_stack <- !monos @ unify_new_monos.rec_stack;
+					rec_stack unify_stack (ft,f2.cf_type)
+						(fun (a2,b2) -> fast_eq b2 f2.cf_type && fast_eq_mono unify_new_monos.rec_stack ft a2)
+						(fun() -> try unify_with_access f1 ft f2 with e -> unify_new_monos.rec_stack <- old_monos; raise e)
+						(fun l -> error (invalid_field n :: l));
+					unify_new_monos.rec_stack <- old_monos;
+				| Method MethNormal | Method MethInline | Var { v_write = AccNo } | Var { v_write = AccNever } ->
+					(* same as before, but unification is reversed (read-only var) *)
+					let old_monos = unify_new_monos.rec_stack in
+					unify_new_monos.rec_stack <- !monos @ unify_new_monos.rec_stack;
+					rec_stack unify_stack (f2.cf_type,ft)
+						(fun(a2,b2) -> fast_eq_mono unify_new_monos.rec_stack b2 ft && fast_eq f2.cf_type a2)
+						(fun() -> try unify_with_access f1 ft f2 with e -> unify_new_monos.rec_stack <- old_monos; raise e)
+						(fun l -> error (invalid_field n :: l));
+					unify_new_monos.rec_stack <- old_monos;
+				| _ ->
+					(* will use fast_eq, which have its own stack *)
+					try
+						unify_with_access f1 ft f2
+					with
+						Unify_error l ->
+							error (invalid_field n :: l));
+
+				List.iter (fun f2o ->
+					if not (List.exists (fun f1o -> type_iseq f1o.cf_type f2o.cf_type) (f1 :: f1.cf_overloads))
+					then error [Missing_overload (f1, f2o.cf_type)]
+				) f2.cf_overloads;
+				(* we mark the field as :?used because it might be used through the structure *)
+				if not (Meta.has Meta.MaybeUsed f1.cf_meta) then begin
+					f1.cf_meta <- (Meta.MaybeUsed,[],f1.cf_pos) :: f1.cf_meta;
+					match f2.cf_kind with
+					| Var vk ->
+						let check name =
+							try
+								let _,_,cf = raw_class_field make_type c tl name in
+								if not (Meta.has Meta.MaybeUsed cf.cf_meta) then
+									cf.cf_meta <- (Meta.MaybeUsed,[],f1.cf_pos) :: cf.cf_meta
+							with Not_found ->
+								()
+						in
+						(match vk.v_read with AccCall -> check ("get_" ^ f1.cf_name) | _ -> ());
+						(match vk.v_write with AccCall -> check ("set_" ^ f1.cf_name) | _ -> ());
+					| _ -> ()
+				end;
+				(match f1.cf_kind with
+				| Method MethInline ->
+					if (c.cl_extern || has_class_field_flag f1 CfExtern) && not (Meta.has Meta.Runtime f1.cf_meta) then error [Has_no_runtime_field (a,n)];
+				| _ -> ());
+			) an.a_fields;
+			(match !(an.a_status) with
+			| Opened -> an.a_status := Closed;
+			| Statics _ | EnumStatics _ | AbstractStatics _ -> error []
+			| Closed | Extend _ | Const -> ())
+		with
+			Unify_error l -> error (cannot_unify a b :: l))
+	| TAnon a1, TAnon a2 ->
+		unify_anons a b a1 a2
+	| TAnon an, TAbstract ({ a_path = [],"Class" },[pt]) ->
+		(match !(an.a_status) with
+		| Statics cl -> unify (TInst (cl,List.map (fun _ -> mk_mono()) cl.cl_params)) pt
+		| _ -> error [cannot_unify a b])
+	| TAnon an, TAbstract ({ a_path = [],"Enum" },[pt]) ->
+		(match !(an.a_status) with
+		| EnumStatics e -> unify (TEnum (e,List.map (fun _ -> mk_mono()) e.e_params)) pt
+		| _ -> error [cannot_unify a b])
+	| TEnum _, TAbstract ({ a_path = [],"EnumValue" },[]) ->
+		()
+	| TEnum(en,_), TAbstract ({ a_path = ["haxe"],"FlatEnum" },[]) when Meta.has Meta.FlatEnum en.e_meta ->
+		()
+	| TFun _, TAbstract ({ a_path = ["haxe"],"Function" },[]) ->
+		()
+	| TInst(c,tl),TAbstract({a_path = ["haxe"],"Constructible"},[t1]) ->
+		begin try
+			begin match c.cl_kind with
+				| KTypeParameter tl ->
+					(* type parameters require an equal Constructible constraint *)
+					if not (List.exists (fun t -> match follow t with TAbstract({a_path = ["haxe"],"Constructible"},[t2]) -> type_iseq t1 t2 | _ -> false) tl) then error [cannot_unify a b]
+				| _ ->
+					let _,t,cf = class_field c tl "new" in
+					if not (has_class_field_flag cf CfPublic) then error [invalid_visibility "new"];
+					begin try unify t t1
+					with Unify_error l -> error (cannot_unify a b :: l) end
+			end
+		with Not_found ->
+			error [has_no_field a "new"]
+		end
+	| TDynamic t , _ ->
+		if t == a then
+			()
+		else (match b with
+		| TDynamic t2 ->
+			if t2 != b then
+				(try
+					type_eq EqRightDynamic t t2
+				with
+					Unify_error l -> error (cannot_unify a b :: l));
+		| TAbstract(bb,tl) when (List.exists (unify_from bb tl a b) bb.a_from) ->
+			()
+		| _ ->
+			error [cannot_unify a b])
+	| _ , TDynamic t ->
+		if t == b then
+			()
+		else (match a with
+		| TDynamic t2 ->
+			if t2 != a then
+				(try
+					type_eq EqRightDynamic t t2
+				with
+					Unify_error l -> error (cannot_unify a b :: l));
+		| TAnon an ->
+			(try
+				(match !(an.a_status) with
+				| Statics _ | EnumStatics _ -> error []
+				| Opened -> an.a_status := Closed
+				| _ -> ());
+				PMap.iter (fun _ f ->
+					try
+						type_eq EqStrict (field_type f) t
+					with Unify_error l ->
+						error (invalid_field f.cf_name :: l)
+				) an.a_fields
+			with Unify_error l ->
+				error (cannot_unify a b :: l))
+		| TAbstract(aa,tl) when (List.exists (unify_to aa tl b) aa.a_to) ->
+			()
+		| _ ->
+			error [cannot_unify a b])
+	| TAbstract (aa,tl), _  ->
+		if not (List.exists (unify_to aa tl b) aa.a_to) then error [cannot_unify a b];
+	| TInst ({ cl_kind = KTypeParameter ctl } as c,pl), TAbstract (bb,tl) ->
+		(* one of the constraints must satisfy the abstract *)
+		if not (List.exists (fun t ->
+			let t = apply_params c.cl_params pl t in
+			try unify t b; true with Unify_error _ -> false
+		) ctl) && not (List.exists (unify_from bb tl a b) bb.a_from) then error [cannot_unify a b];
+	| _, TAbstract (bb,tl) ->
+		if not (List.exists (unify_from bb tl a b) bb.a_from) then error [cannot_unify a b]
+	| _ , _ ->
+		error [cannot_unify a b]
+
+and unify_abstracts a b a1 tl1 a2 tl2 =
+	let f1 = unify_to a1 tl1 b in
+		let f2 = unify_from a2 tl2 a b in
+		if (List.exists (f1 ~allow_transitive_cast:false) a1.a_to)
+		|| (List.exists (f2 ~allow_transitive_cast:false) a2.a_from)
+		|| (((Meta.has Meta.CoreType a1.a_meta) || (Meta.has Meta.CoreType a2.a_meta))
+			&& ((List.exists f1 a1.a_to) || (List.exists f2 a2.a_from))) then
+			()
+		else
+			error [cannot_unify a b]
+
+and unify_anons a b a1 a2 =
+	if would_produce_recursive_anon a1 a2 then error [cannot_unify a b];
+	(try
+		PMap.iter (fun n f2 ->
+		try
+			let f1 = PMap.find n a1.a_fields in
+			if not (unify_kind f1.cf_kind f2.cf_kind) then
+				(match !(a1.a_status), f1.cf_kind, f2.cf_kind with
+				| Opened, Var { v_read = AccNormal; v_write = AccNo }, Var { v_read = AccNormal; v_write = AccNormal } ->
+					f1.cf_kind <- f2.cf_kind;
+				| _ -> error [invalid_kind n f1.cf_kind f2.cf_kind]);
+			if (has_class_field_flag f2 CfPublic) && not (has_class_field_flag f1 CfPublic) then error [invalid_visibility n];
+			try
+				let f1_type =
+					if fast_eq f1.cf_type f2.cf_type then f1.cf_type
+					else field_type f1
+				in
+				unify_with_access f1 f1_type f2;
+				(match !(a1.a_status) with
+				| Statics c when not (Meta.has Meta.MaybeUsed f1.cf_meta) -> f1.cf_meta <- (Meta.MaybeUsed,[],f1.cf_pos) :: f1.cf_meta
+				| _ -> ());
+			with
+				Unify_error l -> error (invalid_field n :: l)
+		with
+			Not_found ->
+				match !(a1.a_status) with
+				| Opened ->
+					if not (link (Monomorph.create()) a f2.cf_type) then error [];
+					a1.a_fields <- PMap.add n f2 a1.a_fields
+				| Const when Meta.has Meta.Optional f2.cf_meta ->
+					()
+				| _ ->
+					error [has_no_field a n];
+		) a2.a_fields;
+		(match !(a1.a_status) with
+		| Const when not (PMap.is_empty a2.a_fields) ->
+			PMap.iter (fun n _ -> if not (PMap.mem n a2.a_fields) then error [has_extra_field a n]) a1.a_fields;
+		| Opened ->
+			a1.a_status := Closed
+		| _ -> ());
+		(match !(a2.a_status) with
+		| Statics c -> (match !(a1.a_status) with Statics c2 when c == c2 -> () | _ -> error [])
+		| EnumStatics e -> (match !(a1.a_status) with EnumStatics e2 when e == e2 -> () | _ -> error [])
+		| AbstractStatics a -> (match !(a1.a_status) with AbstractStatics a2 when a == a2 -> () | _ -> error [])
+		| Opened -> a2.a_status := Closed
+		| Const | Extend _ | Closed -> ())
+	with
+		Unify_error l -> error (cannot_unify a b :: l))
+
+and unify_from ab tl a b ?(allow_transitive_cast=true) t =
+	rec_stack_bool abstract_cast_stack (a,b)
+		(fun (a2,b2) -> fast_eq a a2 && fast_eq b b2)
+		(fun() ->
+			let t = apply_params ab.a_params tl t in
+			let unify_func = if allow_transitive_cast then unify else type_eq EqRightDynamic in
+			unify_func a t)
+
+and unify_to ab tl b ?(allow_transitive_cast=true) t =
+	let t = apply_params ab.a_params tl t in
+	let unify_func = if allow_transitive_cast then unify else type_eq EqStrict in
+	try
+		unify_func t b;
+		true
+	with Unify_error _ ->
+		false
+
+and unify_from_field ab tl a b ?(allow_transitive_cast=true) (t,cf) =
+	rec_stack_bool abstract_cast_stack (a,b)
+		(fun (a2,b2) -> fast_eq a a2 && fast_eq b b2)
+		(fun() ->
+			let unify_func = if allow_transitive_cast then unify else type_eq EqStrict in
+			match follow cf.cf_type with
+			| TFun(_,r) ->
+				let monos = List.map (fun _ -> mk_mono()) cf.cf_params in
+				let map t = apply_params ab.a_params tl (apply_params cf.cf_params monos t) in
+				unify_func a (map t);
+				List.iter2 (fun m (name,t) -> match follow t with
+					| TInst ({ cl_kind = KTypeParameter constr },_) when constr <> [] ->
+						List.iter (fun tc -> match follow m with TMono _ -> raise (Unify_error []) | _ -> unify m (map tc) ) constr
+					| _ -> ()
+				) monos cf.cf_params;
+				unify_func (map r) b;
+				true
+			| _ -> assert false)
+
+and unify_to_field ab tl b ?(allow_transitive_cast=true) (t,cf) =
+	let a = TAbstract(ab,tl) in
+	rec_stack_bool abstract_cast_stack (b,a)
+		(fun (b2,a2) -> fast_eq a a2 && fast_eq b b2)
+		(fun() ->
+			let unify_func = if allow_transitive_cast then unify else type_eq EqStrict in
+			match follow cf.cf_type with
+			| TFun((_,_,ta) :: _,_) ->
+				let monos = List.map (fun _ -> mk_mono()) cf.cf_params in
+				let map t = apply_params ab.a_params tl (apply_params cf.cf_params monos t) in
+				let athis = map ab.a_this in
+				(* we cannot allow implicit casts when the this type is not completely known yet *)
+				(* if has_mono athis then raise (Unify_error []); *)
+				with_variance (type_eq EqStrict) athis (map ta);
+				(* immediate constraints checking is ok here because we know there are no monomorphs *)
+				List.iter2 (fun m (name,t) -> match follow t with
+					| TInst ({ cl_kind = KTypeParameter constr },_) when constr <> [] ->
+						List.iter (fun tc -> match follow m with TMono _ -> raise (Unify_error []) | _ -> unify m (map tc) ) constr
+					| _ -> ()
+				) monos cf.cf_params;
+				unify_func (map t) b;
+			| _ -> assert false)
+
+and unify_with_variance f t1 t2 =
+	let allows_variance_to t tf = type_iseq tf t in
+	match follow t1,follow t2 with
+	| TInst(c1,tl1),TInst(c2,tl2) when c1 == c2 ->
+		List.iter2 f tl1 tl2
+	| TEnum(en1,tl1),TEnum(en2,tl2) when en1 == en2 ->
+		List.iter2 f tl1 tl2
+	| TAbstract(a1,tl1),TAbstract(a2,tl2) when a1 == a2 && Meta.has Meta.CoreType a1.a_meta ->
+		List.iter2 f tl1 tl2
+	| TAbstract(a1,pl1),TAbstract(a2,pl2) ->
+		if (Meta.has Meta.CoreType a1.a_meta) && (Meta.has Meta.CoreType a2.a_meta) then begin
+			let ta1 = apply_params a1.a_params pl1 a1.a_this in
+			let ta2 = apply_params a2.a_params pl2 a2.a_this in
+			type_eq EqStrict ta1 ta2;
+		end;
+		if not (List.exists (allows_variance_to t2) a1.a_to) && not (List.exists (allows_variance_to t1) a2.a_from) then
+			error [cannot_unify t1 t2]
+	| TAbstract(a,pl),t ->
+		type_eq EqBothDynamic (apply_params a.a_params pl a.a_this) t;
+		if not (List.exists (fun t2 -> allows_variance_to t (apply_params a.a_params pl t2)) a.a_to) then error [cannot_unify t1 t2]
+	| t,TAbstract(a,pl) ->
+		type_eq EqBothDynamic t (apply_params a.a_params pl a.a_this);
+		if not (List.exists (fun t2 -> allows_variance_to t (apply_params a.a_params pl t2)) a.a_from) then error [cannot_unify t1 t2]
+	| (TAnon a1 as t1), (TAnon a2 as t2) ->
+		rec_stack unify_stack (t1,t2)
+			(fun (a,b) -> fast_eq a t1 && fast_eq b t2)
+			(fun() -> unify_anons t1 t2 a1 a2)
+			(fun l -> error l)
+	| _ ->
+		error [cannot_unify t1 t2]
+
+and unify_type_params a b tl1 tl2 =
+	let i = ref 0 in
+	List.iter2 (fun t1 t2 ->
+		incr i;
+		try
+			with_variance (type_eq EqRightDynamic) t1 t2
+		with Unify_error l ->
+			let err = cannot_unify a b in
+			error (err :: (Invariant_parameter !i) :: l)
+	) tl1 tl2
+
+and with_variance f t1 t2 =
+	try
+		f t1 t2
+	with Unify_error l -> try
+		unify_with_variance (with_variance f) t1 t2
+	with Unify_error _ ->
+		raise (Unify_error l)
+
+and unify_with_access f1 t1 f2 =
+	match f2.cf_kind with
+	(* write only *)
+	| Var { v_read = AccNo } | Var { v_read = AccNever } -> unify f2.cf_type t1
+	(* read only *)
+	| Method MethNormal | Method MethInline | Var { v_write = AccNo } | Var { v_write = AccNever } ->
+		if (has_class_field_flag f1 CfFinal) <> (has_class_field_flag f2 CfFinal) then raise (Unify_error [FinalInvariance]);
+		unify t1 f2.cf_type
+	(* read/write *)
+	| _ -> with_variance (type_eq EqBothDynamic) t1 f2.cf_type
+
+let does_unify a b =
+	try
+		unify a b;
+		true
+	with Unify_error _ ->
+		false
+;;
+unify_ref := unify;;

+ 251 - 4
src/core/texpr.ml

@@ -1,8 +1,255 @@
 open Globals
 open Ast
-open Type
+open TType
+open TFunctions
+open TUnification
+open TPrinting
 open Error
 
+let iter f e =
+	match e.eexpr with
+	| TConst _
+	| TLocal _
+	| TBreak
+	| TContinue
+	| TTypeExpr _
+	| TIdent _ ->
+		()
+	| TArray (e1,e2)
+	| TBinop (_,e1,e2)
+	| TFor (_,e1,e2)
+	| TWhile (e1,e2,_) ->
+		f e1;
+		f e2;
+	| TThrow e
+	| TField (e,_)
+	| TEnumParameter (e,_,_)
+	| TEnumIndex e
+	| TParenthesis e
+	| TCast (e,_)
+	| TUnop (_,_,e)
+	| TMeta(_,e) ->
+		f e
+	| TArrayDecl el
+	| TNew (_,_,el)
+	| TBlock el ->
+		List.iter f el
+	| TObjectDecl fl ->
+		List.iter (fun (_,e) -> f e) fl
+	| TCall (e,el) ->
+		f e;
+		List.iter f el
+	| TVar (v,eo) ->
+		(match eo with None -> () | Some e -> f e)
+	| TFunction fu ->
+		f fu.tf_expr
+	| TIf (e,e1,e2) ->
+		f e;
+		f e1;
+		(match e2 with None -> () | Some e -> f e)
+	| TSwitch (e,cases,def) ->
+		f e;
+		List.iter (fun (el,e2) -> List.iter f el; f e2) cases;
+		(match def with None -> () | Some e -> f e)
+	| TTry (e,catches) ->
+		f e;
+		List.iter (fun (_,e) -> f e) catches
+	| TReturn eo ->
+		(match eo with None -> () | Some e -> f e)
+
+(**
+	Returns `true` if `predicate` is evaluated to `true` for at least one of sub-expressions.
+	Returns `false` otherwise.
+	Does not evaluate `predicate` for the `e` expression.
+*)
+let check_expr predicate e =
+	match e.eexpr with
+		| TConst _ | TLocal _ | TBreak | TContinue | TTypeExpr _ | TIdent _ ->
+			false
+		| TArray (e1,e2) | TBinop (_,e1,e2) | TFor (_,e1,e2) | TWhile (e1,e2,_) ->
+			predicate e1 || predicate e2;
+		| TThrow e | TField (e,_) | TEnumParameter (e,_,_) | TEnumIndex e | TParenthesis e
+		| TCast (e,_) | TUnop (_,_,e) | TMeta(_,e) ->
+			predicate e
+		| TArrayDecl el | TNew (_,_,el) | TBlock el ->
+			List.exists predicate el
+		| TObjectDecl fl ->
+			List.exists (fun (_,e) -> predicate e) fl
+		| TCall (e,el) ->
+			predicate e ||  List.exists predicate el
+		| TVar (_,eo) | TReturn eo ->
+			(match eo with None -> false | Some e -> predicate e)
+		| TFunction fu ->
+			predicate fu.tf_expr
+		| TIf (e,e1,e2) ->
+			predicate e || predicate e1 || (match e2 with None -> false | Some e -> predicate e)
+		| TSwitch (e,cases,def) ->
+			predicate e
+			|| List.exists (fun (el,e2) -> List.exists predicate el || predicate e2) cases
+			|| (match def with None -> false | Some e -> predicate e)
+		| TTry (e,catches) ->
+			predicate e || List.exists (fun (_,e) -> predicate e) catches
+
+let map_expr f e =
+	match e.eexpr with
+	| TConst _
+	| TLocal _
+	| TBreak
+	| TContinue
+	| TTypeExpr _
+	| TIdent _ ->
+		e
+	| TArray (e1,e2) ->
+		let e1 = f e1 in
+		{ e with eexpr = TArray (e1,f e2) }
+	| TBinop (op,e1,e2) ->
+		let e1 = f e1 in
+		{ e with eexpr = TBinop (op,e1,f e2) }
+	| TFor (v,e1,e2) ->
+		let e1 = f e1 in
+		{ e with eexpr = TFor (v,e1,f e2) }
+	| TWhile (e1,e2,flag) ->
+		let e1 = f e1 in
+		{ e with eexpr = TWhile (e1,f e2,flag) }
+	| TThrow e1 ->
+		{ e with eexpr = TThrow (f e1) }
+	| TEnumParameter (e1,ef,i) ->
+		{ e with eexpr = TEnumParameter(f e1,ef,i) }
+	| TEnumIndex e1 ->
+		{ e with eexpr = TEnumIndex (f e1) }
+	| TField (e1,v) ->
+		{ e with eexpr = TField (f e1,v) }
+	| TParenthesis e1 ->
+		{ e with eexpr = TParenthesis (f e1) }
+	| TUnop (op,pre,e1) ->
+		{ e with eexpr = TUnop (op,pre,f e1) }
+	| TArrayDecl el ->
+		{ e with eexpr = TArrayDecl (List.map f el) }
+	| TNew (t,pl,el) ->
+		{ e with eexpr = TNew (t,pl,List.map f el) }
+	| TBlock el ->
+		{ e with eexpr = TBlock (List.map f el) }
+	| TObjectDecl el ->
+		{ e with eexpr = TObjectDecl (List.map (fun (v,e) -> v, f e) el) }
+	| TCall (e1,el) ->
+		let e1 = f e1 in
+		{ e with eexpr = TCall (e1, List.map f el) }
+	| TVar (v,eo) ->
+		{ e with eexpr = TVar (v, match eo with None -> None | Some e -> Some (f e)) }
+	| TFunction fu ->
+		{ e with eexpr = TFunction { fu with tf_expr = f fu.tf_expr } }
+	| TIf (ec,e1,e2) ->
+		let ec = f ec in
+		let e1 = f e1 in
+		{ e with eexpr = TIf (ec,e1,match e2 with None -> None | Some e -> Some (f e)) }
+	| TSwitch (e1,cases,def) ->
+		let e1 = f e1 in
+		let cases = List.map (fun (el,e2) -> List.map f el, f e2) cases in
+		{ e with eexpr = TSwitch (e1, cases, match def with None -> None | Some e -> Some (f e)) }
+	| TTry (e1,catches) ->
+		let e1 = f e1 in
+		{ e with eexpr = TTry (e1, List.map (fun (v,e) -> v, f e) catches) }
+	| TReturn eo ->
+		{ e with eexpr = TReturn (match eo with None -> None | Some e -> Some (f e)) }
+	| TCast (e1,t) ->
+		{ e with eexpr = TCast (f e1,t) }
+	| TMeta (m,e1) ->
+		 {e with eexpr = TMeta(m,f e1)}
+
+let map_expr_type f ft fv e =
+	match e.eexpr with
+	| TConst _
+	| TBreak
+	| TContinue
+	| TTypeExpr _
+	| TIdent _ ->
+		{ e with etype = ft e.etype }
+	| TLocal v ->
+		{ e with eexpr = TLocal (fv v); etype = ft e.etype }
+	| TArray (e1,e2) ->
+		let e1 = f e1 in
+		{ e with eexpr = TArray (e1,f e2); etype = ft e.etype }
+	| TBinop (op,e1,e2) ->
+		let e1 = f e1 in
+		{ e with eexpr = TBinop (op,e1,f e2); etype = ft e.etype }
+	| TFor (v,e1,e2) ->
+		let v = fv v in
+		let e1 = f e1 in
+		{ e with eexpr = TFor (v,e1,f e2); etype = ft e.etype }
+	| TWhile (e1,e2,flag) ->
+		let e1 = f e1 in
+		{ e with eexpr = TWhile (e1,f e2,flag); etype = ft e.etype }
+	| TThrow e1 ->
+		{ e with eexpr = TThrow (f e1); etype = ft e.etype }
+	| TEnumParameter (e1,ef,i) ->
+		{ e with eexpr = TEnumParameter (f e1,ef,i); etype = ft e.etype }
+	| TEnumIndex e1 ->
+		{ e with eexpr = TEnumIndex (f e1); etype = ft e.etype }
+	| TField (e1,v) ->
+		let e1 = f e1 in
+		let v = try
+			let n = match v with
+				| FClosure _ -> raise Not_found
+				| FAnon f | FInstance (_,_,f) | FStatic (_,f) -> f.cf_name
+				| FEnum (_,f) -> f.ef_name
+				| FDynamic n -> n
+			in
+			quick_field e1.etype n
+		with Not_found ->
+			v
+		in
+		{ e with eexpr = TField (e1,v); etype = ft e.etype }
+	| TParenthesis e1 ->
+		{ e with eexpr = TParenthesis (f e1); etype = ft e.etype }
+	| TUnop (op,pre,e1) ->
+		{ e with eexpr = TUnop (op,pre,f e1); etype = ft e.etype }
+	| TArrayDecl el ->
+		{ e with eexpr = TArrayDecl (List.map f el); etype = ft e.etype }
+	| TNew (c,pl,el) ->
+		let et = ft e.etype in
+		(* make sure that we use the class corresponding to the replaced type *)
+		let t = match c.cl_kind with
+			| KTypeParameter _ | KGeneric ->
+				et
+			| _ ->
+				ft (TInst(c,pl))
+		in
+		let c, pl = (match follow t with TInst (c,pl) -> (c,pl) | TAbstract({a_impl = Some c},pl) -> c,pl | t -> TUnification.error [has_no_field t "new"]) in
+		{ e with eexpr = TNew (c,pl,List.map f el); etype = et }
+	| TBlock el ->
+		{ e with eexpr = TBlock (List.map f el); etype = ft e.etype }
+	| TObjectDecl el ->
+		{ e with eexpr = TObjectDecl (List.map (fun (v,e) -> v, f e) el); etype = ft e.etype }
+	| TCall (e1,el) ->
+		let e1 = f e1 in
+		{ e with eexpr = TCall (e1, List.map f el); etype = ft e.etype }
+	| TVar (v,eo) ->
+		{ e with eexpr = TVar (fv v, match eo with None -> None | Some e -> Some (f e)); etype = ft e.etype }
+	| TFunction fu ->
+		let fu = {
+			tf_expr = f fu.tf_expr;
+			tf_args = List.map (fun (v,o) -> fv v, o) fu.tf_args;
+			tf_type = ft fu.tf_type;
+		} in
+		{ e with eexpr = TFunction fu; etype = ft e.etype }
+	| TIf (ec,e1,e2) ->
+		let ec = f ec in
+		let e1 = f e1 in
+		{ e with eexpr = TIf (ec,e1,match e2 with None -> None | Some e -> Some (f e)); etype = ft e.etype }
+	| TSwitch (e1,cases,def) ->
+		let e1 = f e1 in
+		let cases = List.map (fun (el,e2) -> List.map f el, f e2) cases in
+		{ e with eexpr = TSwitch (e1, cases, match def with None -> None | Some e -> Some (f e)); etype = ft e.etype }
+	| TTry (e1,catches) ->
+		let e1 = f e1 in
+		{ e with eexpr = TTry (e1, List.map (fun (v,e) -> fv v, f e) catches); etype = ft e.etype }
+	| TReturn eo ->
+		{ e with eexpr = TReturn (match eo with None -> None | Some e -> Some (f e)); etype = ft e.etype }
+	| TCast (e1,t) ->
+		{ e with eexpr = TCast (f e1,t); etype = ft e.etype }
+	| TMeta (m,e1) ->
+		{e with eexpr = TMeta(m, f e1); etype = ft e.etype }
+
 let equal_fa fa1 fa2 = match fa1,fa2 with
 	| FStatic(c1,cf1),FStatic(c2,cf2) -> c1 == c2 && cf1.cf_name == cf2.cf_name
 	| FInstance(c1,tl1,cf1),FInstance(c2,tl2,cf2) -> c1 == c2 && safe_for_all2 type_iseq tl1 tl2 && cf1.cf_name == cf2.cf_name
@@ -302,7 +549,7 @@ let rec constructor_side_effects e =
 	| TParenthesis _ | TTypeExpr _ | TLocal _ | TMeta _
 	| TConst _ | TContinue | TBreak | TCast _ | TIdent _ ->
 		try
-			Type.iter (fun e -> if constructor_side_effects e then raise Exit) e;
+			iter (fun e -> if constructor_side_effects e then raise Exit) e;
 			false;
 		with Exit ->
 			true
@@ -343,7 +590,7 @@ let for_remap basic v e1 e2 p =
 	let enext = mk (TField(ev',quick_field t1 "next")) (tfun [] v.v_type) e1.epos in
 	let enext = mk (TCall(enext,[])) v.v_type e1.epos in
 	let eassign = mk (TVar(v,Some enext)) basic.tvoid p in
-	let ebody = Type.concat eassign e2 in
+	let ebody = concat eassign e2 in
 	mk (TBlock [
 		mk (TVar (v',Some e1)) basic.tvoid e1.epos;
 		mk (TWhile((mk (TParenthesis ehasnext) ehasnext.etype ehasnext.epos),ebody,NormalWhile)) basic.tvoid e1.epos;
@@ -529,7 +776,7 @@ let collect_captured_vars e =
 				loop e;
 			) catches
 		| _ ->
-			Type.iter loop e
+			iter loop e
 	in
 	loop e;
 	List.rev !unknown,!accesses_this

+ 4 - 0
src/core/timer.ml

@@ -77,6 +77,10 @@ let timer id =
 	) else
 		(fun() -> ())
 
+let current_id() =
+	match !curtime with
+	| [] -> None
+	| t :: _ -> Some t.id
 
 let rec close_times() =
 	let now = get_time() in

+ 6 - 3250
src/core/type.ml

@@ -17,3257 +17,13 @@
 	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  *)
 
-open Ast
-open Globals
+include TType
+include TFunctions
+include TPrinting
+include TUnification
+include Texpr
+include TOther
 
-type field_kind =
-	| Var of var_kind
-	| Method of method_kind
-
-and var_kind = {
-	v_read : var_access;
-	v_write : var_access;
-}
-
-and var_access =
-	| AccNormal
-	| AccNo             (* can't be accessed outside of the class itself and its subclasses *)
-	| AccNever          (* can't be accessed, even in subclasses *)
-	| AccCtor           (* can only be accessed from the constructor *)
-	| AccResolve        (* call resolve("field") when accessed *)
-	| AccCall           (* perform a method call when accessed *)
-	| AccInline         (* similar to Normal but inline when accessed *)
-	| AccRequire of string * string option (* set when @:require(cond) fails *)
-
-and method_kind =
-	| MethNormal
-	| MethInline
-	| MethDynamic
-	| MethMacro
-
-type module_check_policy =
-	| NoCheckFileTimeModification
-	| CheckFileContentModification
-	| NoCheckDependencies
-	| NoCheckShadowing
-
-type t =
-	| TMono of tmono
-	| TEnum of tenum * tparams
-	| TInst of tclass * tparams
-	| TType of tdef * tparams
-	| TFun of tsignature
-	| TAnon of tanon
-	| TDynamic of t
-	| TLazy of tlazy ref
-	| TAbstract of tabstract * tparams
-
-and tmono_constraint =
-	| CStructure of t * tanon
-	| CTypes of t list
-
-and tmono = {
-	mutable tm_type : t option;
-	mutable tm_constraint : (tmono_constraint * string * pos) option;
-}
-
-and tlazy =
-	| LAvailable of t
-	| LProcessing of (unit -> t)
-	| LWait of (unit -> t)
-
-and tsignature = (string * bool * t) list * t
-
-and tparams = t list
-
-and type_params = (string * t) list
-
-and tconstant =
-	| TInt of int32
-	| TFloat of string
-	| TString of string
-	| TBool of bool
-	| TNull
-	| TThis
-	| TSuper
-
-and tvar_extra = (type_params * texpr option) option
-
-and tvar_origin =
-	| TVOLocalVariable
-	| TVOArgument
-	| TVOForVariable
-	| TVOPatternVariable
-	| TVOCatchVariable
-	| TVOLocalFunction
-
-and tvar_kind =
-	| VUser of tvar_origin
-	| VGenerated
-	| VInlined
-	| VInlinedConstructorVariable
-	| VExtractorVariable
-
-and tvar = {
-	mutable v_id : int;
-	mutable v_name : string;
-	mutable v_type : t;
-	mutable v_kind : tvar_kind;
-	mutable v_capture : bool;
-	mutable v_final : bool;
-	mutable v_extra : tvar_extra;
-	mutable v_meta : metadata;
-	v_pos : pos;
-}
-
-and tfunc = {
-	tf_args : (tvar * texpr option) list;
-	tf_type : t;
-	tf_expr : texpr;
-}
-
-and anon_status =
-	| Closed
-	| Opened
-	| Const
-	| Extend of t list
-	| Statics of tclass
-	| EnumStatics of tenum
-	| AbstractStatics of tabstract
-
-and tanon = {
-	mutable a_fields : (string, tclass_field) PMap.t;
-	a_status : anon_status ref;
-}
-
-and texpr_expr =
-	| TConst of tconstant
-	| TLocal of tvar
-	| TArray of texpr * texpr
-	| TBinop of Ast.binop * texpr * texpr
-	| TField of texpr * tfield_access
-	| TTypeExpr of module_type
-	| TParenthesis of texpr
-	| TObjectDecl of ((string * pos * quote_status) * texpr) list
-	| TArrayDecl of texpr list
-	| TCall of texpr * texpr list
-	| TNew of tclass * tparams * texpr list
-	| TUnop of Ast.unop * Ast.unop_flag * texpr
-	| TFunction of tfunc
-	| TVar of tvar * texpr option
-	| TBlock of texpr list
-	| TFor of tvar * texpr * texpr
-	| TIf of texpr * texpr * texpr option
-	| TWhile of texpr * texpr * Ast.while_flag
-	| TSwitch of texpr * (texpr list * texpr) list * texpr option
-	| TTry of texpr * (tvar * texpr) list
-	| TReturn of texpr option
-	| TBreak
-	| TContinue
-	| TThrow of texpr
-	| TCast of texpr * module_type option
-	| TMeta of metadata_entry * texpr
-	| TEnumParameter of texpr * tenum_field * int
-	| TEnumIndex of texpr
-	| TIdent of string
-
-and tfield_access =
-	| FInstance of tclass * tparams * tclass_field
-	| FStatic of tclass * tclass_field
-	| FAnon of tclass_field
-	| FDynamic of string
-	| FClosure of (tclass * tparams) option * tclass_field (* None class = TAnon *)
-	| FEnum of tenum * tenum_field
-
-and texpr = {
-	eexpr : texpr_expr;
-	etype : t;
-	epos : pos;
-}
-
-and tclass_field = {
-	mutable cf_name : string;
-	mutable cf_type : t;
-	cf_pos : pos;
-	cf_name_pos : pos;
-	mutable cf_doc : Ast.documentation;
-	mutable cf_meta : metadata;
-	mutable cf_kind : field_kind;
-	mutable cf_params : type_params;
-	mutable cf_expr : texpr option;
-	mutable cf_expr_unoptimized : tfunc option;
-	mutable cf_overloads : tclass_field list;
-	mutable cf_flags : int;
-}
-
-and tclass_kind =
-	| KNormal
-	| KTypeParameter of t list
-	| KExpr of Ast.expr
-	| KGeneric
-	| KGenericInstance of tclass * tparams
-	| KMacroType
-	| KGenericBuild of class_field list
-	| KAbstractImpl of tabstract
-
-and metadata = Ast.metadata
-
-and tinfos = {
-	mt_path : path;
-	mt_module : module_def;
-	mt_pos : pos;
-	mt_name_pos : pos;
-	mt_private : bool;
-	mt_doc : Ast.documentation;
-	mutable mt_meta : metadata;
-	mt_params : type_params;
-	mutable mt_using : (tclass * pos) list;
-}
-
-and tclass = {
-	mutable cl_path : path;
-	mutable cl_module : module_def;
-	mutable cl_pos : pos;
-	mutable cl_name_pos : pos;
-	mutable cl_private : bool;
-	mutable cl_doc : Ast.documentation;
-	mutable cl_meta : metadata;
-	mutable cl_params : type_params;
-	mutable cl_using : (tclass * pos) list;
-	(* do not insert any fields above *)
-	mutable cl_kind : tclass_kind;
-	mutable cl_extern : bool;
-	mutable cl_final : bool;
-	mutable cl_interface : bool;
-	mutable cl_super : (tclass * tparams) option;
-	mutable cl_implements : (tclass * tparams) list;
-	mutable cl_fields : (string, tclass_field) PMap.t;
-	mutable cl_statics : (string, tclass_field) PMap.t;
-	mutable cl_ordered_statics : tclass_field list;
-	mutable cl_ordered_fields : tclass_field list;
-	mutable cl_dynamic : t option;
-	mutable cl_array_access : t option;
-	mutable cl_constructor : tclass_field option;
-	mutable cl_init : texpr option;
-	mutable cl_overrides : tclass_field list;
-
-	mutable cl_build : unit -> build_state;
-	mutable cl_restore : unit -> unit;
-	(*
-		These are classes which directly extend or directly implement this class.
-		Populated automatically in post-processing step (Filters.run)
-	*)
-	mutable cl_descendants : tclass list;
-}
-
-and tenum_field = {
-	ef_name : string;
-	mutable ef_type : t;
-	ef_pos : pos;
-	ef_name_pos : pos;
-	ef_doc : Ast.documentation;
-	ef_index : int;
-	mutable ef_params : type_params;
-	mutable ef_meta : metadata;
-}
-
-and tenum = {
-	mutable e_path : path;
-	e_module : module_def;
-	e_pos : pos;
-	e_name_pos : pos;
-	e_private : bool;
-	e_doc : Ast.documentation;
-	mutable e_meta : metadata;
-	mutable e_params : type_params;
-	mutable e_using : (tclass * pos) list;
-	(* do not insert any fields above *)
-	e_type : tdef;
-	mutable e_extern : bool;
-	mutable e_constrs : (string , tenum_field) PMap.t;
-	mutable e_names : string list;
-}
-
-and tdef = {
-	t_path : path;
-	t_module : module_def;
-	t_pos : pos;
-	t_name_pos : pos;
-	t_private : bool;
-	t_doc : Ast.documentation;
-	mutable t_meta : metadata;
-	mutable t_params : type_params;
-	mutable t_using : (tclass * pos) list;
-	(* do not insert any fields above *)
-	mutable t_type : t;
-}
-
-and tabstract = {
-	mutable a_path : path;
-	a_module : module_def;
-	a_pos : pos;
-	a_name_pos : pos;
-	a_private : bool;
-	a_doc : Ast.documentation;
-	mutable a_meta : metadata;
-	mutable a_params : type_params;
-	mutable a_using : (tclass * pos) list;
-	(* do not insert any fields above *)
-	mutable a_ops : (Ast.binop * tclass_field) list;
-	mutable a_unops : (Ast.unop * unop_flag * tclass_field) list;
-	mutable a_impl : tclass option;
-	mutable a_this : t;
-	mutable a_from : t list;
-	mutable a_from_field : (t * tclass_field) list;
-	mutable a_to : t list;
-	mutable a_to_field : (t * tclass_field) list;
-	mutable a_array : tclass_field list;
-	mutable a_read : tclass_field option;
-	mutable a_write : tclass_field option;
-}
-
-and module_type =
-	| TClassDecl of tclass
-	| TEnumDecl of tenum
-	| TTypeDecl of tdef
-	| TAbstractDecl of tabstract
-
-and module_def = {
-	m_id : int;
-	m_path : path;
-	mutable m_types : module_type list;
-	m_extra : module_def_extra;
-}
-
-and module_def_display = {
-	mutable m_inline_calls : (pos * pos) list; (* calls whatever is at pos1 from pos2 *)
-	mutable m_type_hints : (pos * pos) list;
-}
-
-and module_def_extra = {
-	m_file : string;
-	m_sign : string;
-	m_display : module_def_display;
-	mutable m_check_policy : module_check_policy list;
-	mutable m_time : float;
-	mutable m_dirty : path option;
-	mutable m_added : int;
-	mutable m_mark : int;
-	mutable m_deps : (int,module_def) PMap.t;
-	mutable m_processed : int;
-	mutable m_kind : module_kind;
-	mutable m_binded_res : (string, string) PMap.t;
-	mutable m_if_feature : (string *(tclass * tclass_field * bool)) list;
-	mutable m_features : (string,bool) Hashtbl.t;
-}
-
-and module_kind =
-	| MCode
-	| MMacro
-	| MFake
-	| MExtern
-	| MImport
-
-and build_state =
-	| Built
-	| Building of tclass list
-	| BuildMacro of (unit -> unit) list ref
-
-type basic_types = {
-	mutable tvoid : t;
-	mutable tint : t;
-	mutable tfloat : t;
-	mutable tbool : t;
-	mutable tnull : t -> t;
-	mutable tstring : t;
-	mutable tarray : t -> t;
-}
-
-type class_field_scope =
-	| CFSStatic
-	| CFSMember
-	| CFSConstructor
-
-type flag_tclass_field =
-	| CfPublic
-	| CfExtern (* This is only set if the field itself is extern, not just the class. *)
-	| CfFinal
-	| CfModifiesThis (* This is set for methods which reassign `this`. E.g. `this = value` *)
-
-let unify_ref : (t -> t -> unit) ref = ref (fun _ _ -> ())
-let monomorph_create_ref : (unit -> tmono) ref = ref (fun _ -> assert false)
-let monomorph_bind_ref : (tmono -> t -> unit) ref = ref (fun _ _ -> ())
-
-(* Flags *)
-
-let has_flag flags flag =
-	flags land (1 lsl flag) > 0
-
-let set_flag flags flag =
-	flags lor (1 lsl flag)
-
-let unset_flag flags flag =
-	flags land (lnot (1 lsl flag))
-
-let int_of_class_field_flag (flag : flag_tclass_field) =
-	Obj.magic flag
-
-let add_class_field_flag cf (flag : flag_tclass_field) =
-	cf.cf_flags <- set_flag cf.cf_flags (int_of_class_field_flag flag)
-
-let remove_class_field_flag cf (flag : flag_tclass_field) =
-	cf.cf_flags <- unset_flag cf.cf_flags (int_of_class_field_flag flag)
-
-let has_class_field_flag cf (flag : flag_tclass_field) =
-	has_flag cf.cf_flags (int_of_class_field_flag flag)
-
-(* ======= General utility ======= *)
-
-let alloc_var =
-	let uid = ref 0 in
-	(fun kind n t p ->
-		incr uid;
-		{
-			v_kind = kind;
-			v_name = n;
-			v_type = t;
-			v_id = !uid;
-			v_capture = false;
-			v_final = (match kind with VUser TVOLocalFunction -> true | _ -> false);
-			v_extra = None;
-			v_meta = [];
-			v_pos = p
-		}
-	)
-
-let alloc_mid =
-	let mid = ref 0 in
-	(fun() -> incr mid; !mid)
-
-let mk e t p = { eexpr = e; etype = t; epos = p }
-
-let mk_block e =
-	match e.eexpr with
-	| TBlock _ -> e
-	| _ -> mk (TBlock [e]) e.etype e.epos
-
-let mk_cast e t p = mk (TCast(e,None)) t p
-
-let null t p = mk (TConst TNull) t p
-
-let mk_mono() = TMono (!monomorph_create_ref ())
-
-let rec t_dynamic = TDynamic t_dynamic
-
-let mk_anon fl = TAnon { a_fields = fl; a_status = ref Closed; }
-
-(* We use this for display purposes because otherwise we never see the Dynamic type that
-   is defined in StdTypes.hx. This is set each time a typer is created, but this is fine
-   because Dynamic is the same in all contexts. If this ever changes we'll have to review
-   how we handle this. *)
-let t_dynamic_def = ref t_dynamic
-
-let tfun pl r = TFun (List.map (fun t -> "",false,t) pl,r)
-
-let fun_args l = List.map (fun (a,c,t) -> a, c <> None, t) l
-
-let mk_class m path pos name_pos =
-	{
-		cl_path = path;
-		cl_module = m;
-		cl_pos = pos;
-		cl_name_pos = name_pos;
-		cl_doc = None;
-		cl_meta = [];
-		cl_private = false;
-		cl_kind = KNormal;
-		cl_extern = false;
-		cl_final = false;
-		cl_interface = false;
-		cl_params = [];
-		cl_using = [];
-		cl_super = None;
-		cl_implements = [];
-		cl_fields = PMap.empty;
-		cl_ordered_statics = [];
-		cl_ordered_fields = [];
-		cl_statics = PMap.empty;
-		cl_dynamic = None;
-		cl_array_access = None;
-		cl_constructor = None;
-		cl_init = None;
-		cl_overrides = [];
-		cl_build = (fun() -> Built);
-		cl_restore = (fun() -> ());
-		cl_descendants = [];
-	}
-
-let module_extra file sign time kind policy =
-	{
-		m_file = file;
-		m_sign = sign;
-		m_display = {
-			m_inline_calls = [];
-			m_type_hints = [];
-		};
-		m_dirty = None;
-		m_added = 0;
-		m_mark = 0;
-		m_time = time;
-		m_processed = 0;
-		m_deps = PMap.empty;
-		m_kind = kind;
-		m_binded_res = PMap.empty;
-		m_if_feature = [];
-		m_features = Hashtbl.create 0;
-		m_check_policy = policy;
-	}
-
-
-let mk_field name ?(public = true) t p name_pos = {
-	cf_name = name;
-	cf_type = t;
-	cf_pos = p;
-	cf_name_pos = name_pos;
-	cf_doc = None;
-	cf_meta = [];
-	cf_kind = Var { v_read = AccNormal; v_write = AccNormal };
-	cf_expr = None;
-	cf_expr_unoptimized = None;
-	cf_params = [];
-	cf_overloads = [];
-	cf_flags = if public then set_flag 0 (int_of_class_field_flag CfPublic) else 0;
-}
-
-let null_module = {
-		m_id = alloc_mid();
-		m_path = [] , "";
-		m_types = [];
-		m_extra = module_extra "" "" 0. MFake [];
-	}
-
-let null_class =
-	let c = mk_class null_module ([],"") null_pos null_pos in
-	c.cl_private <- true;
-	c
-
-let null_field = mk_field "" t_dynamic null_pos null_pos
-
-let null_abstract = {
-	a_path = ([],"");
-	a_module = null_module;
-	a_pos = null_pos;
-	a_name_pos = null_pos;
-	a_private = true;
-	a_doc = None;
-	a_meta = [];
-	a_params = [];
-	a_using = [];
-	a_ops = [];
-	a_unops = [];
-	a_impl = None;
-	a_this = t_dynamic;
-	a_from = [];
-	a_from_field = [];
-	a_to = [];
-	a_to_field = [];
-	a_array = [];
-	a_read = None;
-	a_write = None;
-}
-
-let add_dependency m mdep =
-	if m != null_module && m != mdep then m.m_extra.m_deps <- PMap.add mdep.m_id mdep m.m_extra.m_deps
-
-let arg_name (a,_) = a.v_name
-
-let t_infos t : tinfos =
-	match t with
-	| TClassDecl c -> Obj.magic c
-	| TEnumDecl e -> Obj.magic e
-	| TTypeDecl t -> Obj.magic t
-	| TAbstractDecl a -> Obj.magic a
-
-let t_path t = (t_infos t).mt_path
-
-let rec is_parent csup c =
-	if c == csup || List.exists (fun (i,_) -> is_parent csup i) c.cl_implements then
-		true
-	else match c.cl_super with
-		| None -> false
-		| Some (c,_) -> is_parent csup c
-
-let add_descendant c descendant =
-	c.cl_descendants <- descendant :: c.cl_descendants
-
-let lazy_type f =
-	match !f with
-	| LAvailable t -> t
-	| LProcessing f | LWait f -> f()
-
-let lazy_available t = LAvailable t
-let lazy_processing f = LProcessing f
-let lazy_wait f = LWait f
-
-let map loop t =
-	match t with
-	| TMono r ->
-		(match r.tm_type with
-		| None -> t
-		| Some t -> loop t) (* erase*)
-	| TEnum (_,[]) | TInst (_,[]) | TType (_,[]) ->
-		t
-	| TEnum (e,tl) ->
-		TEnum (e, List.map loop tl)
-	| TInst (c,tl) ->
-		TInst (c, List.map loop tl)
-	| TType (t2,tl) ->
-		TType (t2,List.map loop tl)
-	| TAbstract (a,tl) ->
-		TAbstract (a,List.map loop tl)
-	| TFun (tl,r) ->
-		TFun (List.map (fun (s,o,t) -> s, o, loop t) tl,loop r)
-	| TAnon a ->
-		let fields = PMap.map (fun f -> { f with cf_type = loop f.cf_type }) a.a_fields in
-		begin match !(a.a_status) with
-			| Opened ->
-				a.a_fields <- fields;
-				t
-			| _ ->
-				TAnon {
-					a_fields = fields;
-					a_status = a.a_status;
-				}
-		end
-	| TLazy f ->
-		let ft = lazy_type f in
-		let ft2 = loop ft in
-		if ft == ft2 then t else ft2
-	| TDynamic t2 ->
-		if t == t2 then	t else TDynamic (loop t2)
-
-let duplicate t =
-	let monos = ref [] in
-	let rec loop t =
-		match t with
-		| TMono { tm_type = None } ->
-			(try
-				List.assq t !monos
-			with Not_found ->
-				let m = mk_mono() in
-				monos := (t,m) :: !monos;
-				m)
-		| _ ->
-			map loop t
-	in
-	loop t
-
-exception ApplyParamsRecursion
-
-(* substitute parameters with other types *)
-let apply_params ?stack cparams params t =
-	match cparams with
-	| [] -> t
-	| _ ->
-	let rec loop l1 l2 =
-		match l1, l2 with
-		| [] , [] -> []
-		| (x,TLazy f) :: l1, _ -> loop ((x,lazy_type f) :: l1) l2
-		| (_,t1) :: l1 , t2 :: l2 -> (t1,t2) :: loop l1 l2
-		| _ -> assert false
-	in
-	let subst = loop cparams params in
-	let rec loop t =
-		try
-			List.assq t subst
-		with Not_found ->
-		match t with
-		| TMono r ->
-			(match r.tm_type with
-			| None -> t
-			| Some t -> loop t)
-		| TEnum (e,tl) ->
-			(match tl with
-			| [] -> t
-			| _ -> TEnum (e,List.map loop tl))
-		| TType (t2,tl) ->
-			(match tl with
-			| [] -> t
-			| _ ->
-				let new_applied_params = List.map loop tl in
-				(match stack with
-				| None -> ()
-				| Some stack ->
-					List.iter (fun (subject, old_applied_params) ->
-						(*
-							E.g.:
-							```
-							typedef Rec<T> = { function method():Rec<Array<T>> }
-							```
-							We need to make sure that we are not applying the result of previous
-							application to the same place, which would mean the result of current
-							application would go into `apply_params` again and then again and so on.
-
-							Argument `stack` holds all previous results of `apply_params` to typedefs in current
-							unification process.
-
-							Imagine we are trying to unify `Rec<Int>` with something.
-
-							Once `apply_params Array<T> Int Rec<Array<T>>` is called for the first time the result
-							will be `Rec< Array<Int> >`. Store `Array<Int>` into `stack`
-
-							Then the next params application looks like this:
-								`apply_params Array<T> Array<Int> Rec<Array<T>>`
-							Notice the second argument is actually the result of a previous `apply_params` call.
-							And the result of the current call is `Rec< Array<Array<Int>> >`.
-
-							The third call would be:
-								`apply_params Array<T> Array<Array<Int>> Rec<Array<T>>`
-							and so on.
-
-							To stop infinite params application we need to check that we are trying to apply params
-							produced by the previous `apply_params Array<Int> _ Rec<Array<T>>` to the same `Rec<Array<T>>`
-						*)
-						if
-							subject == t (* Check the place that we're applying to is the same `Rec<Array<T>>` *)
-							&& old_applied_params == params (* Check that params we're applying are the same params
-																produced by the previous call to
-																`apply_params Array<T> _ Rec<Array<T>>` *)
-						then
-							raise ApplyParamsRecursion
-					) !stack;
-					stack := (t, new_applied_params) :: !stack;
-				);
-				TType (t2,new_applied_params))
-		| TAbstract (a,tl) ->
-			(match tl with
-			| [] -> t
-			| _ -> TAbstract (a,List.map loop tl))
-		| TInst (c,tl) ->
-			(match tl with
-			| [] ->
-				t
-			| [TMono r] ->
-				(match r.tm_type with
-				| Some tt when t == tt ->
-					(* for dynamic *)
-					let pt = mk_mono() in
-					let t = TInst (c,[pt]) in
-					(match pt with TMono r -> !monomorph_bind_ref r t | _ -> assert false);
-					t
-				| _ -> TInst (c,List.map loop tl))
-			| _ ->
-				TInst (c,List.map loop tl))
-		| TFun (tl,r) ->
-			TFun (List.map (fun (s,o,t) -> s, o, loop t) tl,loop r)
-		| TAnon a ->
-			let fields = PMap.map (fun f -> { f with cf_type = loop f.cf_type }) a.a_fields in
-			begin match !(a.a_status) with
-				| Opened ->
-					a.a_fields <- fields;
-					t
-				| _ ->
-					TAnon {
-						a_fields = fields;
-						a_status = a.a_status;
-					}
-			end
-		| TLazy f ->
-			let ft = lazy_type f in
-			let ft2 = loop ft in
-			if ft == ft2 then
-				t
-			else
-				ft2
-		| TDynamic t2 ->
-			if t == t2 then
-				t
-			else
-				TDynamic (loop t2)
-	in
-	loop t
-
-let monomorphs eparams t =
-	apply_params eparams (List.map (fun _ -> mk_mono()) eparams) t
-
-let apply_params_stack = ref []
-
-let try_apply_params_rec cparams params t success =
-	let old_stack = !apply_params_stack in
-	try
-		let result = success (apply_params ~stack:apply_params_stack cparams params t) in
-		apply_params_stack := old_stack;
-		result
-	with
-		| ApplyParamsRecursion ->
-			apply_params_stack := old_stack;
-		| err ->
-			apply_params_stack := old_stack;
-			raise err
-
-let rec follow t =
-	match t with
-	| TMono r ->
-		(match r.tm_type with
-		| Some t -> follow t
-		| _ -> t)
-	| TLazy f ->
-		follow (lazy_type f)
-	| TType (t,tl) ->
-		follow (apply_params t.t_params tl t.t_type)
-	| TAbstract({a_path = [],"Null"},[t]) ->
-		follow t
-	| _ -> t
-
-let follow_once t =
-	match t with
-	| TMono r ->
-		(match r.tm_type with
-		| None -> t
-		| Some t -> t)
-	| TAbstract _ | TEnum _ | TInst _ | TFun _ | TAnon _ | TDynamic _ ->
-		t
-	| TType (t,tl) ->
-		apply_params t.t_params tl t.t_type
-	| TLazy f ->
-		lazy_type f
-
-let rec follow_without_null t =
-	match t with
-	| TMono r ->
-		(match r.tm_type with
-		| Some t -> follow_without_null t
-		| _ -> t)
-	| TLazy f ->
-		follow_without_null (lazy_type f)
-	| TType (t,tl) ->
-		follow_without_null (apply_params t.t_params tl t.t_type)
-	| _ -> t
-
-(** Assumes `follow` has already been applied *)
-let rec ambiguate_funs t =
-	match t with
-	| TFun _ -> TFun ([], t_dynamic)
-	| TMono r ->
-		(match r.tm_type with
-		| Some _ -> assert false
-		| _ -> t)
-	| TInst (a, pl) ->
-	    TInst (a, List.map ambiguate_funs pl)
-	| TEnum (a, pl) ->
-	    TEnum (a, List.map ambiguate_funs pl)
-	| TAbstract (a, pl) ->
-	    TAbstract (a, List.map ambiguate_funs pl)
-	| TType (a, pl) ->
-	    TType (a, List.map ambiguate_funs pl)
-	| TDynamic _ -> t
-	| TAnon a ->
-	    TAnon { a with a_fields =
-		    PMap.map (fun af -> { af with cf_type =
-				ambiguate_funs af.cf_type }) a.a_fields }
-	| TLazy _ -> assert false
-
-let rec is_nullable = function
-	| TMono r ->
-		(match r.tm_type with None -> false | Some t -> is_nullable t)
-	| TAbstract ({ a_path = ([],"Null") },[_]) ->
-		true
-	| TLazy f ->
-		is_nullable (lazy_type f)
-	| TType (t,tl) ->
-		is_nullable (apply_params t.t_params tl t.t_type)
-	| TFun _ ->
-		false
-(*
-	Type parameters will most of the time be nullable objects, so we don't want to make it hard for users
-	to have to specify Null<T> all over the place, so while they could be a basic type, let's assume they will not.
-
-	This will still cause issues with inlining and haxe.rtti.Generic. In that case proper explicit Null<T> is required to
-	work correctly with basic types. This could still be fixed by redoing a nullability inference on the typed AST.
-
-	| TInst ({ cl_kind = KTypeParameter },_) -> false
-*)
-	| TAbstract (a,_) when Meta.has Meta.CoreType a.a_meta ->
-		not (Meta.has Meta.NotNull a.a_meta)
-	| TAbstract (a,tl) ->
-		not (Meta.has Meta.NotNull a.a_meta) && is_nullable (apply_params a.a_params tl a.a_this)
-	| _ ->
-		true
-
-let rec is_null ?(no_lazy=false) = function
-	| TMono r ->
-		(match r.tm_type with None -> false | Some t -> is_null t)
-	| TAbstract ({ a_path = ([],"Null") },[t]) ->
-		not (is_nullable (follow t))
-	| TLazy f ->
-		if no_lazy then raise Exit else is_null (lazy_type f)
-	| TType (t,tl) ->
-		is_null (apply_params t.t_params tl t.t_type)
-	| _ ->
-		false
-
-(* Determines if we have a Null<T>. Unlike is_null, this returns true even if the wrapped type is nullable itself. *)
-let rec is_explicit_null = function
-	| TMono r ->
-		(match r.tm_type with None -> false | Some t -> is_explicit_null t)
-	| TAbstract ({ a_path = ([],"Null") },[t]) ->
-		true
-	| TLazy f ->
-		is_explicit_null (lazy_type f)
-	| TType (t,tl) ->
-		is_explicit_null (apply_params t.t_params tl t.t_type)
-	| _ ->
-		false
-
-let rec has_mono t = match t with
-	| TMono r ->
-		(match r.tm_type with None -> true | Some t -> has_mono t)
-	| TInst(_,pl) | TEnum(_,pl) | TAbstract(_,pl) | TType(_,pl) ->
-		List.exists has_mono pl
-	| TDynamic _ ->
-		false
-	| TFun(args,r) ->
-		has_mono r || List.exists (fun (_,_,t) -> has_mono t) args
-	| TAnon a ->
-		PMap.fold (fun cf b -> has_mono cf.cf_type || b) a.a_fields false
-	| TLazy f ->
-		has_mono (lazy_type f)
-
-let concat e1 e2 =
-	let e = (match e1.eexpr, e2.eexpr with
-		| TBlock el1, TBlock el2 -> TBlock (el1@el2)
-		| TBlock el, _ -> TBlock (el @ [e2])
-		| _, TBlock el -> TBlock (e1 :: el)
-		| _ , _ -> TBlock [e1;e2]
-	) in
-	mk e e2.etype (punion e1.epos e2.epos)
-
-let is_closed a = !(a.a_status) <> Opened
-
-let type_of_module_type = function
-	| TClassDecl c -> TInst (c,List.map snd c.cl_params)
-	| TEnumDecl e -> TEnum (e,List.map snd e.e_params)
-	| TTypeDecl t -> TType (t,List.map snd t.t_params)
-	| TAbstractDecl a -> TAbstract (a,List.map snd a.a_params)
-
-let rec module_type_of_type = function
-	| TInst(c,_) -> TClassDecl c
-	| TEnum(en,_) -> TEnumDecl en
-	| TType(t,_) -> TTypeDecl t
-	| TAbstract(a,_) -> TAbstractDecl a
-	| TLazy f -> module_type_of_type (lazy_type f)
-	| TMono r ->
-		(match r.tm_type with
-		| Some t -> module_type_of_type t
-		| _ -> raise Exit)
-	| _ ->
-		raise Exit
-
-let tconst_to_const = function
-	| TInt i -> Int (Int32.to_string i)
-	| TFloat s -> Float s
-	| TString s -> String(s,SDoubleQuotes)
-	| TBool b -> Ident (if b then "true" else "false")
-	| TNull -> Ident "null"
-	| TThis -> Ident "this"
-	| TSuper -> Ident "super"
-
-let has_ctor_constraint c = match c.cl_kind with
-	| KTypeParameter tl ->
-		List.exists (fun t -> match follow t with
-			| TAnon a when PMap.mem "new" a.a_fields -> true
-			| TAbstract({a_path=["haxe"],"Constructible"},_) -> true
-			| _ -> false
-		) tl;
-	| _ -> false
-
-(* ======= Field utility ======= *)
-
-let field_name f =
-	match f with
-	| FAnon f | FInstance (_,_,f) | FStatic (_,f) | FClosure (_,f) -> f.cf_name
-	| FEnum (_,f) -> f.ef_name
-	| FDynamic n -> n
-
-let extract_field = function
-	| FAnon f | FInstance (_,_,f) | FStatic (_,f) | FClosure (_,f) -> Some f
-	| _ -> None
-
-let is_physical_var_field f =
-	match f.cf_kind with
-	| Var { v_read = AccNormal | AccInline | AccNo } | Var { v_write = AccNormal | AccNo } -> true
-	| Var _ -> Meta.has Meta.IsVar f.cf_meta
-	| _ -> false
-
-let is_physical_field f =
-	match f.cf_kind with
-	| Method _ -> true
-	| _ -> is_physical_var_field f
-
-let field_type f =
-	match f.cf_params with
-	| [] -> f.cf_type
-	| l -> monomorphs l f.cf_type
-
-let rec raw_class_field build_type c tl i =
-	let apply = apply_params c.cl_params tl in
-	try
-		let f = PMap.find i c.cl_fields in
-		Some (c,tl), build_type f , f
-	with Not_found -> try (match c.cl_constructor with
-		| Some ctor when i = "new" -> Some (c,tl), build_type ctor,ctor
-		| _ -> raise Not_found)
-	with Not_found -> try
-		match c.cl_super with
-		| None ->
-			raise Not_found
-		| Some (c,tl) ->
-			let c2 , t , f = raw_class_field build_type c (List.map apply tl) i in
-			c2, apply_params c.cl_params tl t , f
-	with Not_found ->
-		match c.cl_kind with
-		| KTypeParameter tl ->
-			let rec loop = function
-				| [] ->
-					raise Not_found
-				| t :: ctl ->
-					match follow t with
-					| TAnon a ->
-						(try
-							let f = PMap.find i a.a_fields in
-							None, build_type f, f
-						with
-							Not_found -> loop ctl)
-					| TInst (c,tl) ->
-						(try
-							let c2, t , f = raw_class_field build_type c (List.map apply tl) i in
-							c2, apply_params c.cl_params tl t, f
-						with
-							Not_found -> loop ctl)
-					| _ ->
-						loop ctl
-			in
-			loop tl
-		| _ ->
-			if not c.cl_interface then raise Not_found;
-			(*
-				an interface can implements other interfaces without
-				having to redeclare its fields
-			*)
-			let rec loop = function
-				| [] ->
-					raise Not_found
-				| (c,tl) :: l ->
-					try
-						let c2, t , f = raw_class_field build_type c (List.map apply tl) i in
-						c2, apply_params c.cl_params tl t, f
-					with
-						Not_found -> loop l
-			in
-			loop c.cl_implements
-
-let class_field = raw_class_field field_type
-
-let quick_field t n =
-	match follow t with
-	| TInst (c,tl) ->
-		let c, _, f = raw_class_field (fun f -> f.cf_type) c tl n in
-		(match c with None -> FAnon f | Some (c,tl) -> FInstance (c,tl,f))
-	| TAnon a ->
-		(match !(a.a_status) with
-		| EnumStatics e ->
-			let ef = PMap.find n e.e_constrs in
-			FEnum(e,ef)
-		| Statics c ->
-			FStatic (c,PMap.find n c.cl_statics)
-		| AbstractStatics a ->
-			begin match a.a_impl with
-				| Some c ->
-					let cf = PMap.find n c.cl_statics in
-					FStatic(c,cf) (* is that right? *)
-				| _ ->
-					raise Not_found
-			end
-		| _ ->
-			FAnon (PMap.find n a.a_fields))
-	| TDynamic _ ->
-		FDynamic n
-	| TEnum _  | TMono _ | TAbstract _ | TFun _ ->
-		raise Not_found
-	| TLazy _ | TType _ ->
-		assert false
-
-let quick_field_dynamic t s =
-	try quick_field t s
-	with Not_found -> FDynamic s
-
-let rec get_constructor build_type c =
-	match c.cl_constructor, c.cl_super with
-	| Some c, _ -> build_type c, c
-	| None, None -> raise Not_found
-	| None, Some (csup,cparams) ->
-		let t, c = get_constructor build_type csup in
-		apply_params csup.cl_params cparams t, c
-
-let has_constructor c =
-	try
-		ignore(get_constructor (fun cf -> cf.cf_type) c);
-		true
-	with Not_found -> false
-
-(* ======= Printing ======= *)
-
-let print_context() = ref []
-
-let rec s_type_kind t =
-	let map tl = String.concat ", " (List.map s_type_kind tl) in
-	match t with
-	| TMono r ->
-		begin match r.tm_type with
-			| None ->
-				begin match r.tm_constraint with
-				| None ->
-					Printf.sprintf "TMono (None)"
-				| Some (cstr,_,_) ->
-					let s_constraints = match cstr with
-						| CStructure(t,_) -> s_type_kind t
-						| CTypes tl -> String.concat ", " (List.map s_type_kind tl)
-					in
-					Printf.sprintf "(TMono (None : %s))"s_constraints
-				end
-			| Some t -> "TMono (Some (" ^ (s_type_kind t) ^ "))"
-		end
-	| TEnum(en,tl) -> Printf.sprintf "TEnum(%s, [%s])" (s_type_path en.e_path) (map tl)
-	| TInst(c,tl) -> Printf.sprintf "TInst(%s, [%s])" (s_type_path c.cl_path) (map tl)
-	| TType(t,tl) -> Printf.sprintf "TType(%s, [%s])" (s_type_path t.t_path) (map tl)
-	| TAbstract(a,tl) -> Printf.sprintf "TAbstract(%s, [%s])" (s_type_path a.a_path) (map tl)
-	| TFun(tl,r) -> Printf.sprintf "TFun([%s], %s)" (String.concat ", " (List.map (fun (n,b,t) -> Printf.sprintf "%s%s:%s" (if b then "?" else "") n (s_type_kind t)) tl)) (s_type_kind r)
-	| TAnon an -> "TAnon"
-	| TDynamic t2 -> "TDynamic"
-	| TLazy _ -> "TLazy"
-
-let s_module_type_kind = function
-	| TClassDecl c -> "TClassDecl(" ^ (s_type_path c.cl_path) ^ ")"
-	| TEnumDecl en -> "TEnumDecl(" ^ (s_type_path en.e_path) ^ ")"
-	| TAbstractDecl a -> "TAbstractDecl(" ^ (s_type_path a.a_path) ^ ")"
-	| TTypeDecl t -> "TTypeDecl(" ^ (s_type_path t.t_path) ^ ")"
-
-let is_simn = false
-
-let rec s_type ctx t =
-	match t with
-	| TMono r ->
-		(match r.tm_type with
-		| None ->
-			begin try
-				let id = List.assq t (!ctx) in
-				Printf.sprintf "Unknown<%d>" id
-			with Not_found ->
-				let id = List.length !ctx in
-				ctx := (t,id) :: !ctx;
-				begin match r.tm_constraint with
-				| Some (cstr,_,_) when is_simn ->
-					let s_constraints = match cstr with
-						| CStructure(t,_) -> s_type ctx t
-						| CTypes tl -> String.concat ", " (List.map (s_type ctx) tl)
-					in
-					Printf.sprintf "(Unknown<%d> : %s)" id s_constraints
-				| _ ->
-					Printf.sprintf "Unknown<%d>" id
-				end
-			end
-		| Some t -> s_type ctx t)
-	| TEnum (e,tl) ->
-		s_type_path e.e_path ^ s_type_params ctx tl
-	| TInst (c,tl) ->
-		(match c.cl_kind with
-		| KExpr e -> Ast.Printer.s_expr e
-		| _ -> s_type_path c.cl_path ^ s_type_params ctx tl)
-	| TType (t,tl) ->
-		s_type_path t.t_path ^ s_type_params ctx tl
-	| TAbstract (a,tl) ->
-		s_type_path a.a_path ^ s_type_params ctx tl
-	| TFun ([],t) ->
-		"Void -> " ^ s_fun ctx t false
-	| TFun (l,t) ->
-		let args = match l with
-			| [] -> "()"
-			| ["",b,t] -> Printf.sprintf "%s%s" (if b then "?" else "") (s_fun ctx t true)
-			| _ ->
-				let args = String.concat ", " (List.map (fun (s,b,t) ->
-					(if b then "?" else "") ^ (if s = "" then "" else s ^ " : ") ^ s_fun ctx t true
-				) l) in
-				"(" ^ args ^ ")"
-		in
-		Printf.sprintf "%s -> %s" args (s_fun ctx t false)
-	| TAnon a ->
-		begin
-			match !(a.a_status) with
-			| Statics c -> Printf.sprintf "{ Statics %s }" (s_type_path c.cl_path)
-			| EnumStatics e -> Printf.sprintf "{ EnumStatics %s }" (s_type_path e.e_path)
-			| AbstractStatics a -> Printf.sprintf "{ AbstractStatics %s }" (s_type_path a.a_path)
-			| _ ->
-				let fl = PMap.fold (fun f acc -> ((if Meta.has Meta.Optional f.cf_meta then " ?" else " ") ^ f.cf_name ^ " : " ^ s_type ctx f.cf_type) :: acc) a.a_fields [] in
-				"{" ^ (if not (is_closed a) then "+" else "") ^  String.concat "," fl ^ " }"
-		end
-	| TDynamic t2 ->
-		"Dynamic" ^ s_type_params ctx (if t == t2 then [] else [t2])
-	| TLazy f ->
-		s_type ctx (lazy_type f)
-
-and s_fun ctx t void =
-	match t with
-	| TFun _ ->
-		"(" ^ s_type ctx t ^ ")"
-	| TAbstract ({ a_path = ([],"Void") },[]) when void ->
-		"(" ^ s_type ctx t ^ ")"
-	| TMono r ->
-		(match r.tm_type with
-		| None -> s_type ctx t
-		| Some t -> s_fun ctx t void)
-	| TLazy f ->
-		s_fun ctx (lazy_type f) void
-	| _ ->
-		s_type ctx t
-
-and s_type_params ctx = function
-	| [] -> ""
-	| l -> "<" ^ String.concat ", " (List.map (s_type ctx) l) ^ ">"
-
-let s_access is_read = function
-	| AccNormal -> "default"
-	| AccNo -> "null"
-	| AccNever -> "never"
-	| AccResolve -> "resolve"
-	| AccCall -> if is_read then "get" else "set"
-	| AccInline	-> "inline"
-	| AccRequire (n,_) -> "require " ^ n
-	| AccCtor -> "ctor"
-
-let s_kind = function
-	| Var { v_read = AccNormal; v_write = AccNormal } -> "var"
-	| Var v -> "(" ^ s_access true v.v_read ^ "," ^ s_access false v.v_write ^ ")"
-	| Method m ->
-		match m with
-		| MethNormal -> "method"
-		| MethDynamic -> "dynamic method"
-		| MethInline -> "inline method"
-		| MethMacro -> "macro method"
-
-let s_expr_kind e =
-	match e.eexpr with
-	| TConst _ -> "Const"
-	| TLocal _ -> "Local"
-	| TArray (_,_) -> "Array"
-	| TBinop (_,_,_) -> "Binop"
-	| TEnumParameter (_,_,_) -> "EnumParameter"
-	| TEnumIndex _ -> "EnumIndex"
-	| TField (_,_) -> "Field"
-	| TTypeExpr _ -> "TypeExpr"
-	| TParenthesis _ -> "Parenthesis"
-	| TObjectDecl _ -> "ObjectDecl"
-	| TArrayDecl _ -> "ArrayDecl"
-	| TCall (_,_) -> "Call"
-	| TNew (_,_,_) -> "New"
-	| TUnop (_,_,_) -> "Unop"
-	| TFunction _ -> "Function"
-	| TVar _ -> "Vars"
-	| TBlock _ -> "Block"
-	| TFor (_,_,_) -> "For"
-	| TIf (_,_,_) -> "If"
-	| TWhile (_,_,_) -> "While"
-	| TSwitch (_,_,_) -> "Switch"
-	| TTry (_,_) -> "Try"
-	| TReturn _ -> "Return"
-	| TBreak -> "Break"
-	| TContinue -> "Continue"
-	| TThrow _ -> "Throw"
-	| TCast _ -> "Cast"
-	| TMeta _ -> "Meta"
-	| TIdent _ -> "Ident"
-
-let s_const = function
-	| TInt i -> Int32.to_string i
-	| TFloat s -> s
-	| TString s -> Printf.sprintf "\"%s\"" (StringHelper.s_escape s)
-	| TBool b -> if b then "true" else "false"
-	| TNull -> "null"
-	| TThis -> "this"
-	| TSuper -> "super"
-
-let s_field_access s_type fa = match fa with
-	| FStatic (c,f) -> "static(" ^ s_type_path c.cl_path ^ "." ^ f.cf_name ^ ")"
-	| FInstance (c,_,f) -> "inst(" ^ s_type_path c.cl_path ^ "." ^ f.cf_name ^ " : " ^ s_type f.cf_type ^ ")"
-	| FClosure (c,f) -> "closure(" ^ (match c with None -> f.cf_name | Some (c,_) -> s_type_path c.cl_path ^ "." ^ f.cf_name)  ^ ")"
-	| FAnon f -> "anon(" ^ f.cf_name ^ ")"
-	| FEnum (en,f) -> "enum(" ^ s_type_path en.e_path ^ "." ^ f.ef_name ^ ")"
-	| FDynamic f -> "dynamic(" ^ f ^ ")"
-
-let rec s_expr s_type e =
-	let sprintf = Printf.sprintf in
-	let slist f l = String.concat "," (List.map f l) in
-	let loop = s_expr s_type in
-	let s_var v = v.v_name ^ ":" ^ string_of_int v.v_id ^ if v.v_capture then "[c]" else "" in
-	let str = (match e.eexpr with
-	| TConst c ->
-		"Const " ^ s_const c
-	| TLocal v ->
-		"Local " ^ s_var v
-	| TArray (e1,e2) ->
-		sprintf "%s[%s]" (loop e1) (loop e2)
-	| TBinop (op,e1,e2) ->
-		sprintf "(%s %s %s)" (loop e1) (s_binop op) (loop e2)
-	| TEnumIndex e1 ->
-		sprintf "EnumIndex %s" (loop e1)
-	| TEnumParameter (e1,_,i) ->
-		sprintf "%s[%i]" (loop e1) i
-	| TField (e,f) ->
-		let fstr = s_field_access s_type f in
-		sprintf "%s.%s" (loop e) fstr
-	| TTypeExpr m ->
-		sprintf "TypeExpr %s" (s_type_path (t_path m))
-	| TParenthesis e ->
-		sprintf "Parenthesis %s" (loop e)
-	| TObjectDecl fl ->
-		sprintf "ObjectDecl {%s}" (slist (fun ((f,_,qs),e) -> sprintf "%s : %s" (s_object_key_name f qs) (loop e)) fl)
-	| TArrayDecl el ->
-		sprintf "ArrayDecl [%s]" (slist loop el)
-	| TCall (e,el) ->
-		sprintf "Call %s(%s)" (loop e) (slist loop el)
-	| TNew (c,pl,el) ->
-		sprintf "New %s%s(%s)" (s_type_path c.cl_path) (match pl with [] -> "" | l -> sprintf "<%s>" (slist s_type l)) (slist loop el)
-	| TUnop (op,f,e) ->
-		(match f with
-		| Prefix -> sprintf "(%s %s)" (s_unop op) (loop e)
-		| Postfix -> sprintf "(%s %s)" (loop e) (s_unop op))
-	| TFunction f ->
-		let args = slist (fun (v,o) -> sprintf "%s : %s%s" (s_var v) (s_type v.v_type) (match o with None -> "" | Some c -> " = " ^ loop c)) f.tf_args in
-		sprintf "Function(%s) : %s = %s" args (s_type f.tf_type) (loop f.tf_expr)
-	| TVar (v,eo) ->
-		sprintf "Vars %s" (sprintf "%s : %s%s" (s_var v) (s_type v.v_type) (match eo with None -> "" | Some e -> " = " ^ loop e))
-	| TBlock el ->
-		sprintf "Block {\n%s}" (String.concat "" (List.map (fun e -> sprintf "%s;\n" (loop e)) el))
-	| TFor (v,econd,e) ->
-		sprintf "For (%s : %s in %s,%s)" (s_var v) (s_type v.v_type) (loop econd) (loop e)
-	| TIf (e,e1,e2) ->
-		sprintf "If (%s,%s%s)" (loop e) (loop e1) (match e2 with None -> "" | Some e -> "," ^ loop e)
-	| TWhile (econd,e,flag) ->
-		(match flag with
-		| NormalWhile -> sprintf "While (%s,%s)" (loop econd) (loop e)
-		| DoWhile -> sprintf "DoWhile (%s,%s)" (loop e) (loop econd))
-	| TSwitch (e,cases,def) ->
-		sprintf "Switch (%s,(%s)%s)" (loop e) (slist (fun (cl,e) -> sprintf "case %s: %s" (slist loop cl) (loop e)) cases) (match def with None -> "" | Some e -> "," ^ loop e)
-	| TTry (e,cl) ->
-		sprintf "Try %s(%s) " (loop e) (slist (fun (v,e) -> sprintf "catch( %s : %s ) %s" (s_var v) (s_type v.v_type) (loop e)) cl)
-	| TReturn None ->
-		"Return"
-	| TReturn (Some e) ->
-		sprintf "Return %s" (loop e)
-	| TBreak ->
-		"Break"
-	| TContinue ->
-		"Continue"
-	| TThrow e ->
-		"Throw " ^ (loop e)
-	| TCast (e,t) ->
-		sprintf "Cast %s%s" (match t with None -> "" | Some t -> s_type_path (t_path t) ^ ": ") (loop e)
-	| TMeta ((n,el,_),e) ->
-		sprintf "@%s%s %s" (Meta.to_string n) (match el with [] -> "" | _ -> "(" ^ (String.concat ", " (List.map Ast.Printer.s_expr el)) ^ ")") (loop e)
-	| TIdent s ->
-		"Ident " ^ s
-	) in
-	sprintf "(%s : %s)" str (s_type e.etype)
-
-let rec s_expr_pretty print_var_ids tabs top_level s_type e =
-	let sprintf = Printf.sprintf in
-	let loop = s_expr_pretty print_var_ids tabs false s_type in
-	let slist c f l = String.concat c (List.map f l) in
-	let clist f l = slist ", " f l in
-	let local v = if print_var_ids then sprintf "%s<%i>" v.v_name v.v_id else v.v_name in
-	match e.eexpr with
-	| TConst c -> s_const c
-	| TLocal v -> local v
-	| TArray (e1,e2) -> sprintf "%s[%s]" (loop e1) (loop e2)
-	| TBinop (op,e1,e2) -> sprintf "%s %s %s" (loop e1) (s_binop op) (loop e2)
-	| TEnumParameter (e1,_,i) -> sprintf "%s[%i]" (loop e1) i
-	| TEnumIndex e1 -> sprintf "enumIndex %s" (loop e1)
-	| TField (e1,s) -> sprintf "%s.%s" (loop e1) (field_name s)
-	| TTypeExpr mt -> (s_type_path (t_path mt))
-	| TParenthesis e1 -> sprintf "(%s)" (loop e1)
-	| TObjectDecl fl -> sprintf "{%s}" (clist (fun ((f,_,qs),e) -> sprintf "%s : %s" (s_object_key_name f qs) (loop e)) fl)
-	| TArrayDecl el -> sprintf "[%s]" (clist loop el)
-	| TCall (e1,el) -> sprintf "%s(%s)" (loop e1) (clist loop el)
-	| TNew (c,pl,el) ->
-		sprintf "new %s(%s)" (s_type_path c.cl_path) (clist loop el)
-	| TUnop (op,f,e) ->
-		(match f with
-		| Prefix -> sprintf "%s %s" (s_unop op) (loop e)
-		| Postfix -> sprintf "%s %s" (loop e) (s_unop op))
-	| TFunction f ->
-		let args = clist (fun (v,o) -> sprintf "%s:%s%s" (local v) (s_type v.v_type) (match o with None -> "" | Some c -> " = " ^ loop c)) f.tf_args in
-		sprintf "%s(%s) %s" (if top_level then "" else "function") args (loop f.tf_expr)
-	| TVar (v,eo) ->
-		sprintf "var %s" (sprintf "%s%s" (local v) (match eo with None -> "" | Some e -> " = " ^ loop e))
-	| TBlock el ->
-		let ntabs = tabs ^ "\t" in
-		let s = sprintf "{\n%s" (String.concat "" (List.map (fun e -> sprintf "%s%s;\n" ntabs (s_expr_pretty print_var_ids ntabs top_level s_type e)) el)) in
-		(match el with
-			| [] -> "{}"
-			| _ ->  s ^ tabs ^ "}")
-	| TFor (v,econd,e) ->
-		sprintf "for (%s in %s) %s" (local v) (loop econd) (loop e)
-	| TIf (e,e1,e2) ->
-		sprintf "if (%s) %s%s" (loop e) (loop e1) (match e2 with None -> "" | Some e -> " else " ^ loop e)
-	| TWhile (econd,e,flag) ->
-		(match flag with
-		| NormalWhile -> sprintf "while (%s) %s" (loop econd) (loop e)
-		| DoWhile -> sprintf "do (%s) while(%s)" (loop e) (loop econd))
-	| TSwitch (e,cases,def) ->
-		let ntabs = tabs ^ "\t" in
-		let s = sprintf "switch (%s) {\n%s%s" (loop e) (slist "" (fun (cl,e) -> sprintf "%scase %s: %s;\n" ntabs (clist loop cl) (s_expr_pretty print_var_ids ntabs top_level s_type e)) cases) (match def with None -> "" | Some e -> ntabs ^ "default: " ^ (s_expr_pretty print_var_ids ntabs top_level s_type e) ^ "\n") in
-		s ^ tabs ^ "}"
-	| TTry (e,cl) ->
-		sprintf "try %s%s" (loop e) (clist (fun (v,e) -> sprintf " catch (%s:%s) %s" (local v) (s_type v.v_type) (loop e)) cl)
-	| TReturn None ->
-		"return"
-	| TReturn (Some e) ->
-		sprintf "return %s" (loop e)
-	| TBreak ->
-		"break"
-	| TContinue ->
-		"continue"
-	| TThrow e ->
-		"throw " ^ (loop e)
-	| TCast (e,None) ->
-		sprintf "cast %s" (loop e)
-	| TCast (e,Some mt) ->
-		sprintf "cast (%s,%s)" (loop e) (s_type_path (t_path mt))
-	| TMeta ((n,el,_),e) ->
-		sprintf "@%s%s %s" (Meta.to_string n) (match el with [] -> "" | _ -> "(" ^ (String.concat ", " (List.map Ast.Printer.s_expr el)) ^ ")") (loop e)
-	| TIdent s ->
-		s
-
-let rec s_expr_ast print_var_ids tabs s_type e =
-	let sprintf = Printf.sprintf in
-	let loop ?(extra_tabs="") = s_expr_ast print_var_ids (tabs ^ "\t" ^ extra_tabs) s_type in
-	let tag_args tabs sl = match sl with
-		| [] -> ""
-		| [s] when not (String.contains s '\n') -> " " ^ s
-		| _ ->
-			let tabs = "\n" ^ tabs ^ "\t" in
-			tabs ^ (String.concat tabs sl)
-	in
-	let tag s ?(t=None) ?(extra_tabs="") sl =
-		let st = match t with
-			| None -> s_type e.etype
-			| Some t -> s_type t
-		in
-		sprintf "[%s:%s]%s" s st (tag_args (tabs ^ extra_tabs) sl)
-	in
-	let var_id v = if print_var_ids then v.v_id else 0 in
-	let const c t = tag "Const" ~t [s_const c] in
-	let local v t = sprintf "[Local %s(%i):%s%s]" v.v_name (var_id v) (s_type v.v_type) (match t with None -> "" | Some t -> ":" ^ (s_type t)) in
-	let var v sl = sprintf "[Var %s(%i):%s]%s" v.v_name (var_id v) (s_type v.v_type) (tag_args tabs sl) in
-	let module_type mt = sprintf "[TypeExpr %s:%s]" (s_type_path (t_path mt)) (s_type e.etype) in
-	match e.eexpr with
-	| TConst c -> const c (Some e.etype)
-	| TLocal v -> local v (Some e.etype)
-	| TArray (e1,e2) -> tag "Array" [loop e1; loop e2]
-	| TBinop (op,e1,e2) -> tag "Binop" [loop e1; s_binop op; loop e2]
-	| TUnop (op,flag,e1) -> tag "Unop" [s_unop op; if flag = Postfix then "Postfix" else "Prefix"; loop e1]
-	| TEnumParameter (e1,ef,i) -> tag "EnumParameter" [loop e1; ef.ef_name; string_of_int i]
-	| TEnumIndex e1 -> tag "EnumIndex" [loop e1]
-	| TField (e1,fa) ->
-		let sfa = match fa with
-			| FInstance(c,tl,cf) -> tag "FInstance" ~extra_tabs:"\t" [s_type (TInst(c,tl)); Printf.sprintf "%s:%s" cf.cf_name (s_type cf.cf_type)]
-			| FStatic(c,cf) -> tag "FStatic" ~extra_tabs:"\t" [s_type_path c.cl_path; Printf.sprintf "%s:%s" cf.cf_name (s_type cf.cf_type)]
-			| FClosure(co,cf) -> tag "FClosure" ~extra_tabs:"\t" [(match co with None -> "None" | Some (c,tl) -> s_type (TInst(c,tl))); Printf.sprintf "%s:%s" cf.cf_name (s_type cf.cf_type)]
-			| FAnon cf -> tag "FAnon" ~extra_tabs:"\t" [Printf.sprintf "%s:%s" cf.cf_name (s_type cf.cf_type)]
-			| FDynamic s -> tag "FDynamic" ~extra_tabs:"\t" [s]
-			| FEnum(en,ef) -> tag "FEnum" ~extra_tabs:"\t" [s_type_path en.e_path; ef.ef_name]
-		in
-		tag "Field" [loop e1; sfa]
-	| TTypeExpr mt -> module_type mt
-	| TParenthesis e1 -> tag "Parenthesis" [loop e1]
-	| TObjectDecl fl -> tag "ObjectDecl" (List.map (fun ((s,_,qs),e) -> sprintf "%s: %s" (s_object_key_name s qs) (loop e)) fl)
-	| TArrayDecl el -> tag "ArrayDecl" (List.map loop el)
-	| TCall (e1,el) -> tag "Call" (loop e1 :: (List.map loop el))
-	| TNew (c,tl,el) -> tag "New" ((s_type (TInst(c,tl))) :: (List.map loop el))
-	| TFunction f ->
-		let arg (v,cto) =
-			tag "Arg" ~t:(Some v.v_type) ~extra_tabs:"\t" (match cto with None -> [local v None] | Some ct -> [local v None;loop ct])
-		in
-		tag "Function" ((List.map arg f.tf_args) @ [loop f.tf_expr])
-	| TVar (v,eo) -> var v (match eo with None -> [] | Some e -> [loop e])
-	| TBlock el -> tag "Block" (List.map loop el)
-	| TIf (e,e1,e2) -> tag "If" (loop e :: (Printf.sprintf "[Then:%s] %s" (s_type e1.etype) (loop e1)) :: (match e2 with None -> [] | Some e -> [Printf.sprintf "[Else:%s] %s" (s_type e.etype) (loop e)]))
-	| TCast (e1,None) -> tag "Cast" [loop e1]
-	| TCast (e1,Some mt) -> tag "Cast" [loop e1; module_type mt]
-	| TThrow e1 -> tag "Throw" [loop e1]
-	| TBreak -> tag "Break" []
-	| TContinue -> tag "Continue" []
-	| TReturn None -> tag "Return" []
-	| TReturn (Some e1) -> tag "Return" [loop e1]
-	| TWhile (e1,e2,NormalWhile) -> tag "While" [loop e1; loop e2]
-	| TWhile (e1,e2,DoWhile) -> tag "Do" [loop e1; loop e2]
-	| TFor (v,e1,e2) -> tag "For" [local v None; loop e1; loop e2]
-	| TTry (e1,catches) ->
-		let sl = List.map (fun (v,e) ->
-			sprintf "Catch %s%s" (local v None) (tag_args (tabs ^ "\t") [loop ~extra_tabs:"\t" e]);
-		) catches in
-		tag "Try" ((loop e1) :: sl)
-	| TSwitch (e1,cases,eo) ->
-		let sl = List.map (fun (el,e) ->
-			tag "Case" ~t:(Some e.etype) ~extra_tabs:"\t" ((List.map loop el) @ [loop ~extra_tabs:"\t" e])
-		) cases in
-		let sl = match eo with
-			| None -> sl
-			| Some e -> sl @ [tag "Default" ~t:(Some e.etype) ~extra_tabs:"\t" [loop ~extra_tabs:"\t" e]]
-		in
-		tag "Switch" ((loop e1) :: sl)
-	| TMeta ((m,el,_),e1) ->
-		let s = Meta.to_string m in
-		let s = match el with
-			| [] -> s
-			| _ -> sprintf "%s(%s)" s (String.concat ", " (List.map Ast.Printer.s_expr el))
-		in
-		tag "Meta" [s; loop e1]
-	| TIdent s ->
-		tag "Ident" [s]
-
-let s_types ?(sep = ", ") tl =
-	let pctx = print_context() in
-	String.concat sep (List.map (s_type pctx) tl)
-
-let s_class_kind = function
-	| KNormal ->
-		"KNormal"
-	| KTypeParameter tl ->
-		Printf.sprintf "KTypeParameter [%s]" (s_types tl)
-	| KExpr _ ->
-		"KExpr"
-	| KGeneric ->
-		"KGeneric"
-	| KGenericInstance(c,tl) ->
-		Printf.sprintf "KGenericInstance %s<%s>" (s_type_path c.cl_path) (s_types tl)
-	| KMacroType ->
-		"KMacroType"
-	| KGenericBuild _ ->
-		"KGenericBuild"
-	| KAbstractImpl a ->
-		Printf.sprintf "KAbstractImpl %s" (s_type_path a.a_path)
-
-module Printer = struct
-
-	let s_type t =
-		s_type (print_context()) t
-
-	let s_pair s1 s2 =
-		Printf.sprintf "(%s,%s)" s1 s2
-
-	let s_record_field name value =
-		Printf.sprintf "%s = %s;" name value
-
-	let s_pos p =
-		Printf.sprintf "%s: %i-%i" p.pfile p.pmin p.pmax
-
-	let s_record_fields tabs fields =
-		let sl = List.map (fun (name,value) -> s_record_field name value) fields in
-		Printf.sprintf "{\n%s\t%s\n%s}" tabs (String.concat ("\n\t" ^ tabs) sl) tabs
-
-	let s_list sep f l =
-		"[" ^ (String.concat sep (List.map f l)) ^ "]"
-
-	let s_opt f o = match o with
-		| None -> "None"
-		| Some v -> f v
-
-	let s_pmap fk fv pm =
-		"{" ^ (String.concat ", " (PMap.foldi (fun k v acc -> (Printf.sprintf "%s = %s" (fk k) (fv v)) :: acc) pm [])) ^ "}"
-
-	let s_doc = s_opt (fun s -> s)
-
-	let s_metadata_entry (s,el,_) =
-		Printf.sprintf "@%s%s" (Meta.to_string s) (match el with [] -> "" | el -> "(" ^ (String.concat ", " (List.map Ast.Printer.s_expr el)) ^ ")")
-
-	let s_metadata metadata =
-		s_list " " s_metadata_entry metadata
-
-	let s_type_param (s,t) = match follow t with
-		| TInst({cl_kind = KTypeParameter tl1},tl2) ->
-			begin match tl1 with
-			| [] -> s
-			| _ -> Printf.sprintf "%s:%s" s (String.concat ", " (List.map s_type tl1))
-			end
-		| _ -> assert false
-
-	let s_type_params tl =
-		s_list ", " s_type_param tl
-
-	let s_tclass_field tabs cf =
-		s_record_fields tabs [
-			"cf_name",cf.cf_name;
-			"cf_doc",s_doc cf.cf_doc;
-			"cf_type",s_type_kind (follow cf.cf_type);
-			"cf_pos",s_pos cf.cf_pos;
-			"cf_name_pos",s_pos cf.cf_name_pos;
-			"cf_meta",s_metadata cf.cf_meta;
-			"cf_kind",s_kind cf.cf_kind;
-			"cf_params",s_type_params cf.cf_params;
-			"cf_expr",s_opt (s_expr_ast true "\t\t" s_type) cf.cf_expr;
-		]
-
-	let s_tclass tabs c =
-		s_record_fields tabs [
-			"cl_path",s_type_path c.cl_path;
-			"cl_module",s_type_path c.cl_module.m_path;
-			"cl_pos",s_pos c.cl_pos;
-			"cl_name_pos",s_pos c.cl_name_pos;
-			"cl_private",string_of_bool c.cl_private;
-			"cl_doc",s_doc c.cl_doc;
-			"cl_meta",s_metadata c.cl_meta;
-			"cl_params",s_type_params c.cl_params;
-			"cl_kind",s_class_kind c.cl_kind;
-			"cl_extern",string_of_bool c.cl_extern;
-			"cl_final",string_of_bool c.cl_final;
-			"cl_interface",string_of_bool c.cl_interface;
-			"cl_super",s_opt (fun (c,tl) -> s_type (TInst(c,tl))) c.cl_super;
-			"cl_implements",s_list ", " (fun (c,tl) -> s_type (TInst(c,tl))) c.cl_implements;
-			"cl_array_access",s_opt s_type c.cl_array_access;
-			"cl_overrides",s_list "," (fun cf -> cf.cf_name) c.cl_overrides;
-			"cl_init",s_opt (s_expr_ast true "" s_type) c.cl_init;
-			"cl_constructor",s_opt (s_tclass_field (tabs ^ "\t")) c.cl_constructor;
-			"cl_ordered_fields",s_list "\n\t" (s_tclass_field (tabs ^ "\t")) c.cl_ordered_fields;
-			"cl_ordered_statics",s_list "\n\t" (s_tclass_field (tabs ^ "\t")) c.cl_ordered_statics;
-		]
-
-	let s_tdef tabs t =
-		s_record_fields tabs [
-			"t_path",s_type_path t.t_path;
-			"t_module",s_type_path t.t_module.m_path;
-			"t_pos",s_pos t.t_pos;
-			"t_name_pos",s_pos t.t_name_pos;
-			"t_private",string_of_bool t.t_private;
-			"t_doc",s_doc t.t_doc;
-			"t_meta",s_metadata t.t_meta;
-			"t_params",s_type_params t.t_params;
-			"t_type",s_type_kind t.t_type
-		]
-
-	let s_tenum_field tabs ef =
-		s_record_fields tabs [
-			"ef_name",ef.ef_name;
-			"ef_doc",s_doc ef.ef_doc;
-			"ef_pos",s_pos ef.ef_pos;
-			"ef_name_pos",s_pos ef.ef_name_pos;
-			"ef_type",s_type_kind ef.ef_type;
-			"ef_index",string_of_int ef.ef_index;
-			"ef_params",s_type_params ef.ef_params;
-			"ef_meta",s_metadata ef.ef_meta
-		]
-
-	let s_tenum tabs en =
-		s_record_fields tabs [
-			"e_path",s_type_path en.e_path;
-			"e_module",s_type_path en.e_module.m_path;
-			"e_pos",s_pos en.e_pos;
-			"e_name_pos",s_pos en.e_name_pos;
-			"e_private",string_of_bool en.e_private;
-			"d_doc",s_doc en.e_doc;
-			"e_meta",s_metadata en.e_meta;
-			"e_params",s_type_params en.e_params;
-			"e_type",s_tdef "\t" en.e_type;
-			"e_extern",string_of_bool en.e_extern;
-			"e_constrs",s_list "\n\t" (s_tenum_field (tabs ^ "\t")) (PMap.fold (fun ef acc -> ef :: acc) en.e_constrs []);
-			"e_names",String.concat ", " en.e_names
-		]
-
-	let s_tabstract tabs a =
-		s_record_fields tabs [
-			"a_path",s_type_path a.a_path;
-			"a_modules",s_type_path a.a_module.m_path;
-			"a_pos",s_pos a.a_pos;
-			"a_name_pos",s_pos a.a_name_pos;
-			"a_private",string_of_bool a.a_private;
-			"a_doc",s_doc a.a_doc;
-			"a_meta",s_metadata a.a_meta;
-			"a_params",s_type_params a.a_params;
-			"a_ops",s_list ", " (fun (op,cf) -> Printf.sprintf "%s: %s" (s_binop op) cf.cf_name) a.a_ops;
-			"a_unops",s_list ", " (fun (op,flag,cf) -> Printf.sprintf "%s (%s): %s" (s_unop op) (if flag = Postfix then "postfix" else "prefix") cf.cf_name) a.a_unops;
-			"a_impl",s_opt (fun c -> s_type_path c.cl_path) a.a_impl;
-			"a_this",s_type_kind a.a_this;
-			"a_from",s_list ", " s_type_kind a.a_from;
-			"a_to",s_list ", " s_type_kind a.a_to;
-			"a_from_field",s_list ", " (fun (t,cf) -> Printf.sprintf "%s: %s" (s_type_kind t) cf.cf_name) a.a_from_field;
-			"a_to_field",s_list ", " (fun (t,cf) -> Printf.sprintf "%s: %s" (s_type_kind t) cf.cf_name) a.a_to_field;
-			"a_array",s_list ", " (fun cf -> cf.cf_name) a.a_array;
-			"a_read",s_opt (fun cf -> cf.cf_name) a.a_read;
-			"a_write",s_opt (fun cf -> cf.cf_name) a.a_write;
-		]
-
-	let s_tvar_extra (tl,eo) =
-		Printf.sprintf "Some(%s, %s)" (s_type_params tl) (s_opt (s_expr_ast true "" s_type) eo)
-
-	let s_tvar v =
-		s_record_fields "" [
-			"v_id",string_of_int v.v_id;
-			"v_name",v.v_name;
-			"v_type",s_type v.v_type;
-			"v_capture",string_of_bool v.v_capture;
-			"v_extra",s_opt s_tvar_extra v.v_extra;
-			"v_meta",s_metadata v.v_meta;
-		]
-
-	let s_module_kind = function
-		| MCode -> "MCode"
-		| MMacro -> "MMacro"
-		| MFake -> "MFake"
-		| MExtern -> "MExtern"
-		| MImport -> "MImport"
-
-	let s_module_def_extra tabs me =
-		s_record_fields tabs [
-			"m_file",me.m_file;
-			"m_sign",me.m_sign;
-			"m_time",string_of_float me.m_time;
-			"m_dirty",s_opt s_type_path me.m_dirty;
-			"m_added",string_of_int me.m_added;
-			"m_mark",string_of_int me.m_mark;
-			"m_deps",s_pmap string_of_int (fun m -> snd m.m_path) me.m_deps;
-			"m_processed",string_of_int me.m_processed;
-			"m_kind",s_module_kind me.m_kind;
-			"m_binded_res",""; (* TODO *)
-			"m_if_feature",""; (* TODO *)
-			"m_features",""; (* TODO *)
-		]
-
-	let s_module_def m =
-		s_record_fields "" [
-			"m_id",string_of_int m.m_id;
-			"m_path",s_type_path m.m_path;
-			"m_extra",s_module_def_extra "\t" m.m_extra
-		]
-
-	let s_type_path tp =
-		s_record_fields "" [
-			"tpackage",s_list "." (fun s -> s) tp.tpackage;
-			"tname",tp.tname;
-			"tparams","";
-			"tsub",s_opt (fun s -> s) tp.tsub;
-		]
-
-	let s_class_flag = function
-		| HInterface -> "HInterface"
-		| HExtern -> "HExtern"
-		| HPrivate -> "HPrivate"
-		| HExtends tp -> "HExtends " ^ (s_type_path (fst tp))
-		| HImplements tp -> "HImplements " ^ (s_type_path (fst tp))
-		| HFinal -> "HFinal"
-
-	let s_placed f (x,p) =
-		s_pair (f x) (s_pos p)
-
-	let s_class_field cff =
-		s_record_fields "" [
-			"cff_name",s_placed (fun s -> s) cff.cff_name;
-			"cff_doc",s_opt (fun s -> s) cff.cff_doc;
-			"cff_pos",s_pos cff.cff_pos;
-			"cff_meta",s_metadata cff.cff_meta;
-			"cff_access",s_list ", " Ast.s_placed_access cff.cff_access;
-		]
-end
-
-(* ======= Unification ======= *)
-
-type unify_error =
-	| Cannot_unify of t * t
-	| Invalid_field_type of string
-	| Has_no_field of t * string
-	| Has_no_runtime_field of t * string
-	| Has_extra_field of t * string
-	| Invalid_kind of string * field_kind * field_kind
-	| Invalid_visibility of string
-	| Not_matching_optional of string
-	| Cant_force_optional
-	| Invariant_parameter of int
-	| Constraint_failure of string
-	| Missing_overload of tclass_field * t
-	| FinalInvariance (* nice band name *)
-	| Invalid_function_argument of int (* index *) * int (* total *)
-	| Invalid_return_type
-	| Unify_custom of string
-
-exception Unify_error of unify_error list
-
-let error l = raise (Unify_error l)
-
-let check_constraint name f =
-	try
-		f()
-	with Unify_error l ->
-		raise (Unify_error ((Constraint_failure name) :: l))
-
-module Monomorph = struct
-	let create () = {
-		tm_type = None;
-		tm_constraint = None;
-	}
-
-	let unify_merge a b = match a,b with
-		| TAnon an1,TAnon an2 ->
-			let old1 = !(an1.a_status) in
-			let old2 = !(an2.a_status) in
-			an1.a_status := Opened;
-			an2.a_status := Opened;
-			Std.finally (fun () ->
-				an1.a_status := old1;
-				an1.a_status := old2;
-			) (!unify_ref a) b
-		| _ ->
-			!unify_ref a b
-
-	let set_constraint m path p constr =
-		assert(m.tm_type = None);
-		assert(m.tm_constraint = None);
-		m.tm_constraint <- Some (constr,path,p)
-
-	let constrain_to_object m path p tl = set_constraint m path p (CTypes tl)
-
-	let constrain_to_fields m path p fl =
-		let anon = { a_fields = fl; a_status = ref Opened } in
-		set_constraint m path p (CStructure(TAnon anon,anon))
-
-	let do_bind m t =
-		(* assert(m.tm_type = None); *) (* TODO: should be here, but matcher.ml does some weird bind handling at the moment. *)
-		m.tm_type <- Some t
-
-	let merge_constraints mono_to mono_from = match mono_from.tm_constraint with
-		| None ->
-			()
-		| Some cstr -> match mono_to.tm_constraint with
-			| None ->
-				mono_to.tm_constraint <- Some cstr
-			| Some cstr2 -> match cstr,cstr2 with
-				| (CStructure(t1,anon1),path,p),(CStructure(t2,_),_,_) ->
-					!unify_ref t1 t2;
-					mono_to.tm_constraint <- Some(CStructure(t1,anon1),path,p)
-				| (CTypes tl1,path,p),(CTypes tl2,_,_) ->
-					mono_to.tm_constraint <- Some(CTypes (tl1 @ tl2),path,p);
-				| _ ->
-					error [Unify_custom "Cannot merge constraints"]
-
-	let rec bind m t =
-		begin match t with
-		| TMono m2 ->
-			begin match m2.tm_type with
-			| None ->
-				(* Inherit constraints. This avoids too-early unification. *)
-				merge_constraints m2 m;
-				do_bind m t
-			| Some t ->
-				bind m t
-			end;
-		| _ ->
-			Option.may (fun (cstr,path,p) -> match cstr with
-				| CStructure(tanon,anon) ->
-					if not (PMap.is_empty anon.a_fields) then check_constraint path (fun () ->
-						unify_merge t tanon;
-					)
-				| CTypes tl ->
-					check_constraint path (fun () ->
-						List.iter (unify_merge t) tl
-					)
-			) m.tm_constraint;
-			do_bind m t;
-		end
-
-	let unbind m =
-		m.tm_type <- None
-end
-
-let rec link e a b =
-	(* tell if setting a == b will create a type-loop *)
-	let rec loop t =
-		if t == a then
-			true
-		else match t with
-		| TMono t -> (match t.tm_type with None -> false | Some t -> loop t)
-		| TEnum (_,tl) -> List.exists loop tl
-		| TInst (_,tl) | TType (_,tl) | TAbstract (_,tl) -> List.exists loop tl
-		| TFun (tl,t) -> List.exists (fun (_,_,t) -> loop t) tl || loop t
-		| TDynamic t2 ->
-			if t == t2 then
-				false
-			else
-				loop t2
-		| TLazy f ->
-			loop (lazy_type f)
-		| TAnon a ->
-			try
-				PMap.iter (fun _ f -> if loop f.cf_type then raise Exit) a.a_fields;
-				false
-			with
-				Exit -> true
-	in
-	(* tell is already a ~= b *)
-	if loop b then
-		(follow b) == a
-	else if b == t_dynamic then
-		true
-	else begin
-		Monomorph.bind e b;
-		true
-	end
-
-let would_produce_recursive_anon field_acceptor field_donor =
-	try
-		(match !(field_acceptor.a_status) with
-		| Opened ->
-			PMap.iter (fun n field ->
-				match follow field.cf_type with
-				| TAnon a when field_acceptor == a -> raise Exit
-				| _ -> ()
-			) field_donor.a_fields;
-		| _ -> ());
-		false
-	with Exit -> true
-
-let link_dynamic a b = match follow a,follow b with
-	| TMono r,TDynamic _ -> Monomorph.bind r b
-	| TDynamic _,TMono r -> Monomorph.bind r a
-	| _ -> ()
-
-let fast_eq_check type_param_check a b =
-	if a == b then
-		true
-	else match a , b with
-	| TFun (l1,r1) , TFun (l2,r2) when List.length l1 = List.length l2 ->
-		List.for_all2 (fun (_,_,t1) (_,_,t2) -> type_param_check t1 t2) l1 l2 && type_param_check r1 r2
-	| TType (t1,l1), TType (t2,l2) ->
-		t1 == t2 && List.for_all2 type_param_check l1 l2
-	| TEnum (e1,l1), TEnum (e2,l2) ->
-		e1 == e2 && List.for_all2 type_param_check l1 l2
-	| TInst (c1,l1), TInst (c2,l2) ->
-		c1 == c2 && List.for_all2 type_param_check l1 l2
-	| TAbstract (a1,l1), TAbstract (a2,l2) ->
-		a1 == a2 && List.for_all2 type_param_check l1 l2
-	| _ , _ ->
-		false
-
-let rec fast_eq a b = fast_eq_check fast_eq a b
-
-let rec fast_eq_mono ml a b =
-	if fast_eq_check (fast_eq_mono ml) a b then
-		true
-	else match a , b with
-	| TMono _, _ ->
-		List.memq a ml
-	| _ , _ ->
-		false
-
-let rec shallow_eq a b =
-	a == b
-	|| begin
-		let a = follow a
-		and b = follow b in
-		fast_eq_check shallow_eq a b
-		|| match a , b with
-			| t, TMono { tm_type = None } when t == t_dynamic -> true
-			| TMono { tm_type = None }, t when t == t_dynamic -> true
-			| TMono { tm_type = None }, TMono { tm_type = None } -> true
-			| TAnon a1, TAnon a2 ->
-				let fields_eq() =
-					let rec loop fields1 fields2 =
-						match fields1, fields2 with
-						| [], [] -> true
-						| _, [] | [], _ -> false
-						| f1 :: rest1, f2 :: rest2 ->
-							f1.cf_name = f2.cf_name
-							&& (try shallow_eq f1.cf_type f2.cf_type with Not_found -> false)
-							&& loop rest1 rest2
-					in
-					let fields1 = PMap.fold (fun field fields -> field :: fields) a1.a_fields []
-					and fields2 = PMap.fold (fun field fields -> field :: fields) a2.a_fields []
-					and sort_compare f1 f2 = compare f1.cf_name f2.cf_name in
-					loop (List.sort sort_compare fields1) (List.sort sort_compare fields2)
-				in
-				(match !(a2.a_status), !(a1.a_status) with
-				| Statics c, Statics c2 -> c == c2
-				| EnumStatics e, EnumStatics e2 -> e == e2
-				| AbstractStatics a, AbstractStatics a2 -> a == a2
-				| Extend tl1, Extend tl2 -> fields_eq() && List.for_all2 shallow_eq tl1 tl2
-				| Closed, Closed -> fields_eq()
-				| Opened, Opened -> fields_eq()
-				| Const, Const -> fields_eq()
-				| _ -> false
-				)
-			| _ , _ ->
-				false
-	end
-
-(* perform unification with subtyping.
-   the first type is always the most down in the class hierarchy
-   it's also the one that is pointed by the position.
-   It's actually a typecheck of  A :> B where some mutations can happen *)
-
-let cannot_unify a b = Cannot_unify (a,b)
-let invalid_field n = Invalid_field_type n
-let invalid_kind n a b = Invalid_kind (n,a,b)
-let invalid_visibility n = Invalid_visibility n
-let has_no_field t n = Has_no_field (t,n)
-let has_extra_field t n = Has_extra_field (t,n)
-let has_meta m ml = List.exists (fun (m2,_,_) -> m = m2) ml
-let get_meta m ml = List.find (fun (m2,_,_) -> m = m2) ml
-let no_meta = []
-
-(*
-	we can restrict access as soon as both are runtime-compatible
-*)
-let unify_access a1 a2 =
-	a1 = a2 || match a1, a2 with
-	| _, AccNo | _, AccNever -> true
-	| AccInline, AccNormal -> true
-	| _ -> false
-
-let direct_access = function
-	| AccNo | AccNever | AccNormal | AccInline | AccRequire _ | AccCtor -> true
-	| AccResolve | AccCall -> false
-
-let unify_kind k1 k2 =
-	k1 = k2 || match k1, k2 with
-		| Var v1, Var v2 -> unify_access v1.v_read v2.v_read && unify_access v1.v_write v2.v_write
-		| Var v, Method m ->
-			(match v.v_read, v.v_write, m with
-			| AccNormal, _, MethNormal -> true
-			| AccNormal, AccNormal, MethDynamic -> true
-			| _ -> false)
-		| Method m, Var v ->
-			(match m with
-			| MethDynamic -> direct_access v.v_read && direct_access v.v_write
-			| MethMacro -> false
-			| MethNormal | MethInline ->
-				match v.v_read,v.v_write with
-				| AccNormal,(AccNo | AccNever) -> true
-				| _ -> false)
-		| Method m1, Method m2 ->
-			match m1,m2 with
-			| MethInline, MethNormal
-			| MethDynamic, MethNormal -> true
-			| _ -> false
-
-type 'a rec_stack = {
-	mutable rec_stack : 'a list;
-}
-
-let new_rec_stack() = { rec_stack = [] }
-let rec_stack_exists f s = List.exists f s.rec_stack
-let rec_stack_memq v s = List.memq v s.rec_stack
-let rec_stack_loop stack value f arg =
-	stack.rec_stack <- value :: stack.rec_stack;
-	try
-		let r = f arg in
-		stack.rec_stack <- List.tl stack.rec_stack;
-		r
-	with e ->
-		stack.rec_stack <- List.tl stack.rec_stack;
-		raise e
-
-let eq_stack = new_rec_stack()
-
-let rec_stack stack value fcheck frun ferror =
-	if not (rec_stack_exists fcheck stack) then begin
-		try
-			stack.rec_stack <- value :: stack.rec_stack;
-			let v = frun() in
-			stack.rec_stack <- List.tl stack.rec_stack;
-			v
-		with
-			Unify_error l ->
-				stack.rec_stack <- List.tl stack.rec_stack;
-				ferror l
-			| e ->
-				stack.rec_stack <- List.tl stack.rec_stack;
-				raise e
-	end
-
-let rec_stack_default stack value fcheck frun def =
-	if not (rec_stack_exists fcheck stack) then rec_stack_loop stack value frun () else def
-
-let rec_stack_bool stack value fcheck frun =
-	if (rec_stack_exists fcheck stack) then false else begin
-		try
-			stack.rec_stack <- value :: stack.rec_stack;
-			frun();
-			stack.rec_stack <- List.tl stack.rec_stack;
-			true
-		with
-			Unify_error l ->
-				stack.rec_stack <- List.tl stack.rec_stack;
-				false
-			| e ->
-				stack.rec_stack <- List.tl stack.rec_stack;
-				raise e
-	end
-
-type eq_kind =
-	| EqStrict
-	| EqCoreType
-	| EqRightDynamic
-	| EqBothDynamic
-	| EqDoNotFollowNull (* like EqStrict, but does not follow Null<T> *)
-
-let rec type_eq param a b =
-	let can_follow t = match param with
-		| EqCoreType -> false
-		| EqDoNotFollowNull -> not (is_explicit_null t)
-		| _ -> true
-	in
-	if a == b then
-		()
-	else match a , b with
-	| TLazy f , _ -> type_eq param (lazy_type f) b
-	| _ , TLazy f -> type_eq param a (lazy_type f)
-	| TMono t , _ ->
-		(match t.tm_type with
-		| None -> if param = EqCoreType || not (link t a b) then error [cannot_unify a b]
-		| Some t -> type_eq param t b)
-	| _ , TMono t ->
-		(match t.tm_type with
-		| None -> if param = EqCoreType || not (link t b a) then error [cannot_unify a b]
-		| Some t -> type_eq param a t)
-	| TAbstract ({a_path=[],"Null"},[t1]),TAbstract ({a_path=[],"Null"},[t2]) ->
-		type_eq param t1 t2
-	| TAbstract ({a_path=[],"Null"},[t]),_ when param <> EqDoNotFollowNull ->
-		type_eq param t b
-	| _,TAbstract ({a_path=[],"Null"},[t]) when param <> EqDoNotFollowNull ->
-		type_eq param a t
-	| TType (t1,tl1), TType (t2,tl2) when (t1 == t2 || (param = EqCoreType && t1.t_path = t2.t_path)) && List.length tl1 = List.length tl2 ->
-		type_eq_params param a b tl1 tl2
-	| TType (t,tl) , _ when can_follow a ->
-		type_eq param (apply_params t.t_params tl t.t_type) b
-	| _ , TType (t,tl) when can_follow b ->
-		rec_stack eq_stack (a,b)
-			(fun (a2,b2) -> fast_eq a a2 && fast_eq b b2)
-			(fun() -> type_eq param a (apply_params t.t_params tl t.t_type))
-			(fun l -> error (cannot_unify a b :: l))
-	| TEnum (e1,tl1) , TEnum (e2,tl2) ->
-		if e1 != e2 && not (param = EqCoreType && e1.e_path = e2.e_path) then error [cannot_unify a b];
-		type_eq_params param a b tl1 tl2
-	| TInst (c1,tl1) , TInst (c2,tl2) ->
-		if c1 != c2 && not (param = EqCoreType && c1.cl_path = c2.cl_path) && (match c1.cl_kind, c2.cl_kind with KExpr _, KExpr _ -> false | _ -> true) then error [cannot_unify a b];
-		type_eq_params param a b tl1 tl2
-	| TFun (l1,r1) , TFun (l2,r2) when List.length l1 = List.length l2 ->
-		let i = ref 0 in
-		(try
-			type_eq param r1 r2;
-			List.iter2 (fun (n,o1,t1) (_,o2,t2) ->
-				incr i;
-				if o1 <> o2 then error [Not_matching_optional n];
-				type_eq param t1 t2
-			) l1 l2
-		with
-			Unify_error l ->
-				let msg = if !i = 0 then Invalid_return_type else Invalid_function_argument(!i,List.length l1) in
-				error (cannot_unify a b :: msg :: l)
-		)
-	| TDynamic a , TDynamic b ->
-		type_eq param a b
-	| TAbstract (a1,tl1) , TAbstract (a2,tl2) ->
-		if a1 != a2 && not (param = EqCoreType && a1.a_path = a2.a_path) then error [cannot_unify a b];
-		type_eq_params param a b tl1 tl2
-	| TAnon a1, TAnon a2 ->
-		(try
-			(match !(a2.a_status) with
-			| Statics c -> (match !(a1.a_status) with Statics c2 when c == c2 -> () | _ -> error [])
-			| EnumStatics e -> (match !(a1.a_status) with EnumStatics e2 when e == e2 -> () | _ -> error [])
-			| AbstractStatics a -> (match !(a1.a_status) with AbstractStatics a2 when a == a2 -> () | _ -> error [])
-			| _ -> ()
-			);
-			if would_produce_recursive_anon a1 a2 || would_produce_recursive_anon a2 a1 then error [cannot_unify a b];
-			PMap.iter (fun n f1 ->
-				try
-					let f2 = PMap.find n a2.a_fields in
-					if f1.cf_kind <> f2.cf_kind && (param = EqStrict || param = EqCoreType || not (unify_kind f1.cf_kind f2.cf_kind)) then error [invalid_kind n f1.cf_kind f2.cf_kind];
-					let a = f1.cf_type and b = f2.cf_type in
-					(try type_eq param a b with Unify_error l -> error (invalid_field n :: l));
-					if (has_class_field_flag f1 CfPublic) != (has_class_field_flag f2 CfPublic) then error [invalid_visibility n];
-				with
-					Not_found ->
-						if is_closed a2 then error [has_no_field b n];
-						if not (link (Monomorph.create()) b f1.cf_type) then error [cannot_unify a b];
-						a2.a_fields <- PMap.add n f1 a2.a_fields
-			) a1.a_fields;
-			PMap.iter (fun n f2 ->
-				if not (PMap.mem n a1.a_fields) then begin
-					if is_closed a1 then error [has_no_field a n];
-					if not (link (Monomorph.create()) a f2.cf_type) then error [cannot_unify a b];
-					a1.a_fields <- PMap.add n f2 a1.a_fields
-				end;
-			) a2.a_fields;
-		with
-			Unify_error l -> error (cannot_unify a b :: l))
-	| _ , _ ->
-		if b == t_dynamic && (param = EqRightDynamic || param = EqBothDynamic) then
-			()
-		else if a == t_dynamic && param = EqBothDynamic then
-			()
-		else
-			error [cannot_unify a b]
-
-and type_eq_params param a b tl1 tl2 =
-	let i = ref 0 in
-	List.iter2 (fun t1 t2 ->
-		incr i;
-		try
-			type_eq param t1 t2
-		with Unify_error l ->
-			let err = cannot_unify a b in
-			error (err :: (Invariant_parameter !i) :: l)
-		) tl1 tl2
-
-let type_iseq a b =
-	try
-		type_eq EqStrict a b;
-		true
-	with
-		Unify_error _ -> false
-
-let type_iseq_strict a b =
-	try
-		type_eq EqDoNotFollowNull a b;
-		true
-	with Unify_error _ ->
-		false
-
-let unify_stack = new_rec_stack()
-let abstract_cast_stack = new_rec_stack()
-let unify_new_monos = new_rec_stack()
-
-let print_stacks() =
-	let ctx = print_context() in
-	let st = s_type ctx in
-	print_endline "unify_stack";
-	List.iter (fun (a,b) -> Printf.printf "\t%s , %s\n" (st a) (st b)) unify_stack.rec_stack;
-	print_endline "monos";
-	List.iter (fun m -> print_endline ("\t" ^ st m)) unify_new_monos.rec_stack;
-	print_endline "abstract_cast_stack";
-	List.iter (fun (a,b) -> Printf.printf "\t%s , %s\n" (st a) (st b)) abstract_cast_stack.rec_stack
-
-let rec unify a b =
-	if a == b then
-		()
-	else match a, b with
-	| TLazy f , _ -> unify (lazy_type f) b
-	| _ , TLazy f -> unify a (lazy_type f)
-	| TMono t , _ ->
-		(match t.tm_type with
-		| None -> if not (link t a b) then error [cannot_unify a b]
-		| Some t -> unify t b)
-	| _ , TMono t ->
-		(match t.tm_type with
-		| None -> if not (link t b a) then error [cannot_unify a b]
-		| Some t -> unify a t)
-	| TType (t,tl) , _ ->
-		rec_stack unify_stack (a,b)
-			(fun(a2,b2) -> fast_eq a a2 && fast_eq b b2)
-			(fun() -> try_apply_params_rec t.t_params tl t.t_type (fun a -> unify a b))
-			(fun l -> error (cannot_unify a b :: l))
-	| _ , TType (t,tl) ->
-		rec_stack unify_stack (a,b)
-			(fun(a2,b2) -> fast_eq a a2 && fast_eq b b2)
-			(fun() -> try_apply_params_rec t.t_params tl t.t_type (unify a))
-			(fun l -> error (cannot_unify a b :: l))
-	| TEnum (ea,tl1) , TEnum (eb,tl2) ->
-		if ea != eb then error [cannot_unify a b];
-		unify_type_params a b tl1 tl2
-	| TAbstract ({a_path=[],"Null"},[t]),_ ->
-		begin try unify t b
-		with Unify_error l -> error (cannot_unify a b :: l) end
-	| _,TAbstract ({a_path=[],"Null"},[t]) ->
-		begin try unify a t
-		with Unify_error l -> error (cannot_unify a b :: l) end
-	| TAbstract (a1,tl1) , TAbstract (a2,tl2) when a1 == a2 ->
-		begin try
-			unify_type_params a b tl1 tl2
-		with Unify_error _ as err ->
-			(* the type could still have a from/to relation to itself (issue #3494) *)
-			begin try
-				unify_abstracts a b a1 tl1 a2 tl2
-			with Unify_error _ ->
-				raise err
-			end
-		end
-	| TAbstract ({a_path=[],"Void"},_) , _
-	| _ , TAbstract ({a_path=[],"Void"},_) ->
-		error [cannot_unify a b]
-	| TAbstract (a1,tl1) , TAbstract (a2,tl2) ->
-		unify_abstracts a b a1 tl1 a2 tl2
-	| TInst (c1,tl1) , TInst (c2,tl2) ->
-		let rec loop c tl =
-			if c == c2 then begin
-				unify_type_params a b tl tl2;
-				true
-			end else (match c.cl_super with
-				| None -> false
-				| Some (cs,tls) ->
-					loop cs (List.map (apply_params c.cl_params tl) tls)
-			) || List.exists (fun (cs,tls) ->
-				loop cs (List.map (apply_params c.cl_params tl) tls)
-			) c.cl_implements
-			|| (match c.cl_kind with
-			| KTypeParameter pl -> List.exists (fun t ->
-				match follow t with
-				| TInst (cs,tls) -> loop cs (List.map (apply_params c.cl_params tl) tls)
-				| TAbstract(aa,tl) -> List.exists (unify_to aa tl b) aa.a_to
-				| _ -> false
-			) pl
-			| _ -> false)
-		in
-		if not (loop c1 tl1) then error [cannot_unify a b]
-	| TFun (l1,r1) , TFun (l2,r2) when List.length l1 = List.length l2 ->
-		let i = ref 0 in
-		(try
-			(match follow r2 with
-			| TAbstract ({a_path=[],"Void"},_) -> incr i
-			| _ -> unify r1 r2; incr i);
-			List.iter2 (fun (_,o1,t1) (_,o2,t2) ->
-				if o1 && not o2 then error [Cant_force_optional];
-				unify t1 t2;
-				incr i
-			) l2 l1 (* contravariance *)
-		with
-			Unify_error l ->
-				let msg = if !i = 0 then Invalid_return_type else Invalid_function_argument(!i,List.length l1) in
-				error (cannot_unify a b :: msg :: l))
-	| TInst (c,tl) , TAnon an ->
-		if PMap.is_empty an.a_fields then (match c.cl_kind with
-			| KTypeParameter pl ->
-				(* one of the constraints must unify with { } *)
-				if not (List.exists (fun t -> match follow t with TInst _ | TAnon _ -> true | _ -> false) pl) then error [cannot_unify a b]
-			| _ -> ());
-		(try
-			PMap.iter (fun n f2 ->
-				(*
-					introducing monomorphs while unifying might create infinite loops - see #2315
-					let's store these monomorphs and make sure we reach a fixed point
-				*)
-				let monos = ref [] in
-				let make_type f =
-					match f.cf_params with
-					| [] -> f.cf_type
-					| l ->
-						let ml = List.map (fun _ -> mk_mono()) l in
-						monos := ml;
-						apply_params f.cf_params ml f.cf_type
-				in
-				let _, ft, f1 = (try raw_class_field make_type c tl n with Not_found -> error [has_no_field a n]) in
-				let ft = apply_params c.cl_params tl ft in
-				if not (unify_kind f1.cf_kind f2.cf_kind) then error [invalid_kind n f1.cf_kind f2.cf_kind];
-				if (has_class_field_flag f2 CfPublic) && not (has_class_field_flag f1 CfPublic) then error [invalid_visibility n];
-
-				(match f2.cf_kind with
-				| Var { v_read = AccNo } | Var { v_read = AccNever } ->
-					(* we will do a recursive unification, so let's check for possible recursion *)
-					let old_monos = unify_new_monos.rec_stack in
-					unify_new_monos.rec_stack <- !monos @ unify_new_monos.rec_stack;
-					rec_stack unify_stack (ft,f2.cf_type)
-						(fun (a2,b2) -> fast_eq b2 f2.cf_type && fast_eq_mono unify_new_monos.rec_stack ft a2)
-						(fun() -> try unify_with_access f1 ft f2 with e -> unify_new_monos.rec_stack <- old_monos; raise e)
-						(fun l -> error (invalid_field n :: l));
-					unify_new_monos.rec_stack <- old_monos;
-				| Method MethNormal | Method MethInline | Var { v_write = AccNo } | Var { v_write = AccNever } ->
-					(* same as before, but unification is reversed (read-only var) *)
-					let old_monos = unify_new_monos.rec_stack in
-					unify_new_monos.rec_stack <- !monos @ unify_new_monos.rec_stack;
-					rec_stack unify_stack (f2.cf_type,ft)
-						(fun(a2,b2) -> fast_eq_mono unify_new_monos.rec_stack b2 ft && fast_eq f2.cf_type a2)
-						(fun() -> try unify_with_access f1 ft f2 with e -> unify_new_monos.rec_stack <- old_monos; raise e)
-						(fun l -> error (invalid_field n :: l));
-					unify_new_monos.rec_stack <- old_monos;
-				| _ ->
-					(* will use fast_eq, which have its own stack *)
-					try
-						unify_with_access f1 ft f2
-					with
-						Unify_error l ->
-							error (invalid_field n :: l));
-
-				List.iter (fun f2o ->
-					if not (List.exists (fun f1o -> type_iseq f1o.cf_type f2o.cf_type) (f1 :: f1.cf_overloads))
-					then error [Missing_overload (f1, f2o.cf_type)]
-				) f2.cf_overloads;
-				(* we mark the field as :?used because it might be used through the structure *)
-				if not (Meta.has Meta.MaybeUsed f1.cf_meta) then begin
-					f1.cf_meta <- (Meta.MaybeUsed,[],f1.cf_pos) :: f1.cf_meta;
-					match f2.cf_kind with
-					| Var vk ->
-						let check name =
-							try
-								let _,_,cf = raw_class_field make_type c tl name in
-								if not (Meta.has Meta.MaybeUsed cf.cf_meta) then
-									cf.cf_meta <- (Meta.MaybeUsed,[],f1.cf_pos) :: cf.cf_meta
-							with Not_found ->
-								()
-						in
-						(match vk.v_read with AccCall -> check ("get_" ^ f1.cf_name) | _ -> ());
-						(match vk.v_write with AccCall -> check ("set_" ^ f1.cf_name) | _ -> ());
-					| _ -> ()
-				end;
-				(match f1.cf_kind with
-				| Method MethInline ->
-					if (c.cl_extern || has_class_field_flag f1 CfExtern) && not (Meta.has Meta.Runtime f1.cf_meta) then error [Has_no_runtime_field (a,n)];
-				| _ -> ());
-			) an.a_fields;
-			(match !(an.a_status) with
-			| Opened -> an.a_status := Closed;
-			| Statics _ | EnumStatics _ | AbstractStatics _ -> error []
-			| Closed | Extend _ | Const -> ())
-		with
-			Unify_error l -> error (cannot_unify a b :: l))
-	| TAnon a1, TAnon a2 ->
-		unify_anons a b a1 a2
-	| TAnon an, TAbstract ({ a_path = [],"Class" },[pt]) ->
-		(match !(an.a_status) with
-		| Statics cl -> unify (TInst (cl,List.map (fun _ -> mk_mono()) cl.cl_params)) pt
-		| _ -> error [cannot_unify a b])
-	| TAnon an, TAbstract ({ a_path = [],"Enum" },[pt]) ->
-		(match !(an.a_status) with
-		| EnumStatics e -> unify (TEnum (e,List.map (fun _ -> mk_mono()) e.e_params)) pt
-		| _ -> error [cannot_unify a b])
-	| TEnum _, TAbstract ({ a_path = [],"EnumValue" },[]) ->
-		()
-	| TEnum(en,_), TAbstract ({ a_path = ["haxe"],"FlatEnum" },[]) when Meta.has Meta.FlatEnum en.e_meta ->
-		()
-	| TFun _, TAbstract ({ a_path = ["haxe"],"Function" },[]) ->
-		()
-	| TInst(c,tl),TAbstract({a_path = ["haxe"],"Constructible"},[t1]) ->
-		begin try
-			begin match c.cl_kind with
-				| KTypeParameter tl ->
-					(* type parameters require an equal Constructible constraint *)
-					if not (List.exists (fun t -> match follow t with TAbstract({a_path = ["haxe"],"Constructible"},[t2]) -> type_iseq t1 t2 | _ -> false) tl) then error [cannot_unify a b]
-				| _ ->
-					let _,t,cf = class_field c tl "new" in
-					if not (has_class_field_flag cf CfPublic) then error [invalid_visibility "new"];
-					begin try unify t t1
-					with Unify_error l -> error (cannot_unify a b :: l) end
-			end
-		with Not_found ->
-			error [has_no_field a "new"]
-		end
-	| TDynamic t , _ ->
-		if t == a then
-			()
-		else (match b with
-		| TDynamic t2 ->
-			if t2 != b then
-				(try
-					type_eq EqRightDynamic t t2
-				with
-					Unify_error l -> error (cannot_unify a b :: l));
-		| TAbstract(bb,tl) when (List.exists (unify_from bb tl a b) bb.a_from) ->
-			()
-		| _ ->
-			error [cannot_unify a b])
-	| _ , TDynamic t ->
-		if t == b then
-			()
-		else (match a with
-		| TDynamic t2 ->
-			if t2 != a then
-				(try
-					type_eq EqRightDynamic t t2
-				with
-					Unify_error l -> error (cannot_unify a b :: l));
-		| TAnon an ->
-			(try
-				(match !(an.a_status) with
-				| Statics _ | EnumStatics _ -> error []
-				| Opened -> an.a_status := Closed
-				| _ -> ());
-				PMap.iter (fun _ f ->
-					try
-						type_eq EqStrict (field_type f) t
-					with Unify_error l ->
-						error (invalid_field f.cf_name :: l)
-				) an.a_fields
-			with Unify_error l ->
-				error (cannot_unify a b :: l))
-		| TAbstract(aa,tl) when (List.exists (unify_to aa tl b) aa.a_to) ->
-			()
-		| _ ->
-			error [cannot_unify a b])
-	| TAbstract (aa,tl), _  ->
-		if not (List.exists (unify_to aa tl b) aa.a_to) then error [cannot_unify a b];
-	| TInst ({ cl_kind = KTypeParameter ctl } as c,pl), TAbstract (bb,tl) ->
-		(* one of the constraints must satisfy the abstract *)
-		if not (List.exists (fun t ->
-			let t = apply_params c.cl_params pl t in
-			try unify t b; true with Unify_error _ -> false
-		) ctl) && not (List.exists (unify_from bb tl a b) bb.a_from) then error [cannot_unify a b];
-	| _, TAbstract (bb,tl) ->
-		if not (List.exists (unify_from bb tl a b) bb.a_from) then error [cannot_unify a b]
-	| _ , _ ->
-		error [cannot_unify a b]
-
-and unify_abstracts a b a1 tl1 a2 tl2 =
-	let f1 = unify_to a1 tl1 b in
-		let f2 = unify_from a2 tl2 a b in
-		if (List.exists (f1 ~allow_transitive_cast:false) a1.a_to)
-		|| (List.exists (f2 ~allow_transitive_cast:false) a2.a_from)
-		|| (((Meta.has Meta.CoreType a1.a_meta) || (Meta.has Meta.CoreType a2.a_meta))
-			&& ((List.exists f1 a1.a_to) || (List.exists f2 a2.a_from))) then
-			()
-		else
-			error [cannot_unify a b]
-
-and unify_anons a b a1 a2 =
-	if would_produce_recursive_anon a1 a2 then error [cannot_unify a b];
-	(try
-		PMap.iter (fun n f2 ->
-		try
-			let f1 = PMap.find n a1.a_fields in
-			if not (unify_kind f1.cf_kind f2.cf_kind) then
-				(match !(a1.a_status), f1.cf_kind, f2.cf_kind with
-				| Opened, Var { v_read = AccNormal; v_write = AccNo }, Var { v_read = AccNormal; v_write = AccNormal } ->
-					f1.cf_kind <- f2.cf_kind;
-				| _ -> error [invalid_kind n f1.cf_kind f2.cf_kind]);
-			if (has_class_field_flag f2 CfPublic) && not (has_class_field_flag f1 CfPublic) then error [invalid_visibility n];
-			try
-				let f1_type =
-					if fast_eq f1.cf_type f2.cf_type then f1.cf_type
-					else field_type f1
-				in
-				unify_with_access f1 f1_type f2;
-				(match !(a1.a_status) with
-				| Statics c when not (Meta.has Meta.MaybeUsed f1.cf_meta) -> f1.cf_meta <- (Meta.MaybeUsed,[],f1.cf_pos) :: f1.cf_meta
-				| _ -> ());
-			with
-				Unify_error l -> error (invalid_field n :: l)
-		with
-			Not_found ->
-				match !(a1.a_status) with
-				| Opened ->
-					if not (link (Monomorph.create()) a f2.cf_type) then error [];
-					a1.a_fields <- PMap.add n f2 a1.a_fields
-				| Const when Meta.has Meta.Optional f2.cf_meta ->
-					()
-				| _ ->
-					error [has_no_field a n];
-		) a2.a_fields;
-		(match !(a1.a_status) with
-		| Const when not (PMap.is_empty a2.a_fields) ->
-			PMap.iter (fun n _ -> if not (PMap.mem n a2.a_fields) then error [has_extra_field a n]) a1.a_fields;
-		| Opened ->
-			a1.a_status := Closed
-		| _ -> ());
-		(match !(a2.a_status) with
-		| Statics c -> (match !(a1.a_status) with Statics c2 when c == c2 -> () | _ -> error [])
-		| EnumStatics e -> (match !(a1.a_status) with EnumStatics e2 when e == e2 -> () | _ -> error [])
-		| AbstractStatics a -> (match !(a1.a_status) with AbstractStatics a2 when a == a2 -> () | _ -> error [])
-		| Opened -> a2.a_status := Closed
-		| Const | Extend _ | Closed -> ())
-	with
-		Unify_error l -> error (cannot_unify a b :: l))
-
-and unify_from ab tl a b ?(allow_transitive_cast=true) t =
-	rec_stack_bool abstract_cast_stack (a,b)
-		(fun (a2,b2) -> fast_eq a a2 && fast_eq b b2)
-		(fun() ->
-			let t = apply_params ab.a_params tl t in
-			let unify_func = if allow_transitive_cast then unify else type_eq EqRightDynamic in
-			unify_func a t)
-
-and unify_to ab tl b ?(allow_transitive_cast=true) t =
-	let t = apply_params ab.a_params tl t in
-	let unify_func = if allow_transitive_cast then unify else type_eq EqStrict in
-	try
-		unify_func t b;
-		true
-	with Unify_error _ ->
-		false
-
-and unify_from_field ab tl a b ?(allow_transitive_cast=true) (t,cf) =
-	rec_stack_bool abstract_cast_stack (a,b)
-		(fun (a2,b2) -> fast_eq a a2 && fast_eq b b2)
-		(fun() ->
-			let unify_func = if allow_transitive_cast then unify else type_eq EqStrict in
-			match follow cf.cf_type with
-			| TFun(_,r) ->
-				let monos = List.map (fun _ -> mk_mono()) cf.cf_params in
-				let map t = apply_params ab.a_params tl (apply_params cf.cf_params monos t) in
-				unify_func a (map t);
-				List.iter2 (fun m (name,t) -> match follow t with
-					| TInst ({ cl_kind = KTypeParameter constr },_) when constr <> [] ->
-						List.iter (fun tc -> match follow m with TMono _ -> raise (Unify_error []) | _ -> unify m (map tc) ) constr
-					| _ -> ()
-				) monos cf.cf_params;
-				unify_func (map r) b;
-				true
-			| _ -> assert false)
-
-and unify_to_field ab tl b ?(allow_transitive_cast=true) (t,cf) =
-	let a = TAbstract(ab,tl) in
-	rec_stack_bool abstract_cast_stack (b,a)
-		(fun (b2,a2) -> fast_eq a a2 && fast_eq b b2)
-		(fun() ->
-			let unify_func = if allow_transitive_cast then unify else type_eq EqStrict in
-			match follow cf.cf_type with
-			| TFun((_,_,ta) :: _,_) ->
-				let monos = List.map (fun _ -> mk_mono()) cf.cf_params in
-				let map t = apply_params ab.a_params tl (apply_params cf.cf_params monos t) in
-				let athis = map ab.a_this in
-				(* we cannot allow implicit casts when the this type is not completely known yet *)
-				(* if has_mono athis then raise (Unify_error []); *)
-				with_variance (type_eq EqStrict) athis (map ta);
-				(* immediate constraints checking is ok here because we know there are no monomorphs *)
-				List.iter2 (fun m (name,t) -> match follow t with
-					| TInst ({ cl_kind = KTypeParameter constr },_) when constr <> [] ->
-						List.iter (fun tc -> match follow m with TMono _ -> raise (Unify_error []) | _ -> unify m (map tc) ) constr
-					| _ -> ()
-				) monos cf.cf_params;
-				unify_func (map t) b;
-			| _ -> assert false)
-
-and unify_with_variance f t1 t2 =
-	let allows_variance_to t tf = type_iseq tf t in
-	match follow t1,follow t2 with
-	| TInst(c1,tl1),TInst(c2,tl2) when c1 == c2 ->
-		List.iter2 f tl1 tl2
-	| TEnum(en1,tl1),TEnum(en2,tl2) when en1 == en2 ->
-		List.iter2 f tl1 tl2
-	| TAbstract(a1,tl1),TAbstract(a2,tl2) when a1 == a2 && Meta.has Meta.CoreType a1.a_meta ->
-		List.iter2 f tl1 tl2
-	| TAbstract(a1,pl1),TAbstract(a2,pl2) ->
-		if (Meta.has Meta.CoreType a1.a_meta) && (Meta.has Meta.CoreType a2.a_meta) then begin
-			let ta1 = apply_params a1.a_params pl1 a1.a_this in
-			let ta2 = apply_params a2.a_params pl2 a2.a_this in
-			type_eq EqStrict ta1 ta2;
-		end;
-		if not (List.exists (allows_variance_to t2) a1.a_to) && not (List.exists (allows_variance_to t1) a2.a_from) then
-			error [cannot_unify t1 t2]
-	| TAbstract(a,pl),t ->
-		type_eq EqBothDynamic (apply_params a.a_params pl a.a_this) t;
-		if not (List.exists (fun t2 -> allows_variance_to t (apply_params a.a_params pl t2)) a.a_to) then error [cannot_unify t1 t2]
-	| t,TAbstract(a,pl) ->
-		type_eq EqBothDynamic t (apply_params a.a_params pl a.a_this);
-		if not (List.exists (fun t2 -> allows_variance_to t (apply_params a.a_params pl t2)) a.a_from) then error [cannot_unify t1 t2]
-	| (TAnon a1 as t1), (TAnon a2 as t2) ->
-		rec_stack unify_stack (t1,t2)
-			(fun (a,b) -> fast_eq a t1 && fast_eq b t2)
-			(fun() -> unify_anons t1 t2 a1 a2)
-			(fun l -> error l)
-	| _ ->
-		error [cannot_unify t1 t2]
-
-and unify_type_params a b tl1 tl2 =
-	let i = ref 0 in
-	List.iter2 (fun t1 t2 ->
-		incr i;
-		try
-			with_variance (type_eq EqRightDynamic) t1 t2
-		with Unify_error l ->
-			let err = cannot_unify a b in
-			error (err :: (Invariant_parameter !i) :: l)
-	) tl1 tl2
-
-and with_variance f t1 t2 =
-	try
-		f t1 t2
-	with Unify_error l -> try
-		unify_with_variance (with_variance f) t1 t2
-	with Unify_error _ ->
-		raise (Unify_error l)
-
-and unify_with_access f1 t1 f2 =
-	match f2.cf_kind with
-	(* write only *)
-	| Var { v_read = AccNo } | Var { v_read = AccNever } -> unify f2.cf_type t1
-	(* read only *)
-	| Method MethNormal | Method MethInline | Var { v_write = AccNo } | Var { v_write = AccNever } ->
-		if (has_class_field_flag f1 CfFinal) <> (has_class_field_flag f2 CfFinal) then raise (Unify_error [FinalInvariance]);
-		unify t1 f2.cf_type
-	(* read/write *)
-	| _ -> with_variance (type_eq EqBothDynamic) t1 f2.cf_type
-
-let does_unify a b =
-	try
-		unify a b;
-		true
-	with Unify_error _ ->
-		false
-
-(* ======= Mapping and iterating ======= *)
-
-let iter f e =
-	match e.eexpr with
-	| TConst _
-	| TLocal _
-	| TBreak
-	| TContinue
-	| TTypeExpr _
-	| TIdent _ ->
-		()
-	| TArray (e1,e2)
-	| TBinop (_,e1,e2)
-	| TFor (_,e1,e2)
-	| TWhile (e1,e2,_) ->
-		f e1;
-		f e2;
-	| TThrow e
-	| TField (e,_)
-	| TEnumParameter (e,_,_)
-	| TEnumIndex e
-	| TParenthesis e
-	| TCast (e,_)
-	| TUnop (_,_,e)
-	| TMeta(_,e) ->
-		f e
-	| TArrayDecl el
-	| TNew (_,_,el)
-	| TBlock el ->
-		List.iter f el
-	| TObjectDecl fl ->
-		List.iter (fun (_,e) -> f e) fl
-	| TCall (e,el) ->
-		f e;
-		List.iter f el
-	| TVar (v,eo) ->
-		(match eo with None -> () | Some e -> f e)
-	| TFunction fu ->
-		f fu.tf_expr
-	| TIf (e,e1,e2) ->
-		f e;
-		f e1;
-		(match e2 with None -> () | Some e -> f e)
-	| TSwitch (e,cases,def) ->
-		f e;
-		List.iter (fun (el,e2) -> List.iter f el; f e2) cases;
-		(match def with None -> () | Some e -> f e)
-	| TTry (e,catches) ->
-		f e;
-		List.iter (fun (_,e) -> f e) catches
-	| TReturn eo ->
-		(match eo with None -> () | Some e -> f e)
-
-(**
-	Returns `true` if `predicate` is evaluated to `true` for at least one of sub-expressions.
-	Returns `false` otherwise.
-	Does not evaluate `predicate` for the `e` expression.
-*)
-let check_expr predicate e =
-	match e.eexpr with
-		| TConst _ | TLocal _ | TBreak | TContinue | TTypeExpr _ | TIdent _ ->
-			false
-		| TArray (e1,e2) | TBinop (_,e1,e2) | TFor (_,e1,e2) | TWhile (e1,e2,_) ->
-			predicate e1 || predicate e2;
-		| TThrow e | TField (e,_) | TEnumParameter (e,_,_) | TEnumIndex e | TParenthesis e
-		| TCast (e,_) | TUnop (_,_,e) | TMeta(_,e) ->
-			predicate e
-		| TArrayDecl el | TNew (_,_,el) | TBlock el ->
-			List.exists predicate el
-		| TObjectDecl fl ->
-			List.exists (fun (_,e) -> predicate e) fl
-		| TCall (e,el) ->
-			predicate e ||  List.exists predicate el
-		| TVar (_,eo) | TReturn eo ->
-			(match eo with None -> false | Some e -> predicate e)
-		| TFunction fu ->
-			predicate fu.tf_expr
-		| TIf (e,e1,e2) ->
-			predicate e || predicate e1 || (match e2 with None -> false | Some e -> predicate e)
-		| TSwitch (e,cases,def) ->
-			predicate e
-			|| List.exists (fun (el,e2) -> List.exists predicate el || predicate e2) cases
-			|| (match def with None -> false | Some e -> predicate e)
-		| TTry (e,catches) ->
-			predicate e || List.exists (fun (_,e) -> predicate e) catches
-
-let map_expr f e =
-	match e.eexpr with
-	| TConst _
-	| TLocal _
-	| TBreak
-	| TContinue
-	| TTypeExpr _
-	| TIdent _ ->
-		e
-	| TArray (e1,e2) ->
-		let e1 = f e1 in
-		{ e with eexpr = TArray (e1,f e2) }
-	| TBinop (op,e1,e2) ->
-		let e1 = f e1 in
-		{ e with eexpr = TBinop (op,e1,f e2) }
-	| TFor (v,e1,e2) ->
-		let e1 = f e1 in
-		{ e with eexpr = TFor (v,e1,f e2) }
-	| TWhile (e1,e2,flag) ->
-		let e1 = f e1 in
-		{ e with eexpr = TWhile (e1,f e2,flag) }
-	| TThrow e1 ->
-		{ e with eexpr = TThrow (f e1) }
-	| TEnumParameter (e1,ef,i) ->
-		{ e with eexpr = TEnumParameter(f e1,ef,i) }
-	| TEnumIndex e1 ->
-		{ e with eexpr = TEnumIndex (f e1) }
-	| TField (e1,v) ->
-		{ e with eexpr = TField (f e1,v) }
-	| TParenthesis e1 ->
-		{ e with eexpr = TParenthesis (f e1) }
-	| TUnop (op,pre,e1) ->
-		{ e with eexpr = TUnop (op,pre,f e1) }
-	| TArrayDecl el ->
-		{ e with eexpr = TArrayDecl (List.map f el) }
-	| TNew (t,pl,el) ->
-		{ e with eexpr = TNew (t,pl,List.map f el) }
-	| TBlock el ->
-		{ e with eexpr = TBlock (List.map f el) }
-	| TObjectDecl el ->
-		{ e with eexpr = TObjectDecl (List.map (fun (v,e) -> v, f e) el) }
-	| TCall (e1,el) ->
-		let e1 = f e1 in
-		{ e with eexpr = TCall (e1, List.map f el) }
-	| TVar (v,eo) ->
-		{ e with eexpr = TVar (v, match eo with None -> None | Some e -> Some (f e)) }
-	| TFunction fu ->
-		{ e with eexpr = TFunction { fu with tf_expr = f fu.tf_expr } }
-	| TIf (ec,e1,e2) ->
-		let ec = f ec in
-		let e1 = f e1 in
-		{ e with eexpr = TIf (ec,e1,match e2 with None -> None | Some e -> Some (f e)) }
-	| TSwitch (e1,cases,def) ->
-		let e1 = f e1 in
-		let cases = List.map (fun (el,e2) -> List.map f el, f e2) cases in
-		{ e with eexpr = TSwitch (e1, cases, match def with None -> None | Some e -> Some (f e)) }
-	| TTry (e1,catches) ->
-		let e1 = f e1 in
-		{ e with eexpr = TTry (e1, List.map (fun (v,e) -> v, f e) catches) }
-	| TReturn eo ->
-		{ e with eexpr = TReturn (match eo with None -> None | Some e -> Some (f e)) }
-	| TCast (e1,t) ->
-		{ e with eexpr = TCast (f e1,t) }
-	| TMeta (m,e1) ->
-		 {e with eexpr = TMeta(m,f e1)}
-
-let map_expr_type f ft fv e =
-	match e.eexpr with
-	| TConst _
-	| TBreak
-	| TContinue
-	| TTypeExpr _
-	| TIdent _ ->
-		{ e with etype = ft e.etype }
-	| TLocal v ->
-		{ e with eexpr = TLocal (fv v); etype = ft e.etype }
-	| TArray (e1,e2) ->
-		let e1 = f e1 in
-		{ e with eexpr = TArray (e1,f e2); etype = ft e.etype }
-	| TBinop (op,e1,e2) ->
-		let e1 = f e1 in
-		{ e with eexpr = TBinop (op,e1,f e2); etype = ft e.etype }
-	| TFor (v,e1,e2) ->
-		let v = fv v in
-		let e1 = f e1 in
-		{ e with eexpr = TFor (v,e1,f e2); etype = ft e.etype }
-	| TWhile (e1,e2,flag) ->
-		let e1 = f e1 in
-		{ e with eexpr = TWhile (e1,f e2,flag); etype = ft e.etype }
-	| TThrow e1 ->
-		{ e with eexpr = TThrow (f e1); etype = ft e.etype }
-	| TEnumParameter (e1,ef,i) ->
-		{ e with eexpr = TEnumParameter (f e1,ef,i); etype = ft e.etype }
-	| TEnumIndex e1 ->
-		{ e with eexpr = TEnumIndex (f e1); etype = ft e.etype }
-	| TField (e1,v) ->
-		let e1 = f e1 in
-		let v = try
-			let n = match v with
-				| FClosure _ -> raise Not_found
-				| FAnon f | FInstance (_,_,f) | FStatic (_,f) -> f.cf_name
-				| FEnum (_,f) -> f.ef_name
-				| FDynamic n -> n
-			in
-			quick_field e1.etype n
-		with Not_found ->
-			v
-		in
-		{ e with eexpr = TField (e1,v); etype = ft e.etype }
-	| TParenthesis e1 ->
-		{ e with eexpr = TParenthesis (f e1); etype = ft e.etype }
-	| TUnop (op,pre,e1) ->
-		{ e with eexpr = TUnop (op,pre,f e1); etype = ft e.etype }
-	| TArrayDecl el ->
-		{ e with eexpr = TArrayDecl (List.map f el); etype = ft e.etype }
-	| TNew (c,pl,el) ->
-		let et = ft e.etype in
-		(* make sure that we use the class corresponding to the replaced type *)
-		let t = match c.cl_kind with
-			| KTypeParameter _ | KGeneric ->
-				et
-			| _ ->
-				ft (TInst(c,pl))
-		in
-		let c, pl = (match follow t with TInst (c,pl) -> (c,pl) | TAbstract({a_impl = Some c},pl) -> c,pl | t -> error [has_no_field t "new"]) in
-		{ e with eexpr = TNew (c,pl,List.map f el); etype = et }
-	| TBlock el ->
-		{ e with eexpr = TBlock (List.map f el); etype = ft e.etype }
-	| TObjectDecl el ->
-		{ e with eexpr = TObjectDecl (List.map (fun (v,e) -> v, f e) el); etype = ft e.etype }
-	| TCall (e1,el) ->
-		let e1 = f e1 in
-		{ e with eexpr = TCall (e1, List.map f el); etype = ft e.etype }
-	| TVar (v,eo) ->
-		{ e with eexpr = TVar (fv v, match eo with None -> None | Some e -> Some (f e)); etype = ft e.etype }
-	| TFunction fu ->
-		let fu = {
-			tf_expr = f fu.tf_expr;
-			tf_args = List.map (fun (v,o) -> fv v, o) fu.tf_args;
-			tf_type = ft fu.tf_type;
-		} in
-		{ e with eexpr = TFunction fu; etype = ft e.etype }
-	| TIf (ec,e1,e2) ->
-		let ec = f ec in
-		let e1 = f e1 in
-		{ e with eexpr = TIf (ec,e1,match e2 with None -> None | Some e -> Some (f e)); etype = ft e.etype }
-	| TSwitch (e1,cases,def) ->
-		let e1 = f e1 in
-		let cases = List.map (fun (el,e2) -> List.map f el, f e2) cases in
-		{ e with eexpr = TSwitch (e1, cases, match def with None -> None | Some e -> Some (f e)); etype = ft e.etype }
-	| TTry (e1,catches) ->
-		let e1 = f e1 in
-		{ e with eexpr = TTry (e1, List.map (fun (v,e) -> fv v, f e) catches); etype = ft e.etype }
-	| TReturn eo ->
-		{ e with eexpr = TReturn (match eo with None -> None | Some e -> Some (f e)); etype = ft e.etype }
-	| TCast (e1,t) ->
-		{ e with eexpr = TCast (f e1,t); etype = ft e.etype }
-	| TMeta (m,e1) ->
-		{e with eexpr = TMeta(m, f e1); etype = ft e.etype }
-
-let resolve_typedef t =
-	match t with
-	| TClassDecl _ | TEnumDecl _ | TAbstractDecl _ -> t
-	| TTypeDecl td ->
-		match follow td.t_type with
-		| TEnum (e,_) -> TEnumDecl e
-		| TInst (c,_) -> TClassDecl c
-		| TAbstract (a,_) -> TAbstractDecl a
-		| _ -> t
-
-module TExprToExpr = struct
-	let tpath p mp pl =
-		if snd mp = snd p then
-			CTPath {
-				tpackage = fst p;
-				tname = snd p;
-				tparams = pl;
-				tsub = None;
-			}
-		else CTPath {
-				tpackage = fst mp;
-				tname = snd mp;
-				tparams = pl;
-				tsub = Some (snd p);
-			}
-
-	let rec convert_type = function
-		| TMono r ->
-			(match r.tm_type with
-			| None -> raise Exit
-			| Some t -> convert_type t)
-		| TInst ({cl_private = true; cl_path=_,name},tl)
-		| TEnum ({e_private = true; e_path=_,name},tl)
-		| TType ({t_private = true; t_path=_,name},tl)
-		| TAbstract ({a_private = true; a_path=_,name},tl) ->
-			CTPath {
-				tpackage = [];
-				tname = name;
-				tparams = List.map tparam tl;
-				tsub = None;
-			}
-		| TEnum (e,pl) ->
-			tpath e.e_path e.e_module.m_path (List.map tparam pl)
-		| TInst({cl_kind = KExpr e} as c,pl) ->
-			tpath ([],snd c.cl_path) ([],snd c.cl_path) (List.map tparam pl)
-		| TInst({cl_kind = KTypeParameter _} as c,pl) ->
-			tpath ([],snd c.cl_path) ([],snd c.cl_path) (List.map tparam pl)
-		| TInst (c,pl) ->
-			tpath c.cl_path c.cl_module.m_path (List.map tparam pl)
-		| TType (t,pl) as tf ->
-			(* recurse on type-type *)
-			if (snd t.t_path).[0] = '#' then convert_type (follow tf) else tpath t.t_path t.t_module.m_path (List.map tparam pl)
-		| TAbstract (a,pl) ->
-			tpath a.a_path a.a_module.m_path (List.map tparam pl)
-		| TFun (args,ret) ->
-			CTFunction (List.map (fun (_,_,t) -> convert_type' t) args, (convert_type' ret))
-		| TAnon a ->
-			begin match !(a.a_status) with
-			| Statics c -> tpath ([],"Class") ([],"Class") [TPType (tpath c.cl_path c.cl_path [],null_pos)]
-			| EnumStatics e -> tpath ([],"Enum") ([],"Enum") [TPType (tpath e.e_path e.e_path [],null_pos)]
-			| _ ->
-				CTAnonymous (PMap.foldi (fun _ f acc ->
-					{
-						cff_name = f.cf_name,null_pos;
-						cff_kind = FVar (mk_type_hint f.cf_type null_pos,None);
-						cff_pos = f.cf_pos;
-						cff_doc = f.cf_doc;
-						cff_meta = f.cf_meta;
-						cff_access = [];
-					} :: acc
-				) a.a_fields [])
-			end
-		| (TDynamic t2) as t ->
-			tpath ([],"Dynamic") ([],"Dynamic") (if t == t_dynamic then [] else [tparam t2])
-		| TLazy f ->
-			convert_type (lazy_type f)
-
-	and convert_type' t =
-		convert_type t,null_pos
-
-	and tparam = function
-		| TInst ({cl_kind = KExpr e}, _) -> TPExpr e
-		| t -> TPType (convert_type' t)
-
-	and mk_type_hint t p =
-		match follow t with
-		| TMono _ -> None
-		| _ -> (try Some (convert_type t,p) with Exit -> None)
-
-	let rec convert_expr e =
-		let full_type_path t =
-			let mp,p = match t with
-			| TClassDecl c -> c.cl_module.m_path,c.cl_path
-			| TEnumDecl en -> en.e_module.m_path,en.e_path
-			| TAbstractDecl a -> a.a_module.m_path,a.a_path
-			| TTypeDecl t -> t.t_module.m_path,t.t_path
-			in
-			if snd mp = snd p then p else (fst mp) @ [snd mp],snd p
-		in
-		let mk_path = expr_of_type_path in
-		let mk_ident = function
-			| "`trace" -> Ident "trace"
-			| n -> Ident n
-		in
-		let eopt = function None -> None | Some e -> Some (convert_expr e) in
-		((match e.eexpr with
-		| TConst c ->
-			EConst (tconst_to_const c)
-		| TLocal v -> EConst (mk_ident v.v_name)
-		| TArray (e1,e2) -> EArray (convert_expr e1,convert_expr e2)
-		| TBinop (op,e1,e2) -> EBinop (op, convert_expr e1, convert_expr e2)
-		| TField (e,f) -> EField (convert_expr e, field_name f)
-		| TTypeExpr t -> fst (mk_path (full_type_path t) e.epos)
-		| TParenthesis e -> EParenthesis (convert_expr e)
-		| TObjectDecl fl -> EObjectDecl (List.map (fun (k,e) -> k, convert_expr e) fl)
-		| TArrayDecl el -> EArrayDecl (List.map convert_expr el)
-		| TCall (e,el) -> ECall (convert_expr e,List.map convert_expr el)
-		| TNew (c,pl,el) -> ENew ((match (try convert_type (TInst (c,pl)) with Exit -> convert_type (TInst (c,[]))) with CTPath p -> p,null_pos | _ -> assert false),List.map convert_expr el)
-		| TUnop (op,p,e) -> EUnop (op,p,convert_expr e)
-		| TFunction f ->
-			let arg (v,c) = (v.v_name,v.v_pos), false, v.v_meta, mk_type_hint v.v_type null_pos, (match c with None -> None | Some c -> Some (convert_expr c)) in
-			EFunction (FKAnonymous,{ f_params = []; f_args = List.map arg f.tf_args; f_type = mk_type_hint f.tf_type null_pos; f_expr = Some (convert_expr f.tf_expr) })
-		| TVar (v,eo) ->
-			EVars ([(v.v_name,v.v_pos), v.v_final, mk_type_hint v.v_type v.v_pos, eopt eo])
-		| TBlock el -> EBlock (List.map convert_expr el)
-		| TFor (v,it,e) ->
-			let ein = (EBinop (OpIn,(EConst (Ident v.v_name),it.epos),convert_expr it),it.epos) in
-			EFor (ein,convert_expr e)
-		| TIf (e,e1,e2) -> EIf (convert_expr e,convert_expr e1,eopt e2)
-		| TWhile (e1,e2,flag) -> EWhile (convert_expr e1, convert_expr e2, flag)
-		| TSwitch (e,cases,def) ->
-			let cases = List.map (fun (vl,e) ->
-				List.map convert_expr vl,None,(match e.eexpr with TBlock [] -> None | _ -> Some (convert_expr e)),e.epos
-			) cases in
-			let def = match eopt def with None -> None | Some (EBlock [],_) -> Some (None,null_pos) | Some e -> Some (Some e,pos e) in
-			ESwitch (convert_expr e,cases,def)
-		| TEnumIndex _
-		| TEnumParameter _ ->
-			(* these are considered complex, so the AST is handled in TMeta(Meta.Ast) *)
-			assert false
-		| TTry (e,catches) ->
-			let e1 = convert_expr e in
-			let catches = List.map (fun (v,e) ->
-				let ct = try convert_type v.v_type,null_pos with Exit -> assert false in
-				let e = convert_expr e in
-				(v.v_name,v.v_pos),ct,e,(pos e)
-			) catches in
-			ETry (e1,catches)
-		| TReturn e -> EReturn (eopt e)
-		| TBreak -> EBreak
-		| TContinue -> EContinue
-		| TThrow e -> EThrow (convert_expr e)
-		| TCast (e,t) ->
-			let t = (match t with
-				| None -> None
-				| Some t ->
-					let t = (match t with TClassDecl c -> TInst (c,[]) | TEnumDecl e -> TEnum (e,[]) | TTypeDecl t -> TType (t,[]) | TAbstractDecl a -> TAbstract (a,[])) in
-					Some (try convert_type t,null_pos with Exit -> assert false)
-			) in
-			ECast (convert_expr e,t)
-		| TMeta ((Meta.Ast,[e1,_],_),_) -> e1
-		| TMeta (m,e) -> EMeta(m,convert_expr e)
-		| TIdent s -> EConst (Ident s))
-		,e.epos)
-
-end
-
-module ExtType = struct
-	let is_mono = function
-		| TMono { tm_type = None } -> true
-		| _ -> false
-
-	let is_void = function
-		| TAbstract({a_path=[],"Void"},_) -> true
-		| _ -> false
-
-	let is_int t = match t with
-		| TAbstract({a_path=[],"Int"},_) -> true
-		| _ -> false
-
-	let is_float t = match t with
-		| TAbstract({a_path=[],"Float"},_) -> true
-		| _ -> false
-
-	let is_numeric t = match t with
-		| TAbstract({a_path=[],"Float"},_) -> true
-		| TAbstract({a_path=[],"Int"},_) -> true
-		| _ -> false
-
-	let is_string t = match t with
-		| TInst({cl_path=[],"String"},_) -> true
-		| _ -> false
-
-	let is_bool t = match t with
-		| TAbstract({a_path=[],"Bool"},_) -> true
-		| _ -> false
-
-	type semantics =
-		| VariableSemantics
-		| ReferenceSemantics
-		| ValueSemantics
-
-	let semantics_name = function
-		| VariableSemantics -> "variable"
-		| ReferenceSemantics -> "reference"
-		| ValueSemantics -> "value"
-
-	let has_semantics t sem =
-		let name = semantics_name sem in
-		let check meta =
-			has_meta_option meta Meta.Semantics name
-		in
-		let rec loop t = match t with
-			| TInst(c,_) -> check c.cl_meta
-			| TEnum(en,_) -> check en.e_meta
-			| TType(t,tl) -> check t.t_meta || (loop (apply_params t.t_params tl t.t_type))
-			| TAbstract(a,_) -> check a.a_meta
-			| TLazy f -> loop (lazy_type f)
-			| TMono r ->
-				(match r.tm_type with
-				| Some t -> loop t
-				| _ -> false)
-			| _ ->
-				false
-		in
-		loop t
-
-	let has_variable_semantics t = has_semantics t VariableSemantics
-	let has_reference_semantics t = has_semantics t ReferenceSemantics
-	let has_value_semantics t = has_semantics t ValueSemantics
-end
-
-let class_module_type c = {
-	t_path = [],"Class<" ^ (s_type_path c.cl_path) ^ ">" ;
-	t_module = c.cl_module;
-	t_doc = None;
-	t_pos = c.cl_pos;
-	t_name_pos = null_pos;
-	t_type = TAnon {
-		a_fields = c.cl_statics;
-		a_status = ref (Statics c);
-	};
-	t_private = true;
-	t_params = [];
-	t_using = [];
-	t_meta = no_meta;
-}
-
-let enum_module_type m path p  = {
-	t_path = [], "Enum<" ^ (s_type_path path) ^ ">";
-	t_module = m;
-	t_doc = None;
-	t_pos = p;
-	t_name_pos = null_pos;
-	t_type = mk_mono();
-	t_private = true;
-	t_params = [];
-	t_using = [];
-	t_meta = [];
-}
-
-let abstract_module_type a tl = {
-	t_path = [],Printf.sprintf "Abstract<%s%s>" (s_type_path a.a_path) (s_type_params (ref []) tl);
-	t_module = a.a_module;
-	t_doc = None;
-	t_pos = a.a_pos;
-	t_name_pos = null_pos;
-	t_type = TAnon {
-		a_fields = PMap.empty;
-		a_status = ref (AbstractStatics a);
-	};
-	t_private = true;
-	t_params = [];
-	t_using = [];
-	t_meta = no_meta;
-}
-
-module TClass = struct
-	let get_member_fields' self_too c0 tl =
-		let rec loop acc c tl =
-			let apply = apply_params c.cl_params tl in
-			let maybe_add acc cf =
-				if not (PMap.mem cf.cf_name acc) then begin
-					let cf = if tl = [] then cf else {cf with cf_type = apply cf.cf_type} in
-					PMap.add cf.cf_name (c,cf) acc
-				end else acc
-			in
-			let acc = if self_too || c != c0 then List.fold_left maybe_add acc c.cl_ordered_fields else acc in
-			if c.cl_interface then
-				List.fold_left (fun acc (i,tl) -> loop acc i (List.map apply tl)) acc c.cl_implements
-			else
-				match c.cl_super with
-				| Some(c,tl) -> loop acc c (List.map apply tl)
-				| None -> acc
-		in
-		loop PMap.empty c0 tl
-
-	let get_all_super_fields c =
-		get_member_fields' false c (List.map snd c.cl_params)
-
-	let get_all_fields c tl =
-		get_member_fields' true c tl
-
-	let get_overridden_fields c cf =
-		let rec loop acc c = match c.cl_super with
-			| None ->
-				acc
-			| Some(c,_) ->
-				begin try
-					let cf' = PMap.find cf.cf_name c.cl_fields in
-					loop (cf' :: acc) c
-				with Not_found ->
-					loop acc c
-				end
-		in
-		loop [] c
-end
-
-let s_class_path c =
-	let path = match c.cl_kind with
-		| KAbstractImpl a -> a.a_path
-		| _ -> c.cl_path
-	in
-	s_type_path path
 ;;
-unify_ref := unify;;
 monomorph_bind_ref := Monomorph.bind;;
 monomorph_create_ref := Monomorph.create;;

+ 11 - 3
src/dune

@@ -6,18 +6,26 @@
 	)
 )
 
-(executable
+(library
 	(name haxe)
-	(public_name haxe)
-	(package haxe)
 	(libraries
 		extc extproc extlib_leftovers ilib javalib neko objsize pcre swflib ttflib ziplib
 		json
 		unix str threads dynlink
 		xml-light extlib ptmap sha
 	)
+	(modules (:standard \ haxe))
 	(preprocess (per_module
 		((pps sedlex.ppx) json lexer)
 	))
+	(wrapped false)
+)
+
+(executable
+	(name haxe)
+	(public_name haxe)
+	(package haxe)
+	(libraries haxe)
+	(modules haxe)
 	(link_flags (:include ../lib.sexp))
 )

+ 2 - 2
src/filters/filters.ml

@@ -809,7 +809,7 @@ module ForRemap = struct
 end
 
 let run com tctx main =
-	let detail_times = Common.raw_defined com "filter-times" in
+	let detail_times = Common.defined com DefineList.FilterTimes in
 	let new_types = List.filter (fun t ->
 		let cached = is_cached t in
 		begin match t with
@@ -848,8 +848,8 @@ let run com tctx main =
 		fix_return_dynamic_from_void_function tctx true;
 		check_local_vars_init;
 		check_abstract_as_value;
-		if Common.defined com Define.OldConstructorInline then Optimizer.inline_constructors tctx else InlineConstructors.inline_constructors tctx;
 		Optimizer.reduce_expression tctx;
+		if Common.defined com Define.OldConstructorInline then Optimizer.inline_constructors tctx else InlineConstructors.inline_constructors tctx;
 		CapturedVars.captured_vars com;
 	] in
 	let filters =

+ 3 - 6
src/generators/genjs.ml

@@ -962,11 +962,8 @@ and gen_syntax ctx meth args pos =
 		in
 		begin
 			match args with
-			| [] ->
-				if code = "this" then
-					spr ctx (this ctx)
-				else
-					spr ctx (String.concat "\n" (ExtString.String.nsplit code "\r\n"))
+			| [] when code = "this" ->
+				spr ctx (this ctx)
 			| _ ->
 				Codegen.interpolate_code ctx.com code args (spr ctx) (gen_value ctx) code_pos
 		end
@@ -1776,7 +1773,7 @@ let generate com =
 	end;
 	if has_feature ctx "$global.$haxeUID" then begin
 		add_feature ctx "js.Lib.global";
-		print ctx "if(typeof $global.$haxeUID == \"undefined\") $global.$haxeUID = 0;\n";
+		print ctx "$global.$haxeUID |= 0;\n";
 	end;
 	List.iter (gen_block_element ~after:true ctx) (List.rev ctx.inits);
 	List.iter (generate_static ctx) (List.rev ctx.statics);

+ 6 - 4
src/generators/genjvm.ml

@@ -43,11 +43,13 @@ let rec pow a b = match b with
 
 let java_hash s =
 	let h = ref Int32.zero in
-	let l = String.length s in
+	let l = UTF8.length s in
 	let i31 = Int32.of_int 31 in
-	String.iteri (fun i char ->
-		let char = Int32.of_int (int_of_char char) in
-		h := Int32.add !h (Int32.mul char (pow i31 (l - (i + 1))))
+	let i = ref 0 in
+	UTF8.iter (fun char ->
+		let char = Int32.of_int (UCharExt.uint_code char) in
+		h := Int32.add !h (Int32.mul char (pow i31 (l - (!i + 1))));
+		incr i;
 	) s;
 	!h
 

+ 6 - 3
src/generators/genlua.ml

@@ -917,8 +917,8 @@ and gen_expr ?(local=true) ctx e = begin
     | TWhile (cond,e,Ast.NormalWhile) ->
         gen_loop ctx "while" cond e
     | TWhile (cond,e,Ast.DoWhile) ->
-        println ctx "while true do ";
         gen_block_element ctx e;
+        newline ctx;
         gen_loop ctx "while" cond e
     | TObjectDecl [] ->
         spr ctx "_hx_e()";
@@ -2060,11 +2060,14 @@ let generate com =
     List.iter (transform_multireturn ctx) com.types;
     List.iter (generate_type ctx) com.types;
 
-    if has_feature ctx "use._bitop" || has_feature ctx "lua.Boot.clamp" then begin
-        print_file (Common.find_file com "lua/_lua/_hx_bit_clamp.lua");
+    (* If bit ops are manually imported include the haxe wrapper for them *)
+    if has_feature ctx "use._bitop" then begin
         print_file (Common.find_file com "lua/_lua/_hx_bit.lua");
     end;
 
+    (* integer clamping is always required, and will use bit ops if available *)
+    print_file (Common.find_file com "lua/_lua/_hx_bit_clamp.lua");
+
     (* Array is required, always patch it *)
     println ctx "_hx_array_mt.__index = Array.prototype";
     newline ctx;

+ 24 - 18
src/generators/genphp7.ml

@@ -911,20 +911,18 @@ class class_wrapper (cls) =
 				match cls.cl_init with
 					| Some _ -> true
 					| None ->
-						let needs = ref false in
-						PMap.iter
-							(fun _ field ->
+						List.exists
+							(fun field ->
 								(* Skip `inline var` fields *)
-								if not (is_inline_var field) then begin
-									if not !needs then needs := is_var_with_nonconstant_expr field;
-									(* Check static vars with non-constant expressions *)
-									if not !needs then needs := is_var_with_nonconstant_expr field;
-									(* Check static dynamic functions *)
-									if not !needs then needs := is_dynamic_method field
-								end
+								not (is_inline_var field)
+								&& match field.cf_kind, field.cf_expr with
+									| Var _, Some { eexpr = TConst (TInt value) } -> value = Int32.min_int
+									| Var _, Some { eexpr = TConst _ } -> false
+									| Var _, Some _ -> true
+									| Method MethDynamic, _ -> true
+									| _ -> false
 							)
-							cls.cl_statics;
-						!needs
+							cls.cl_ordered_statics
 		(**
 			Returns expression of a user-defined static __init__ method
 			@see http://old.haxe.org/doc/advanced/magic#initialization-magic
@@ -3531,13 +3529,13 @@ class class_builder ctx (cls:tclass) =
 				);
 				writer#write ";\n"
 			in
-			PMap.iter
-				(fun _ field ->
+			List.iter
+				(fun field ->
 					match field.cf_kind with
 						| Method MethDynamic -> write_dynamic_method_initialization field
 						| _ -> ()
 				)
-				cls.cl_statics;
+				cls.cl_ordered_statics;
 			(* `static var` initialization *)
 			let write_var_initialization field =
 				let write_assign expr =
@@ -3548,8 +3546,8 @@ class class_builder ctx (cls:tclass) =
 					Do not generate fields for RTTI meta, because this generator uses another way to store it.
 					Also skip initialization for `inline var` fields as those are generated as PHP class constants.
 				*)
-				let is_auto_meta_var = field.cf_name = "__meta__" && (has_rtti_meta ctx.pgc_common wrapper#get_module_type) in
-				if (is_var_with_nonconstant_expr field) && (not is_auto_meta_var) && (not (is_inline_var field)) then begin
+				let is_auto_meta_var() = field.cf_name = "__meta__" && (has_rtti_meta ctx.pgc_common wrapper#get_module_type) in
+				if (is_var_with_nonconstant_expr field) && (not (is_auto_meta_var())) && (not (is_inline_var field)) then begin
 					(match field.cf_expr with
 						| None -> ()
 						(* There can be not-inlined blocks when compiling with `-debug` *)
@@ -3569,6 +3567,11 @@ class class_builder ctx (cls:tclass) =
 					);
 					writer#write ";\n"
 				end
+				else match field.cf_expr with
+					| Some ({ eexpr = TConst (TInt value) } as expr) when value = Int32.min_int ->
+						write_assign expr;
+						writer#write ";\n"
+					| _ -> ()
 			in
 			List.iter write_var_initialization cls.cl_ordered_statics
 		(**
@@ -3597,7 +3600,10 @@ class class_builder ctx (cls:tclass) =
 			let visibility = get_visibility field.cf_meta in
 			writer#write (visibility ^ " $" ^ (field_name field));
 			match field.cf_expr with
-				| None -> writer#write ";\n"
+				| None ->
+					writer#write ";\n"
+				| Some { eexpr = TConst (TInt value) } when value = Int32.min_int ->
+					writer#write ";\n"
 				| Some expr ->
 					match expr.eexpr with
 						| TConst _ ->

+ 11 - 9
src/generators/genpy.ml

@@ -1306,8 +1306,8 @@ module Printer = struct
 					Printf.sprintf "%s(%s,%s)" (third ops) (print_expr pctx e1) (print_expr pctx e2)
 				| _,_ -> Printf.sprintf "(%s %s %s)" (print_expr pctx e1) (snd ops) (print_expr pctx e2))
 			| TBinop(OpMod,e1,e2) when (is_type1 "" "Int")(e1.etype) && (is_type1 "" "Int")(e2.etype) ->
-				(match e1.eexpr with
-				| TConst(TInt(x)) when (Int32.to_int x) >= 0 ->
+				(match e1.eexpr, e2.eexpr with
+				| TConst(TInt(x1)), TConst(TInt(x2)) when (Int32.to_int x1) >= 0 && (Int32.to_int x2) >= 0 ->
 					(* constant optimization *)
 					Printf.sprintf "%s %% %s" (print_expr pctx e1) (print_expr pctx e2)
 				| _ ->
@@ -2505,13 +2505,15 @@ module Generator = struct
 		if has_feature ctx "closure_Array" || has_feature ctx "closure_String" then
 			spr ctx "from functools import partial as _hx_partial\n";
 		spr ctx "import sys\n";
-		spr ctx "try:\n";
-		spr ctx "    if sys.stdout.encoding != 'utf-8':\n";
-		spr ctx "        sys.stdout = open(sys.stdout.fileno(), mode='w', encoding='utf8', buffering=1)\n";
-		spr ctx "    if sys.stderr.encoding != 'utf-8':\n";
-		spr ctx "        sys.stderr = open(sys.stderr.fileno(), mode='w', encoding='utf8', buffering=1)\n";
-		spr ctx "except:\n";
-		spr ctx "    pass\n";
+		if defined com Define.StdEncodingUtf8 then begin
+			spr ctx "try:\n";
+			spr ctx "    if sys.stdout.encoding != 'utf-8':\n";
+			spr ctx "        sys.stdout = open(sys.stdout.fileno(), mode='w', encoding='utf8', buffering=1)\n";
+			spr ctx "    if sys.stderr.encoding != 'utf-8':\n";
+			spr ctx "        sys.stderr = open(sys.stderr.fileno(), mode='w', encoding='utf8', buffering=1)\n";
+			spr ctx "except:\n";
+			spr ctx "    pass\n";
+		end;
 		gen_imports ctx;
 		gen_resources ctx;
 		gen_types ctx;

+ 34 - 12
src/macro/eval/evalDebugSocket.ml

@@ -331,28 +331,34 @@ module ValueCompletion = struct
 		loop IntMap.empty proto
 
 	let prototype_static_fields proto =
-		IntMap.fold (fun name _ acc -> IntMap.add name (name,"field",None) acc) proto.pnames IntMap.empty
-
+		IntMap.fold (fun name offset acc ->
+			let v = proto.pfields.(offset) in
+			let kind = match v with
+				| VFunction _ -> "method"
+				| _ -> "field"
+			in
+			IntMap.add name (name,kind,None) acc
+		) proto.pnames IntMap.empty
 
 	let to_json l =
 		JArray (List.map (fun (n,k,column) ->
-			let fields = ["label",JString (rev_hash n);"kind",JString k] in
+			let fields = ["label",JString (rev_hash n);"type",JString k] in
 			let fields = match column with None -> fields | Some column -> ("start",JInt column) :: fields in
 			JObject fields
 		) l)
 
 	let collect_idents ctx env =
 		let acc = Hashtbl.create 0 in
-		let add key =
+		let add key kind =
 			if not (Hashtbl.mem acc key) then
-				Hashtbl.add acc key (key,"value",None)
+				Hashtbl.add acc key (key,kind,None)
 		in
 		(* 0. Extra locals *)
-		IntMap.iter (fun key _ -> add key) env.env_extra_locals;
+		IntMap.iter (fun key _ -> add key "variable") env.env_extra_locals;
 		(* 1. Variables *)
 		let rec loop scopes = match scopes with
 			| scope :: scopes ->
-				Hashtbl.iter (fun key _ -> add (hash key)) scope.local_ids;
+				Hashtbl.iter (fun key _ -> add (hash key) "variable") scope.local_ids;
 				loop scopes
 			| [] ->
 				()
@@ -360,7 +366,7 @@ module ValueCompletion = struct
 		loop env.env_debug.scopes;
 		(* 2. Captures *)
 		Hashtbl.iter (fun slot vi ->
-			add (hash vi.vi_name)
+			add (hash vi.vi_name) "variable"
 		) env.env_info.capture_infos;
 		(* 3. Instance *)
 		if not env.env_info.static then begin
@@ -368,7 +374,7 @@ module ValueCompletion = struct
 			begin match v with
 			| VInstance vi ->
 				let fields = prototype_instance_fields vi.iproto in
-				IntMap.iter (fun key _ -> add key) fields
+				IntMap.iter (fun key (_,kind,_) -> add key kind) fields
 			| _ ->
 				()
 			end
@@ -378,7 +384,7 @@ module ValueCompletion = struct
 			| EKMethod(i1,_) ->
 				let proto = get_static_prototype_raise ctx i1 in
 				let fields = prototype_static_fields proto in
-				IntMap.iter (fun key _ -> add key) fields
+				IntMap.iter (fun key (_,kind,_) -> add key kind) fields
 			| _ ->
 				raise Not_found
 		end;
@@ -386,7 +392,18 @@ module ValueCompletion = struct
 		begin match ctx.toplevel with
 		| VObject o ->
 			let fields = object_fields o in
-			List.iter (fun (n,_) -> add n) fields
+			List.iter (fun (n,v) ->
+				let kind = match v with
+					| VPrototype proto ->
+						begin match proto.pkind with
+						| PClass _ -> "class"
+						| PEnum _ -> "enum"
+						| _ -> "class" (* ? *)
+						end
+					| _ -> "module"
+				in
+				add n kind
+			) fields
 		| _ ->
 			()
 		end;
@@ -485,8 +502,13 @@ module ValueCompletion = struct
 		with _ ->
 			save();
 			raise Exit
-		end;
+		end
 
+	let get_completion ctx text column env =
+		if text = "" then
+			collect_idents ctx env
+		else
+			get_completion ctx text column env
 end
 
 type handler_context = {

+ 5 - 0
src/macro/macroApi.ml

@@ -1951,6 +1951,11 @@ let macro_api ccom get_api =
 			);
 			vnull
 		);
+		"timer", vfun1 (fun id ->
+			let full_id = (Option.default [] (Timer.current_id())) @ [decode_string id] in
+			let stop = Timer.timer full_id in
+			vfun0 (fun() -> stop(); vnull)
+		);
 	]
 
 

+ 2 - 2
src/optimization/analyzerTexprTransformer.ml

@@ -475,7 +475,7 @@ let rec func ctx bb tf t p =
 			let bb_next = if bb_breaks = [] then begin
 				(* The loop appears to be infinite, let's assume that something within it throws.
 				   Otherwise DCE's mark-pass won't see its body and removes everything. *)
-				add_cfg_edge bb_loop_body_next bb_exit CFGMaybeThrow;
+				add_cfg_edge bb_loop_body bb_exit CFGMaybeThrow;
 				g.g_unreachable
 			end else
 				create_node BKNormal bb.bb_type bb.bb_pos
@@ -484,7 +484,7 @@ let rec func ctx bb tf t p =
 			set_syntax_edge bb_loop_pre (SEWhile(bb_loop_head,bb_loop_body,bb_next));
 			close_node g bb_loop_pre;
 			add_texpr bb_loop_pre {e with eexpr = TWhile(e1,make_block_meta bb_loop_body,NormalWhile)};
-			add_cfg_edge bb_loop_body_next bb_loop_head CFGGoto;
+			if bb_loop_body_next != g.g_unreachable then add_cfg_edge bb_loop_body_next bb_loop_head CFGGoto;
 			add_cfg_edge bb_loop_head bb_loop_body CFGGoto;
 			close_node g bb_loop_body_next;
 			close_node g bb_loop_head;

+ 14 - 5
src/optimization/inline.ml

@@ -136,7 +136,12 @@ let api_inline ctx c field params p =
 			mk (TBinop (Ast.OpEq, tof, (mk (TConst (TString t)) tstring p))) tbool p
 		in
 
-		(match t.eexpr with
+		let rec skip_cast = function
+			| { eexpr = TCast (e, None) } -> skip_cast e
+			| e -> e
+		in
+
+		(match (skip_cast t).eexpr with
 		(* generate simple typeof checks for basic types *)
 		| TTypeExpr (TClassDecl ({ cl_path = [],"String" })) -> Some (typeof "string")
 		| TTypeExpr (TAbstractDecl ({ a_path = [],"Bool" })) -> Some (typeof "boolean")
@@ -149,10 +154,14 @@ let api_inline ctx c field params p =
 		| TTypeExpr (TClassDecl ({ cl_path = [],"Array" })) ->
 			(* generate (o instanceof Array) && o.__enum__ == null check *)
 			let iof = Texpr.Builder.fcall (eJsSyntax()) "instanceof" [o;t] tbool p in
-			let enum = mk (TField (o, FDynamic "__enum__")) (mk_mono()) p in
-			let null = mk (TConst TNull) (mk_mono()) p in
-			let not_enum = mk (TBinop (Ast.OpEq, enum, null)) tbool p in
-			Some (mk (TBinop (Ast.OpBoolAnd, iof, not_enum)) tbool p)
+			if not (Common.defined ctx.com Define.JsEnumsAsArrays) then
+				Some iof
+			else begin
+				let enum = mk (TField (o, FDynamic "__enum__")) t_dynamic p in
+				let null = mk (TConst TNull) t_dynamic p in
+				let not_enum = mk (TBinop (Ast.OpEq, enum, null)) tbool p in
+				Some (mk (TBinop (Ast.OpBoolAnd, iof, not_enum)) tbool p)
+			end
 		| TTypeExpr (TClassDecl cls) ->
 			if cls.cl_interface then
 				Some (Texpr.Builder.fcall (eJsBoot()) "__implements" [o;t] tbool p)

+ 1 - 0
src/syntax/grammar.mly

@@ -926,6 +926,7 @@ and parse_constraint_param s =
 		let cto = (match s with parser
 			| [< '(DblDot,_); s >] ->
 				(match s with parser
+				| [< '(POpen,p1); t = parse_complex_type; '(PClose,p2) >] -> Some t
 				| [< t = parse_complex_type >] -> Some t
 				| [< >] -> serror())
 			| [< >] -> None

+ 42 - 1
src/typing/typeloadFields.ml

@@ -159,6 +159,7 @@ let ensure_struct_init_constructor ctx c ast_fields p =
 		let params = List.map snd c.cl_params in
 		let ethis = mk (TConst TThis) (TInst(c,params)) p in
 		let args,el,tl = List.fold_left (fun (args,el,tl) cf -> match cf.cf_kind with
+			| Var { v_write = AccNever } -> args,el,tl
 			| Var _ ->
 				let has_default_expr = field_has_default_expr cf.cf_name in
 				let opt = has_default_expr || (Meta.has Meta.Optional cf.cf_meta) in
@@ -325,6 +326,11 @@ type enum_abstract_mode =
 	| EAInt of int ref
 	| EAOther
 
+type enum_constructor_visibility =
+	| VUnknown
+	| VPublic of placed_access
+	| VPrivate of placed_access
+
 let build_enum_abstract ctx c a fields p =
 	let mode =
 		if does_unify a.a_this ctx.t.tint then EAInt (ref 0)
@@ -334,7 +340,31 @@ let build_enum_abstract ctx c a fields p =
 	List.iter (fun field ->
 		match field.cff_kind with
 		| FVar(ct,eo) when not (List.mem_assoc AStatic field.cff_access) ->
-			field.cff_access <- [AStatic,null_pos; if (List.mem_assoc APrivate field.cff_access) then (APrivate,null_pos) else (APublic,null_pos)];
+			let check_visibility_conflict visibility p1 =
+				match visibility with
+				| VUnknown ->
+					()
+				| VPublic(access,p2) | VPrivate(access,p2) ->
+					display_error ctx (Printf.sprintf "Conflicting access modifier %s" (Ast.s_access access)) p1;
+					display_error ctx "Conflicts with this" p2;
+			in
+			let rec loop visibility acc = match acc with
+				| (AExtern,p) :: acc ->
+					display_error ctx "extern modifier is not allowed on enum abstract fields" p;
+					loop visibility acc
+				| (APrivate,p) as access :: acc ->
+					check_visibility_conflict visibility p;
+					loop (VPrivate access) acc
+				| (APublic,p) as access :: acc ->
+					check_visibility_conflict visibility p;
+					loop (VPublic access) acc
+				| _ :: acc ->
+					loop visibility acc
+				| [] ->
+					visibility
+			in
+			let visibility = loop VUnknown field.cff_access in
+			field.cff_access <- [AStatic,null_pos; match visibility with VPublic acc | VPrivate acc -> acc | VUnknown -> (APublic,null_pos)];
 			field.cff_meta <- (Meta.Enum,[],null_pos) :: (Meta.Impl,[],null_pos) :: field.cff_meta;
 			let ct = match ct with
 				| Some _ -> ct
@@ -872,6 +902,17 @@ let check_abstract (ctx,cctx,fctx) c cf fd t ret p =
 					a.a_from_field <- (TLazy r,cf) :: a.a_from_field;
 				| (Meta.To,_,_) :: _ ->
 					if fctx.is_macro then error (cf.cf_name ^ ": Macro cast functions are not supported") p;
+					(match cf.cf_kind, cf.cf_type with
+					| Var _, _ ->
+						error "@:to meta should be used on methods" p
+					| Method _, TFun(args, _) when not fctx.is_abstract_member && List.length args <> 1 ->
+						if not (Meta.has Meta.MultiType a.a_meta) then (* TODO: get rid of this check once multitype is removed *)
+						error ("static @:to method should have one argument") p
+					| Method _, TFun(args, _) when fctx.is_abstract_member && List.length args <> 1 ->
+						if not (Meta.has Meta.MultiType a.a_meta) then (* TODO: get rid of this check once multitype is removed *)
+						error "@:to method should have no arguments" p
+					| _ -> ()
+					);
 					(* TODO: this doesn't seem quite right... *)
 					if not (Meta.has Meta.Impl cf.cf_meta) then cf.cf_meta <- (Meta.Impl,[],null_pos) :: cf.cf_meta;
 					let resolve_m args =

+ 1 - 0
src/typing/typeloadFunction.ml

@@ -84,6 +84,7 @@ let type_function_arg_value ctx t c do_display =
 			let rec loop e = match e.eexpr with
 				| TConst _ -> Some e
 				| TField({eexpr = TTypeExpr _},FEnum _) -> Some e
+                | TField({eexpr = TTypeExpr _},FStatic({cl_kind = KAbstractImpl a},cf)) when Meta.has Meta.Enum a.a_meta && Meta.has Meta.Enum cf.cf_meta -> Some e
 				| TCast(e,None) -> loop e
 				| _ ->
 					if ctx.com.display.dms_kind = DMNone || ctx.com.display.dms_inline && ctx.com.display.dms_error_policy = EPCollect then

+ 16 - 10
src/typing/typerDisplay.ml

@@ -541,16 +541,22 @@ let handle_display ?resume_typing ctx e_ast dk with_type =
 				| Yes -> true
 				| YesButPrivate ->
 					if (Meta.has Meta.PrivateAccess ctx.meta) then true
-					else begin
-						let path = (mt.pack,mt.name) in
-						let rec loop c =
-							if c.cl_path = path then true
-							else match c.cl_super with
-								| Some(c,_) -> loop c
-								| None -> false
-						in
-						loop ctx.curclass
-					end
+					else
+						begin
+							match ctx.curclass.cl_kind with
+							| KAbstractImpl { a_path = (pack, name) } -> pack = mt.pack && name = mt.name
+							| _ -> false
+						end
+						|| begin
+							let path = (mt.pack,mt.name) in
+							let rec loop c =
+								if c.cl_path = path then true
+								else match c.cl_super with
+									| Some(c,_) -> loop c
+									| None -> false
+							in
+							loop ctx.curclass
+						end
 				| No -> false
 				| Maybe ->
 					begin try

+ 4 - 2
std/cs/Boot.hx

@@ -41,8 +41,10 @@ import Reflect;
 @:dox(hide)
 class Boot {
 	@:keep public static function init():Void {
-		cs.system.Console.InputEncoding = new cs.system.text.UTF8Encoding();
-		cs.system.Console.OutputEncoding = new cs.system.text.UTF8Encoding();
+		#if std_encoding_utf8
+			cs.system.Console.InputEncoding = new cs.system.text.UTF8Encoding();
+			cs.system.Console.OutputEncoding = new cs.system.text.UTF8Encoding();
+		#end
 		cs.Lib.applyCultureChanges();
 	}
 }

+ 1 - 0
std/flash/_std/haxe/Http.hx

@@ -41,6 +41,7 @@ class HttpFlash extends haxe.http.HttpBase {
 	}
 
 	public override function request(?post:Bool) {
+		responseAsString = null;
 		responseBytes = null;
 		var loader = req = new flash.net.URLLoader();
 		loader.dataFormat = BINARY;

+ 22 - 7
std/haxe/CallStack.hx

@@ -43,15 +43,15 @@ class CallStack {
 	static function getStack(e:js.lib.Error):Array<StackItem> {
 		if (e == null)
 			return [];
-		// https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
-		var oldValue = (untyped Error).prepareStackTrace;
-		(untyped Error).prepareStackTrace = function(error, callsites:Array<Dynamic>) {
+		// https://v8.dev/docs/stack-trace-api
+		var oldValue = V8Error.prepareStackTrace;
+		V8Error.prepareStackTrace = function(error, callsites) {
 			var stack = [];
 			for (site in callsites) {
 				if (wrapCallSite != null)
 					site = wrapCallSite(site);
 				var method = null;
-				var fullName:String = site.getFunctionName();
+				var fullName = site.getFunctionName();
 				if (fullName != null) {
 					var idx = fullName.lastIndexOf(".");
 					if (idx >= 0) {
@@ -60,7 +60,7 @@ class CallStack {
 						method = Method(className, methodName);
 					}
 				}
-				var fileName:String = site.getFileName();
+				var fileName = site.getFileName();
 				var fileAddr = fileName == null ? -1 : fileName.indexOf("file:");
 				if (wrapCallSite != null && fileAddr > 0)
 					fileName = fileName.substr(fileAddr + 6);
@@ -69,13 +69,13 @@ class CallStack {
 			return stack;
 		}
 		var a = makeStack(e.stack);
-		(untyped Error).prepareStackTrace = oldValue;
+		V8Error.prepareStackTrace = oldValue;
 		return a;
 	}
 
 	// support for source-map-support module
 	@:noCompletion
-	public static var wrapCallSite:Dynamic->Dynamic;
+	public static var wrapCallSite:V8CallSite->V8CallSite;
 	#end
 
 	#if eval
@@ -413,3 +413,18 @@ class CallStack {
 		#end
 	}
 }
+
+#if js
+// https://v8.dev/docs/stack-trace-api
+@:native("Error")
+private extern class V8Error {
+	static var prepareStackTrace:(error:js.lib.Error, structuredStackTrace:Array<V8CallSite>)->Any;
+}
+
+typedef V8CallSite = {
+	function getFunctionName():String;
+	function getFileName():String;
+	function getLineNumber():Int;
+	function getColumnNumber():Int;
+}
+#end

+ 17 - 7
std/haxe/EntryPoint.hx

@@ -108,15 +108,25 @@ class EntryPoint {
 	@:keep public static function run() @:privateAccess {
 		#if js
 		var nextTick = processEvents();
-
+		inline function setTimeoutNextTick() {
+			if (nextTick >= 0) {
+				(untyped setTimeout)(run, nextTick);
+			}
+		}
 		#if nodejs
-		if (nextTick < 0)
-			return;
-		(untyped setTimeout)(run, nextTick);
+		setTimeoutNextTick();
 		#else
-		var window:Dynamic = js.Browser.window;
-		var rqf:Dynamic = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
-		rqf(run);
+		if(js.Lib.typeof(js.Browser.window) != 'undefined') {
+			var window:Dynamic = js.Browser.window;
+			var rqf:Dynamic = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
+			if(rqf != null) {
+				rqf(run);
+			} else {
+				setTimeoutNextTick();
+			}
+		} else {
+			setTimeoutNextTick();
+		}
 		#end
 		#elseif flash
 		flash.Lib.current.stage.addEventListener(flash.events.Event.ENTER_FRAME, function(_) processEvents());

+ 4 - 4
std/haxe/MainLoop.hx

@@ -18,7 +18,7 @@ class MainEvent {
 	function new(f, p) {
 		this.f = f;
 		this.priority = p;
-		nextRun = -1;
+		nextRun = Math.NEGATIVE_INFINITY;
 	}
 
 	/**
@@ -26,7 +26,7 @@ class MainEvent {
 		If t is null, the event will be run at tick() time.
 	**/
 	public function delay(t:Null<Float>) {
-		nextRun = t == null ? -1 : haxe.Timer.stamp() + t;
+		nextRun = t == null ? Math.NEGATIVE_INFINITY : haxe.Timer.stamp() + t;
 	}
 
 	/**
@@ -44,7 +44,7 @@ class MainEvent {
 		if (f == null)
 			return;
 		f = null;
-		nextRun = -1;
+		nextRun = Math.NEGATIVE_INFINITY;
 		if (prev == null)
 			@:privateAccess MainLoop.pending = next;
 		else
@@ -169,7 +169,7 @@ class MainLoop {
 		while (e != null) {
 			var next = e.next;
 			var wt = e.nextRun - now;
-			if (e.nextRun < 0 || wt <= 0) {
+			if (wt <= 0) {
 				wait = 0;
 				e.call();
 			} else if (wait > wt)

+ 1 - 0
std/haxe/http/HttpBase.hx

@@ -231,6 +231,7 @@ class HttpBase {
 
 	function success(data:Bytes) {
 		responseBytes = data;
+		responseAsString = null;
 		if (hasOnData()) {
 			onData(responseData);
 		}

+ 18 - 0
std/haxe/macro/Context.hx

@@ -585,6 +585,24 @@ class Context {
 		load("register_module_dependency", 2)(modulePath, externFile);
 	}
 
+	/**
+		Creates a timer which will be printed in the compilation report
+		if `--times` compilation argument is set.
+
+		Note that a timer may be omitted from the report if the amount of time
+		measured is too small.
+
+		This method immediately starts a timer and returns a function to stop it:
+		```
+		var stopTimer = haxe.macro.Context.timer("my heavy task");
+		runTask();
+		stopTimer();
+		```
+	**/
+	public static function timer(id:String):()->Void {
+		return load("timer", 1)(id);
+	}
+
 	@:deprecated
 	public static function registerModuleReuseCall(modulePath:String, macroCall:String) {
 		throw "This method is no longer supported. See https://github.com/HaxeFoundation/haxe/issues/5746";

+ 1 - 1
std/haxe/macro/Printer.hx

@@ -165,7 +165,7 @@ class Printer {
 			+ (field.meta != null && field.meta.length > 0 ? field.meta.map(printMetadata).join('\n$tabs') + '\n$tabs' : "")
 			+ (field.access != null && field.access.length > 0 ? field.access.map(printAccess).join(" ") + " " : "")
 			+ switch (field.kind) {
-				case FVar(t, eo): 'var ${field.name}' + opt(t, printComplexType, " : ") + opt(eo, printExpr, " = ");
+				case FVar(t, eo): ((field.access != null && field.access.has(AFinal)) ? '' : 'var ') + '${field.name}' + opt(t, printComplexType, " : ") + opt(eo, printExpr, " = ");
 				case FProp(get, set, t, eo): 'var ${field.name}($get, $set)' + opt(t, printComplexType, " : ") + opt(eo, printExpr, " = ");
 				case FFun(func): 'function ${field.name}' + printFunction(func);
 			}

+ 3 - 0
std/haxe/zip/Huffman.hx

@@ -84,6 +84,9 @@ class HuffTools {
 	}
 
 	public function make(lengths, pos, nlengths, maxbits) {
+		if (nlengths == 1) {
+			return NeedBit(Found(0), Found(0));
+		}
 		var counts = new Array();
 		var tmp = new Array();
 		if (maxbits > 32)

+ 1 - 1
std/hl/_std/sys/db/Sqlite.hx

@@ -78,7 +78,7 @@ private class SqliteConnection implements Connection {
 	var c:SqliteConnectionHandle;
 
 	public function new(file:String) {
-		c = SqliteLib.connect(Sys.getPath(file));
+		c = SqliteLib.connect(file.bytes);
 	}
 
 	public function close():Void {

+ 2 - 0
std/java/Init.hx

@@ -23,9 +23,11 @@ package java;
 
 @:native("haxe.java.Init") @:keep class Init {
 	public static function init():Void {
+		#if std_encoding_utf8
 		try {
 			java.lang.System.setOut(new java.io.PrintStream(java.lang.System.out, true, "utf-8"));
 			java.lang.System.setErr(new java.io.PrintStream(java.lang.System.err, true, "utf-8"));
 		} catch (e:java.io.UnsupportedEncodingException) {}
+		#end
 	}
 }

+ 3 - 1
std/java/_std/EReg.hx

@@ -22,6 +22,8 @@
 
 import java.util.regex.*;
 
+using StringTools;
+
 @:coreApi class EReg {
 	private var pattern:String;
 	private var matcher:Matcher;
@@ -141,7 +143,7 @@ import java.util.regex.*;
 
 	public function replace(s:String, by:String):String {
 		matcher.reset(s);
-		by = by.split("$$").join("\\$");
+		by = by.replace("\\", "\\\\").replace("$$", "\\$");
 		return isGlobal ? matcher.replaceAll(by) : matcher.replaceFirst(by);
 	}
 

+ 1 - 1
std/js/Lib.hx

@@ -135,7 +135,7 @@ class Lib {
 		Generate next unique id
 	**/
 	@:allow(haxe.ds.ObjectMap.assignId)
-	static inline function getNextHaxeUID():{function nextId(counterName:String):Int;} {
+	static inline function getNextHaxeUID():Int {
 		return js.Syntax.code("{0}.$haxeUID++", untyped __define_feature__("$global.$haxeUID", global));
 	}
 }

+ 2 - 1
std/js/lib/Float32Array.hx

@@ -218,7 +218,8 @@ extern class Float32Array implements ArrayBufferView implements ArrayAccess<Floa
 	@:overload(function(array:Uint32Array, ?offset:Int):Void {})
 	@:overload(function(array:Float32Array, ?offset:Int):Void {})
 	@:overload(function(array:Float64Array, ?offset:Int):Void {})
-	function set(array:Array<Int>, ?offset:Int):Void;
+	@:overload(function(array:Array<Int>, ?offset:Int):Void {})
+	function set(array:Array<Float>, ?offset:Int):Void;
 
 	/**
 		Extracts a section of an array and returns a new array.

+ 2 - 1
std/js/lib/Float64Array.hx

@@ -218,7 +218,8 @@ extern class Float64Array implements ArrayBufferView implements ArrayAccess<Floa
 	@:overload(function(array:Uint32Array, ?offset:Int):Void {})
 	@:overload(function(array:Float32Array, ?offset:Int):Void {})
 	@:overload(function(array:Float64Array, ?offset:Int):Void {})
-	function set(array:Array<Int>, ?offset:Int):Void;
+	@:overload(function(array:Array<Int>, ?offset:Int):Void {})
+	function set(array:Array<Float>, ?offset:Int):Void;
 
 	/**
 		Extracts a section of an array and returns a new array.

+ 2 - 0
std/js/lib/Map.hx

@@ -108,3 +108,5 @@ extern class Map<K, V> {
 		return new HaxeIterator(this.entries());
 	}
 }
+
+@:deprecated typedef MapEntry<K, V> = KeyValue<K, V>;

+ 1 - 11
std/lua/Boot.hx

@@ -302,17 +302,7 @@ class Boot {
 		A 32 bit clamp function for numbers
 	**/
 	public inline static function clampInt32(x:Float) {
-#if lua_vanilla
-		if (x < Min_Int32 ) {
-			return Min_Int32;
-		} else if (x > Max_Int32) {
-			return Max_Int32;
-		} else {
-			return Math.floor(x);
-		}
-#else
-		return untyped __define_feature__("lua.Boot.clamp", _hx_bit_clamp(x));
-#end
+        return untyped _hx_bit_clamp(x);
 	}
 
 	/**

+ 12 - 0
std/lua/NativeStringTools.hx

@@ -145,6 +145,18 @@ extern class NativeStringTools {
 		function must be a Lua function without upvalues.
 	**/
 	public static function dump(d:Dynamic):Dynamic;
+
+
+    /**
+        Returns a string that is the concatenation of n copies of
+        the string s separated by the string sep. The default value
+        for sep is the empty string (that is, no separator).
+        Returns the empty string if n is not positive.  (Note that
+        it is very easy to exhaust the memory of your machine with
+        a single call to this function.)
+    **/
+    public static function rep(s:String, n : Int, ?sep : String) : String;
+
 }
 
 @:multiReturn extern class StringFind {

+ 7 - 2
std/lua/_lua/_hx_bit.lua

@@ -1,11 +1,16 @@
 -- require this for lua 5.1
 pcall(require, 'bit')
 if bit then
-  _hx_bit = bit
+  _hx_bit_raw = bit
+  _hx_bit = setmetatable({}, { __index = _hx_bit_raw });
 else
-  local _hx_bit_raw = _G.require('bit32')
+  _hx_bit_raw = _G.require('bit32')
   _hx_bit = setmetatable({}, { __index = _hx_bit_raw });
   -- lua 5.2 weirdness
   _hx_bit.bnot = function(...) return _hx_bit_clamp(_hx_bit_raw.bnot(...)) end;
   _hx_bit.bxor = function(...) return _hx_bit_clamp(_hx_bit_raw.bxor(...)) end;
 end
+-- see https://github.com/HaxeFoundation/haxe/issues/8849
+_hx_bit.bor = function(...) return _hx_bit_clamp(_hx_bit_raw.bor(...)) end;
+_hx_bit.band = function(...) return _hx_bit_clamp(_hx_bit_raw.band(...)) end;
+_hx_bit.arshift = function(...) return _hx_bit_clamp(_hx_bit_raw.arshift(...)) end;

+ 25 - 9
std/lua/_lua/_hx_bit_clamp.lua

@@ -1,10 +1,26 @@
-_hx_bit_clamp = function(v)
-  if v <= 2147483647 and v >= -2147483648 then
-    if v > 0 then return _G.math.floor(v)
-    else return _G.math.ceil(v)
+if _hx_bit_raw then
+    _hx_bit_clamp = function(v)
+    if v <= 2147483647 and v >= -2147483648 then
+        if v > 0 then return _G.math.floor(v)
+        else return _G.math.ceil(v)
+        end
     end
-  end
-  if v > 2251798999999999 then v = v*2 end;
-  if (v ~= v or math.abs(v) == _G.math.huge) then return nil end
-  return _hx_bit.band(v, 2147483647 ) - math.abs(_hx_bit.band(v, 2147483648))
-end
+    if v > 2251798999999999 then v = v*2 end;
+    if (v ~= v or math.abs(v) == _G.math.huge) then return nil end
+    return _hx_bit_raw.band(v, 2147483647 ) - math.abs(_hx_bit_raw.band(v, 2147483648))
+    end
+else
+    _hx_bit_clamp = function(v)
+        if v < -2147483648 then
+            return -2147483648
+        elseif v > 2147483647 then
+            return 2147483647
+        elseif v > 0 then
+            return _G.math.floor(v)
+        else
+            return _G.math.ceil(v)
+        end
+    end
+end;
+
+

+ 37 - 6
std/lua/_std/EReg.hx

@@ -24,7 +24,7 @@ import lua.Table;
 import lua.Lib;
 import lua.lib.lrexlib.Rex;
 // Note - lrexlib gives ascii-based offsets.  Use native string tools.
-import lua.NativeStringTools;
+import lua.NativeStringTools.*;
 
 @:coreApi
 class EReg {
@@ -61,9 +61,13 @@ class EReg {
 	}
 
 	public function match(s:String):Bool {
+		return matchFromByte(s, 1);
+	}
+
+	inline function matchFromByte(s:String, offset:Int):Bool {
 		if (s == null)
 			return false;
-		this.m = lua.TableTools.pack(r.exec(s));
+		this.m = lua.TableTools.pack(r.exec(s, offset));
 		this.s = s;
 		return m[1] != null;
 	}
@@ -72,13 +76,13 @@ class EReg {
 		if (m[1] == null || n < 0)
 			throw "EReg::matched";
 		else if (n == 0) {
-			var k = NativeStringTools.sub(s, m[1], m[2]).match;
+			var k = sub(s, m[1], m[2]).match;
 			return k;
 		} else if (Std.is(m[3], lua.Table)) {
 			var mn = 2 * (n - 1);
 			if (Std.is(untyped m[3][mn + 1], Bool))
 				return null;
-			return NativeStringTools.sub(s, untyped m[3][mn + 1], untyped m[3][mn + 2]).match;
+			return sub(s, untyped m[3][mn + 1], untyped m[3][mn + 2]).match;
 		} else {
 			throw "EReg:matched";
 		}
@@ -87,13 +91,13 @@ class EReg {
 	public function matchedLeft():String {
 		if (m[1] == null)
 			throw "No string matched";
-		return NativeStringTools.sub(s, 1, m[1] - 1).match;
+		return sub(s, 1, m[1] - 1).match;
 	}
 
 	public function matchedRight():String {
 		if (m[1] == null)
 			throw "No string matched";
-		return NativeStringTools.sub(s, m[2] + 1).match;
+		return sub(s, m[2] + 1).match;
 	}
 
 	public function matchedPos():{pos:Int, len:Int} {
@@ -145,6 +149,33 @@ class EReg {
 	}
 
 	public function map(s:String, f:EReg->String):String {
+		var bytesOffset = 1;
+		var buf = new StringBuf();
+		do {
+			if (bytesOffset > len(s)) {
+				break;
+			} else if (!matchFromByte(s, bytesOffset)) {
+				buf.add(sub(s, bytesOffset).match);
+				break;
+			}
+			var pos = m[1];
+			var length = m[2] - m[1];
+			buf.add(sub(s, bytesOffset, pos - 1).match);
+			buf.add(f(this));
+			if (length < 0) {
+				var charBytes = len(sub(s, pos).match.charAt(0));
+				buf.add(sub(s, pos, pos + charBytes - 1).match);
+				bytesOffset = pos + charBytes;
+			} else {
+				bytesOffset = m[2] + 1;
+			}
+		} while (global);
+		if (!global && bytesOffset > 1 && bytesOffset - 1 < len(s))
+			buf.add(sub(s, bytesOffset).match);
+		return buf.toString();
+	}
+
+	function map_old(s:String, f:EReg->String):String {
 		var offset = 0;
 		var buf = new StringBuf();
 		do {

+ 3 - 3
std/lua/_std/Sys.hx

@@ -23,7 +23,7 @@
 import lua.Boot;
 import lua.Io;
 import lua.Lua;
-import lua.Os;
+import lua.lib.luv.Os;
 import lua.lib.luv.Misc;
 import sys.io.FileInput;
 import sys.io.FileOutput;
@@ -109,11 +109,11 @@ class Sys {
 		Misc.chdir(s);
 
 	public inline static function getEnv(s:String):String {
-		return Misc.os_getenv(s);
+		return Os.getenv(s);
 	}
 
 	public inline static function putEnv(s:String, v:String):Void {
-		Misc.os_setenv(s, v);
+		Os.setenv(s, v);
 	}
 
 	public inline static function setTimeLocale(loc:String):Bool {

+ 0 - 6
std/lua/lib/luv/Misc.hx

@@ -26,9 +26,6 @@ package lua.lib.luv;
 extern class Misc {
 	public static function chdir(path:String):Bool;
 
-	public static function os_homedir():String;
-	public static function os_tmpdir():String;
-	public static function os_get_passwd():String;
 	public static function cpu_info():Table<Int, CpuInfo>;
 
 	public static function cwd():String;
@@ -38,9 +35,6 @@ extern class Misc {
 	public static function get_free_memory():Int;
 	public static function getpid():Int;
 
-	public static function os_getenv(env:String):String;
-	public static function os_setenv(env:String, value:String):Void;
-
 	// TODO Windows only?
 	public static function getuid():Int;
 	public static function setuid(from:Int, to:Int):String;

+ 43 - 0
std/lua/lib/luv/Os.hx

@@ -0,0 +1,43 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package lua.lib.luv;
+
+@:luaRequire("luv")
+extern class Os {
+    @:native("os_homedir")
+	public static function homedir():String;
+
+    @:native("os_tmpdir")
+	public static function tmpdir():String;
+
+    @:native("os_get_passwd")
+	public static function get_passwd():String;
+
+    @:native("os_getenv")
+	public static function getenv(env:String):String;
+
+    @:native("os_setenv")
+	public static function setenv(env:String, value:String):Void;
+
+}
+

+ 57 - 0
std/php/ArrayIterator.hx

@@ -0,0 +1,57 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package php;
+
+/**
+	@see https://www.php.net/manual/en/class.arrayiterator.php
+**/
+@:native('ArrayIterator')
+extern class ArrayIterator<K, V> implements php.ArrayAccess<K, V> implements SeekableIterator<K, V> implements Countable implements Serializable {
+	@:phpClassConst static final STD_PROP_LIST:Int;
+	@:phpClassConst static final ARRAY_AS_PROPS:Int;
+
+	function new(?array:NativeArray, ?flags:Int);
+	function append(value:V):Void;
+	function asort():Void;
+	function count():Int;
+	function current():V;
+	function getArrayCopy():NativeArray;
+	function getFlags():Int;
+	function key():K;
+	function ksort():Void;
+	function natcasesort():Void;
+	function natsort():Void;
+	function next():Void;
+	function offsetExists(offset:K):Bool;
+	function offsetGet(offset:K):V;
+	function offsetSet(offset:K, value:V):Void;
+	function offsetUnset(offset:K):Void;
+	function rewind():Void;
+	function seek(position:Int):Void;
+	function serialize():String;
+	function setFlags(flags:Int):Void;
+	function uasort(cmp_function:(a:V, b:V) -> Int):Void;
+	function uksort(cmp_function:(a:K, b:K) -> Int):Void;
+	function unserialize(serialized:String):Void;
+	function valid():Bool;
+}

+ 1 - 1
std/php/Boot.hx

@@ -616,7 +616,7 @@ class Boot {
 	}
 
 	/**
-		Get UTF-8 code of che first character in `s` without any checks
+		Get UTF-8 code of the first character in `s` without any checks
 	**/
 	static public inline function unsafeOrd(s:NativeString):Int {
 		var code = Global.ord(s[0]);

+ 31 - 0
std/php/Countable.hx

@@ -0,0 +1,31 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+ 
+package php;
+
+/**
+	@see https://www.php.net/manual/en/class.countable.php
+**/
+@:native('Countable')
+extern interface Countable {
+	function count():Int;
+}

+ 5 - 3
std/php/IteratorAggregate.hx

@@ -22,13 +22,15 @@
 
 package php;
 
+/**
+	@see https://www.php.net/manual/en/class.iteratoraggregate.php
+**/
 @:native('IteratorAggregate')
-extern interface IteratorAggregate<T> {
+extern interface IteratorAggregate<T> extends Traversable {
 	/**
 		This method is not public to not induce Haxe users to use it ;)
 		Use iterator() instead.
-		The return type would be Aggregator that is unusable in Haxe
 	**/
-	private function getIterator():Iterator<T>; //
+	private function getIterator():Traversable;
 
 }

+ 31 - 0
std/php/JsonSerializable.hx

@@ -0,0 +1,31 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package php;
+
+/**
+	@see https://www.php.net/manual/en/class.jsonserializable.php
+**/
+@:native('JsonSerializable')
+extern interface JsonSerializable<T> {
+	private function jsonSerialize():T;
+}

+ 36 - 0
std/php/NativeIterator.hx

@@ -0,0 +1,36 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package php;
+
+/**
+	Native PHP interface.
+	@see https://www.php.net/manual/en/class.iterator.php
+**/
+@:native('Iterator')
+extern interface NativeIterator<K, V> extends Traversable {
+	function current():V;
+	function key():K;
+	function next():Void;
+	function rewind():Void;
+	function valid():Bool;
+}

+ 8 - 0
std/php/NativeStructArray.hx

@@ -38,4 +38,12 @@ abstract NativeStructArray<T:{}>(NativeArray) to NativeArray {
 	inline function __toObject():T {
 		return Boot.createAnon(this);
 	}
+
+	@:arrayAccess
+	inline function get<V>(key:String):V
+		return this[key];
+
+	@:arrayAccess
+	inline function set<V>(key:String, val:V):V
+		return this[key] = val;
 }

+ 31 - 0
std/php/SeekableIterator.hx

@@ -0,0 +1,31 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+ 
+package php;
+
+/**
+	@see https://www.php.net/manual/en/class.seekableiterator.php
+**/
+@:native('SeekableIterator')
+extern interface SeekableIterator<K, V> extends NativeIterator<K, V> {
+	function seek(position:Int):Void;
+}

+ 32 - 0
std/php/Serializable.hx

@@ -0,0 +1,32 @@
+/*
+ * Copyright (C)2005-2019 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package php;
+
+/**
+	@see https://www.php.net/manual/en/class.serializable.php
+**/
+@:native('Serializable')
+extern interface Serializable {
+    function serialize():String;
+    function unserialize(serialized:String):Void;
+}

+ 15 - 4
std/php/_std/Array.hx

@@ -21,11 +21,12 @@
  */
 
 import php.*;
+import php.ArrayIterator as NativeArrayIterator;
 
 using php.Global;
 
 @:coreApi
-final class Array<T> implements ArrayAccess<Int, T> {
+final class Array<T> implements ArrayAccess<Int, T> implements IteratorAggregate<T> implements JsonSerializable<NativeIndexedArray<T>> {
 	public var length(default, null):Int;
 
 	var arr:NativeIndexedArray<T>;
@@ -83,8 +84,8 @@ final class Array<T> implements ArrayAccess<Int, T> {
 		Global.array_splice(arr, pos, 0, Syntax.arrayDecl(x));
 	}
 
-	@:keep
-	public function iterator():Iterator<T> {
+	@:ifFeature("dynamic_read.iterator", "anon_optional_read.iterator", "anon_read.iterator")
+	public inline function iterator():Iterator<T> {
 		return new ArrayIterator(this);
 	}
 
@@ -228,6 +229,16 @@ final class Array<T> implements ArrayAccess<Int, T> {
 		}
 	}
 
+	@:noCompletion @:keep
+	private function getIterator():Traversable {
+		return new NativeArrayIterator(arr);
+	}
+
+	@:noCompletion @:keep
+	function jsonSerialize():NativeIndexedArray<T> {
+		return arr;
+	}
+
 	static function wrap<T>(arr:NativeIndexedArray<T>):Array<T> {
 		var a = new Array();
 		a.arr = arr;
@@ -272,4 +283,4 @@ private extern interface ArrayAccess<K, V> {
 	private function offsetGet(offset:K):V;
 	private function offsetSet(offset:K, value:V):Void;
 	private function offsetUnset(offset:K):Void;
-}
+}

+ 25 - 25
std/php/_std/EReg.hx

@@ -37,11 +37,16 @@ import php.*;
 		this.pattern = r;
 		options = Global.str_replace('g', '', opt);
 		global = options != opt;
+		options = Global.str_replace('u', '', options);
 		this.re = '"' + Global.str_replace('"', '\\"', r) + '"' + options;
 	}
 
 	public function match(s:String):Bool {
-		var p = Global.preg_match(reUnicode, s, matches, Const.PREG_OFFSET_CAPTURE);
+		return matchFromByte(s, 0);
+	}
+
+	inline function matchFromByte(s:String, bytesOffset:Int):Bool {
+		var p = Global.preg_match(reUnicode, s, matches, Const.PREG_OFFSET_CAPTURE, bytesOffset);
 		if (p == false) {
 			handlePregError();
 			p = Global.preg_match(re, s, matches, Const.PREG_OFFSET_CAPTURE);
@@ -65,9 +70,9 @@ import php.*;
 		} else if (e == Const.PREG_JIT_STACKLIMIT_ERROR) {
 			throw 'failed due to limited JIT stack space';
 		}
-		// else if(e == PREG_BAD_UTF8_ERROR) {
+		// else if(e == Const.PREG_BAD_UTF8_ERROR) {
 		// 	throw 'EReg: malformed UTF8';
-		// } else if(e == PREG_BAD_UTF8_OFFSET_ERROR) {
+		// } else if(e == Const.PREG_BAD_UTF8_OFFSET_ERROR) {
 		// 	throw 'EReg: the offset didn\'t correspond to the begin of a valid UTF-8 code point';
 		// }
 	}
@@ -75,7 +80,7 @@ import php.*;
 	public function matched(n:Int):String {
 		if (matches == null || n < 0)
 			throw "EReg::matched";
-		// we can't differenciate between optional groups at the end of a match
+		// we can't differentiate between optional groups at the end of a match
 		// that have not been matched and invalid groups
 		if (n >= Global.count(matches))
 			return null;
@@ -143,30 +148,25 @@ import php.*;
 	}
 
 	public function map(s:String, f:EReg->String):String {
-		var offset = 0;
-		var buf = new StringBuf();
-		var length = s.length;
+		if(!matchFromByte(s, 0)) {
+			return s;
+		}
+		var result = '';
+		var bytesOffset = 0;
+		var bytesTotal = Global.strlen(s);
 		do {
-			if (offset >= length) {
-				break;
-			} else if (!matchSub(s, offset)) {
-				buf.add(s.substr(offset));
-				break;
-			}
-			var p = matchedPos();
-			buf.add(s.substr(offset, p.pos - offset));
-			buf.add(f(this));
-			if (p.len == 0) {
-				buf.add(s.substr(p.pos, 1));
-				offset = p.pos + 1;
+			result += Global.substr(s, bytesOffset, matches[0][1] - bytesOffset);
+			result += f(this);
+			bytesOffset = matches[0][1];
+			if(matches[0][0] == '') {
+				result += Global.mb_substr(Global.substr(s, bytesOffset), 0, 1);
+				bytesOffset++;
 			} else {
-				offset = p.pos + p.len;
+				bytesOffset += Global.strlen(matches[0][0]);
 			}
-		} while (global);
-		if (!global && offset > 0 && offset < length) {
-			buf.add(s.substr(offset));
-		}
-		return buf.toString();
+		} while(global && bytesOffset < bytesTotal && matchFromByte(s, bytesOffset));
+		result += Global.substr(s, bytesOffset);
+		return result;
 	}
 
 	public static inline function escape(s:String):String {

+ 3 - 1
std/php/_std/haxe/ds/IntMap.hx

@@ -59,11 +59,13 @@ import php.NativeIndexedArray;
 		return Global.array_keys(data).iterator();
 	}
 
+	@:ifFeature("dynamic_read.iterator", "anon_optional_read.iterator", "anon_read.iterator")
 	public inline function iterator():Iterator<T> {
 		return Global.array_values(data).iterator();
 	}
 
-	@:runtime public inline function keyValueIterator():KeyValueIterator<Int, T> {
+	@:ifFeature("dynamic_read.keyValueIterator", "anon_optional_read.keyValueIterator", "anon_read.keyValueIterator")
+	public inline function keyValueIterator():KeyValueIterator<Int, T> {
 		return new haxe.iterators.MapKeyValueIterator(this);
 	}
 

+ 3 - 1
std/php/_std/haxe/ds/ObjectMap.hx

@@ -63,11 +63,13 @@ class ObjectMap<K:{}, V> implements haxe.Constraints.IMap<K, V> {
 		return _keys.iterator();
 	}
 
+	@:ifFeature("dynamic_read.iterator", "anon_optional_read.iterator", "anon_read.iterator")
 	public inline function iterator():Iterator<V> {
 		return _values.iterator();
 	}
 
-	@:runtime public inline function keyValueIterator():KeyValueIterator<K, V> {
+	@:ifFeature("dynamic_read.keyValueIterator", "anon_optional_read.keyValueIterator", "anon_read.keyValueIterator")
+	public inline function keyValueIterator():KeyValueIterator<K, V> {
 		return new haxe.iterators.MapKeyValueIterator(this);
 	}
 

+ 3 - 1
std/php/_std/haxe/ds/StringMap.hx

@@ -60,11 +60,13 @@ import haxe.Constraints;
 		return Global.array_map('strval', Global.array_keys(data)).iterator();
 	}
 
+	@:ifFeature("dynamic_read.iterator", "anon_optional_read.iterator", "anon_read.iterator")
 	public inline function iterator():Iterator<T> {
 		return data.iterator();
 	}
 
-	@:runtime public inline function keyValueIterator():KeyValueIterator<String, T> {
+	@:ifFeature("dynamic_read.keyValueIterator", "anon_optional_read.keyValueIterator", "anon_read.keyValueIterator")
+	public inline function keyValueIterator():KeyValueIterator<String, T> {
 		return new haxe.iterators.MapKeyValueIterator(this);
 	}
 

+ 64 - 64
std/php/db/PDO.hx

@@ -26,70 +26,70 @@ import php.*;
 
 @:native('PDO')
 extern class PDO {
-	@:phpClassConst static var PARAM_BOOL:Int;
-	@:phpClassConst static var PARAM_NULL:Int;
-	@:phpClassConst static var PARAM_INT:Int;
-	@:phpClassConst static var PARAM_STR:Int;
-	@:phpClassConst static var PARAM_LOB:Int;
-	@:phpClassConst static var PARAM_STMT:Int;
-	@:phpClassConst static var PARAM_INPUT_OUTPUT:Int;
-	@:phpClassConst static var FETCH_LAZY:Int;
-	@:phpClassConst static var FETCH_ASSOC:Int;
-	@:phpClassConst static var FETCH_NAMED:Int;
-	@:phpClassConst static var FETCH_NUM:Int;
-	@:phpClassConst static var FETCH_BOTH:Int;
-	@:phpClassConst static var FETCH_OBJ:Int;
-	@:phpClassConst static var FETCH_BOUND:Int;
-	@:phpClassConst static var FETCH_COLUMN:Int;
-	@:phpClassConst static var FETCH_CLASS:Int;
-	@:phpClassConst static var FETCH_INTO:Int;
-	@:phpClassConst static var FETCH_FUNC:Int;
-	@:phpClassConst static var FETCH_GROUP:Int;
-	@:phpClassConst static var FETCH_UNIQUE:Int;
-	@:phpClassConst static var FETCH_KEY_PAIR:Int;
-	@:phpClassConst static var FETCH_CLASSTYPE:Int;
-	@:phpClassConst static var FETCH_SERIALIZE:Int;
-	@:phpClassConst static var FETCH_PROPS_LATE:Int;
-	@:phpClassConst static var ATTR_AUTOCOMMIT:Int;
-	@:phpClassConst static var ATTR_PREFETCH:Int;
-	@:phpClassConst static var ATTR_TIMEOUT:Int;
-	@:phpClassConst static var ATTR_ERRMODE:Int;
-	@:phpClassConst static var ATTR_SERVER_VERSION:Int;
-	@:phpClassConst static var ATTR_CLIENT_VERSION:Int;
-	@:phpClassConst static var ATTR_SERVER_INFO:Int;
-	@:phpClassConst static var ATTR_CONNECTION_STATUS:Int;
-	@:phpClassConst static var ATTR_CASE:Int;
-	@:phpClassConst static var ATTR_CURSOR_NAME:Int;
-	@:phpClassConst static var ATTR_CURSOR:Int;
-	@:phpClassConst static var ATTR_DRIVER_NAME:String;
-	@:phpClassConst static var ATTR_ORACLE_NULLS:Int;
-	@:phpClassConst static var ATTR_PERSISTENT:Int;
-	@:phpClassConst static var ATTR_STATEMENT_CLASS:Int;
-	@:phpClassConst static var ATTR_FETCH_TABLE_NAMES:Int;
-	@:phpClassConst static var ATTR_STRINGIFY_FETCHES:Int;
-	@:phpClassConst static var ATTR_EMULATE_PREPARES:Int;
-	@:phpClassConst static var ERRMODE_SILENT:Int;
-	@:phpClassConst static var ERRMODE_WARNING:Int;
-	@:phpClassConst static var ERRMODE_EXCEPTION:Int;
-	@:phpClassConst static var CASE_NATURAL:Int;
-	@:phpClassConst static var CASE_LOWER:Int;
-	@:phpClassConst static var CASE_UPPER:Int;
-	@:phpClassConst static var NULL_NATURAL:Int;
-	@:phpClassConst static var FETCH_ORI_PRIOR:Int;
-	@:phpClassConst static var FETCH_ORI_FIRST:Int;
-	@:phpClassConst static var FETCH_ORI_LAST:Int;
-	@:phpClassConst static var FETCH_ORI_ABS:Int;
-	@:phpClassConst static var FETCH_ORI_REL:Int;
-	@:phpClassConst static var CURSOR_FWDONLY:Int;
-	@:phpClassConst static var CURSOR_SCROLL:Int;
-	@:phpClassConst static var ERR_NONE:String;
-	@:phpClassConst static var PARAM_EVT_ALLOC:Int;
-	@:phpClassConst static var PARAM_EVT_FREE:Int;
-	@:phpClassConst static var PARAM_EVT_EXEC_PRE:Int;
-	@:phpClassConst static var PARAM_EVT_EXEC_POST:Int;
-	@:phpClassConst static var PARAM_EVT_FETCH_PRE:Int;
-	@:phpClassConst static var PARAM_EVT_FETCH_POST:Int;
-	@:phpClassConst static var PARAM_EVT_NORMALIZE:Int;
+	@:phpClassConst static final PARAM_BOOL:Int;
+	@:phpClassConst static final PARAM_NULL:Int;
+	@:phpClassConst static final PARAM_INT:Int;
+	@:phpClassConst static final PARAM_STR:Int;
+	@:phpClassConst static final PARAM_LOB:Int;
+	@:phpClassConst static final PARAM_STMT:Int;
+	@:phpClassConst static final PARAM_INPUT_OUTPUT:Int;
+	@:phpClassConst static final FETCH_LAZY:Int;
+	@:phpClassConst static final FETCH_ASSOC:Int;
+	@:phpClassConst static final FETCH_NAMED:Int;
+	@:phpClassConst static final FETCH_NUM:Int;
+	@:phpClassConst static final FETCH_BOTH:Int;
+	@:phpClassConst static final FETCH_OBJ:Int;
+	@:phpClassConst static final FETCH_BOUND:Int;
+	@:phpClassConst static final FETCH_COLUMN:Int;
+	@:phpClassConst static final FETCH_CLASS:Int;
+	@:phpClassConst static final FETCH_INTO:Int;
+	@:phpClassConst static final FETCH_FUNC:Int;
+	@:phpClassConst static final FETCH_GROUP:Int;
+	@:phpClassConst static final FETCH_UNIQUE:Int;
+	@:phpClassConst static final FETCH_KEY_PAIR:Int;
+	@:phpClassConst static final FETCH_CLASSTYPE:Int;
+	@:phpClassConst static final FETCH_SERIALIZE:Int;
+	@:phpClassConst static final FETCH_PROPS_LATE:Int;
+	@:phpClassConst static final ATTR_AUTOCOMMIT:Int;
+	@:phpClassConst static final ATTR_PREFETCH:Int;
+	@:phpClassConst static final ATTR_TIMEOUT:Int;
+	@:phpClassConst static final ATTR_ERRMODE:Int;
+	@:phpClassConst static final ATTR_SERVER_VERSION:Int;
+	@:phpClassConst static final ATTR_CLIENT_VERSION:Int;
+	@:phpClassConst static final ATTR_SERVER_INFO:Int;
+	@:phpClassConst static final ATTR_CONNECTION_STATUS:Int;
+	@:phpClassConst static final ATTR_CASE:Int;
+	@:phpClassConst static final ATTR_CURSOR_NAME:Int;
+	@:phpClassConst static final ATTR_CURSOR:Int;
+	@:phpClassConst static final ATTR_DRIVER_NAME:String;
+	@:phpClassConst static final ATTR_ORACLE_NULLS:Int;
+	@:phpClassConst static final ATTR_PERSISTENT:Int;
+	@:phpClassConst static final ATTR_STATEMENT_CLASS:Int;
+	@:phpClassConst static final ATTR_FETCH_TABLE_NAMES:Int;
+	@:phpClassConst static final ATTR_STRINGIFY_FETCHES:Int;
+	@:phpClassConst static final ATTR_EMULATE_PREPARES:Int;
+	@:phpClassConst static final ERRMODE_SILENT:Int;
+	@:phpClassConst static final ERRMODE_WARNING:Int;
+	@:phpClassConst static final ERRMODE_EXCEPTION:Int;
+	@:phpClassConst static final CASE_NATURAL:Int;
+	@:phpClassConst static final CASE_LOWER:Int;
+	@:phpClassConst static final CASE_UPPER:Int;
+	@:phpClassConst static final NULL_NATURAL:Int;
+	@:phpClassConst static final FETCH_ORI_PRIOR:Int;
+	@:phpClassConst static final FETCH_ORI_FIRST:Int;
+	@:phpClassConst static final FETCH_ORI_LAST:Int;
+	@:phpClassConst static final FETCH_ORI_ABS:Int;
+	@:phpClassConst static final FETCH_ORI_REL:Int;
+	@:phpClassConst static final CURSOR_FWDONLY:Int;
+	@:phpClassConst static final CURSOR_SCROLL:Int;
+	@:phpClassConst static final ERR_NONE:String;
+	@:phpClassConst static final PARAM_EVT_ALLOC:Int;
+	@:phpClassConst static final PARAM_EVT_FREE:Int;
+	@:phpClassConst static final PARAM_EVT_EXEC_PRE:Int;
+	@:phpClassConst static final PARAM_EVT_EXEC_POST:Int;
+	@:phpClassConst static final PARAM_EVT_FETCH_PRE:Int;
+	@:phpClassConst static final PARAM_EVT_FETCH_POST:Int;
+	@:phpClassConst static final PARAM_EVT_NORMALIZE:Int;
 
 	function new(dns:String, ?username:String, ?password:String, ?options:NativeArray):Void;
 	function beginTransaction():Bool;

+ 3 - 3
std/php/reflection/ReflectionClass.hx

@@ -26,9 +26,9 @@ import haxe.extern.Rest;
 
 @:native('ReflectionClass')
 extern class ReflectionClass implements Reflector {
-	@:phpClassConst static var IS_IMPLICIT_ABSTRACT:Int;
-	@:phpClassConst static var IS_EXPLICIT_ABSTRACT:Int;
-	@:phpClassConst static var IS_FINAL:Int;
+	@:phpClassConst static final IS_IMPLICIT_ABSTRACT:Int;
+	@:phpClassConst static final IS_EXPLICIT_ABSTRACT:Int;
+	@:phpClassConst static final IS_FINAL:Int;
 
 	static function export(argument:Dynamic, returnValue:Bool = false):String;
 

+ 6 - 6
std/php/reflection/ReflectionMethod.hx

@@ -27,12 +27,12 @@ import haxe.extern.Rest;
 
 @:native('ReflectionMethod')
 extern class ReflectionMethod extends ReflectionFunctionAbstract {
-	@:phpClassConst static var IS_STATIC:Int;
-	@:phpClassConst static var IS_PUBLIC:Int;
-	@:phpClassConst static var IS_PROTECTED:Int;
-	@:phpClassConst static var IS_PRIVATE:Int;
-	@:phpClassConst static var IS_ABSTRACT:Int;
-	@:phpClassConst static var IS_FINAL:Int;
+	@:phpClassConst static final IS_STATIC:Int;
+	@:phpClassConst static final IS_PUBLIC:Int;
+	@:phpClassConst static final IS_PROTECTED:Int;
+	@:phpClassConst static final IS_PRIVATE:Int;
+	@:phpClassConst static final IS_ABSTRACT:Int;
+	@:phpClassConst static final IS_FINAL:Int;
 
 	// public var class : String;
 	public static function export(className:String, name:String, ?returnValue:Bool):String;

+ 4 - 4
std/php/reflection/ReflectionProperty.hx

@@ -24,10 +24,10 @@ package php.reflection;
 
 @:native('ReflectionProperty')
 extern class ReflectionProperty implements Reflector {
-	@:phpClassConst static var IS_STATIC:Int;
-	@:phpClassConst static var IS_:Int;
-	@:phpClassConst static var IS_PROTECTED:Int;
-	@:phpClassConst static var IS_PRIVATE:Int;
+	@:phpClassConst static final IS_STATIC:Int;
+	@:phpClassConst static final IS_:Int;
+	@:phpClassConst static final IS_PROTECTED:Int;
+	@:phpClassConst static final IS_PRIVATE:Int;
 
 	var name:String;
 

+ 24 - 2
std/python/internal/HxOverrides.hx

@@ -142,12 +142,34 @@ class HxOverrides {
 
 	@:ifFeature("binop_%")
 	static public function modf(a:Float, b:Float) {
-		return Syntax.code("float('nan') if (b == 0.0) else a % b if a >= 0 else -(-a % b)");
+		if(b == 0.0) {
+			return Syntax.code("float('nan')");
+		} else if(a < 0) {
+			if(b < 0) {
+				return Syntax.code("-(-{0} % (-{1}))", a, b);
+			} else {
+				return Syntax.code("-(-{0} % {1})", a, b);
+			}
+		} else if(b < 0) {
+			return Syntax.code("{0} % (-{1})", a, b);
+		} else {
+			return Syntax.code("{0} % {1}", a, b);
+		}
 	}
 
 	@:ifFeature("binop_%")
 	static public function mod(a:Int, b:Int) {
-		return Syntax.code("a % b if a >= 0 else -(-a % b)");
+		if(a < 0) {
+			if(b < 0) {
+				return Syntax.code("-(-{0} % (-{1}))", a, b);
+			} else {
+				return Syntax.code("-(-{0} % {1})", a, b);
+			}
+		} else if(b < 0) {
+			return Syntax.code("{0} % (-{1})", a, b);
+		} else {
+			return Syntax.code("{0} % {1}", a, b);
+		}
 	}
 
 	@:ifFeature("dynamic_array_read")

+ 23 - 0
tests/display/src/cases/Issue8789.hx

@@ -0,0 +1,23 @@
+package cases;
+
+class Issue8789 extends DisplayTestCase {
+	/**
+		abstract Int8(Int) {
+			inline function new(value:Int) {
+				this = value;
+			}
+
+			inline function pvt() {}
+
+			public function test() {
+				var i = new Int8{-1-}(10);
+				i.{-2-}
+			}
+		}
+	**/
+	function test() {
+		var r = toplevel(pos(1));
+		eq(true, hasToplevel(r, "type", "Int8"));
+		eq(true, hasField(fields(pos(2)), "pvt", "Void -> Void"));
+	}
+}

+ 5 - 0
tests/misc/php/projects/Issue8851/compile.hxml

@@ -0,0 +1,5 @@
+-cp src
+-main Main
+-dce full
+-php bin
+--cmd php bin/index.php

+ 16 - 0
tests/misc/php/projects/Issue8851/src/Main.hx

@@ -0,0 +1,16 @@
+class Main {
+	static var tmp:Any;
+
+	static function main() {
+		iter([1 => 2]);
+		iter(['1' => 2]);
+		iter([{field:1} => 2]);
+		iter([1, 2]);
+	}
+
+	static function iter<T>(it:Iterable<T>) {
+		for(i in it) {
+			tmp = i;
+		}
+	}
+}

+ 1 - 1
tests/misc/projects/Issue7526/compile-fail.hxml.stderr

@@ -1,4 +1,4 @@
 Main.hx:16: characters 9-29 : Class<C> should be { member : Int }
 Main.hx:16: characters 9-29 : The field member is not public
-Main.hx:17: characters 9-29 : Class<_Main.A_Impl_> should be { member : Int }
+Main.hx:17: characters 9-29 : Abstract<A> should be { member : Int }
 Main.hx:17: characters 9-29 : The field member is not public

+ 7 - 0
tests/misc/projects/Issue8819/Main.hx

@@ -0,0 +1,7 @@
+class Main {
+	static function main() {
+		Type.allEnums(Foo);
+	}
+}
+
+enum abstract Foo(String) {}

+ 1 - 0
tests/misc/projects/Issue8819/compile-fail.hxml

@@ -0,0 +1 @@
+-main Main

+ 2 - 0
tests/misc/projects/Issue8819/compile-fail.hxml.stderr

@@ -0,0 +1,2 @@
+Main.hx:3: characters 17-20 : Abstract<Foo> should be Enum<Unknown<0>>
+Main.hx:3: characters 17-20 : For function argument 'e'

Некоторые файлы не были показаны из-за большого количества измененных файлов