瀏覽代碼

Merge branch 'development' into constrained_monomorphs

# Conflicts:
#	src/core/type.ml
#	src/typing/fields.ml
#	src/typing/generic.ml
Simon Krajewski 5 年之前
父節點
當前提交
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=
 LFLAGS=
 STATICLINK?=0
 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
 # Configuration
 
 
 ADD_REVISION?=0
 ADD_REVISION?=0
@@ -61,6 +74,11 @@ haxe:
 	$(DUNE_COMMAND) build --workspace dune-workspace.dev src/haxe.exe
 	$(DUNE_COMMAND) build --workspace dune-workspace.dev src/haxe.exe
 	cp -f _build/default/src/haxe.exe ./${HAXE_OUTPUT}
 	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:
 kill_exe_win:
 ifdef SYSTEMROOT
 ifdef SYSTEMROOT
 	-@taskkill /F /IM haxe.exe 2>/dev/null
 	-@taskkill /F /IM haxe.exe 2>/dev/null
@@ -143,7 +161,7 @@ $(INSTALLER_TMP_DIR):
 	mkdir -p $(INSTALLER_TMP_DIR)
 	mkdir -p $(INSTALLER_TMP_DIR)
 
 
 $(INSTALLER_TMP_DIR)/neko-osx64.tar.gz: $(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
 # Installer
 
 

+ 1 - 1
Makefile.win

@@ -72,7 +72,7 @@ package_choco:
 	rm -rf out/choco
 	rm -rf out/choco
 
 
 $(INSTALLER_TMP_DIR)/neko-win.zip: $(INSTALLER_TMP_DIR)
 $(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
 package_installer_win: $(INSTALLER_TMP_DIR)/neko-win.zip package_win
 	$(eval OUTFILE := $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_installer.zip)
 	$(eval OUTFILE := $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_installer.zip)

+ 16 - 3
azure-pipelines.yml

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

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

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

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

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

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

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

+ 1 - 0
extra/release-checklist.txt

@@ -3,6 +3,7 @@
 - Check that haxelib is working
 - Check that haxelib is working
 - Make sure to update the haxelib submodule
 - Make sure to update the haxelib submodule
 - Check that the run-time haxelibs are ready for release: hxcpp, hxjava, hxcs
 - 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
 # Making the release
 
 

+ 1 - 0
libs/ilib/dune

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

+ 2 - 1
libs/ttflib/dune

@@ -2,6 +2,7 @@
 
 
 (library
 (library
 	(name ttflib)
 	(name ttflib)
-	(libraries extlib extlib_leftovers swflib)
+	(libraries extlib extlib_leftovers swflib unix)
+	(modules (:standard \ main))
 	(wrapped false)
 	(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.",
 		"doc": "Record per-method execution times in macro/interp mode. Implies eval_stack.",
 		"platforms": ["eval"]
 		"platforms": ["eval"]
 	},
 	},
+	{
+		"name": "FilterTimes",
+		"define": "filter_times",
+		"doc": "Record per-filter execution times upon --times."
+	},
 	{
 	{
 		"name": "FastCast",
 		"name": "FastCast",
 		"define": "fast_cast",
 		"define": "fast_cast",
@@ -550,6 +555,12 @@
 		"define": "static",
 		"define": "static",
 		"doc": "Defined if the current target is 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",
 		"name": "Swc",
 		"define": "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 *)
 				(* unify applied original *)
 			with
 			with
 				| Unify_error el ->
 				| 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 _ ->
 				| Invalid_argument _ ->
 						gen.gcon.warning ("This expression may be invalid") pos
 						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
 		as it will help normalize the AST
 *)
 *)
 
 
-let rec filter_param stack t =
+let rec filter_param (stack:t list) t =
 	match t with
 	match t with
 	| TInst({ cl_kind = KTypeParameter _ } as c,_) when Meta.has Meta.EnumConstructorParam c.cl_meta ->
 	| TInst({ cl_kind = KTypeParameter _ } as c,_) when Meta.has Meta.EnumConstructorParam c.cl_meta ->
 		t_dynamic
 		t_dynamic
@@ -39,10 +39,8 @@ let rec filter_param stack t =
 		| Some t -> filter_param stack t)
 		| Some t -> filter_param stack t)
 	| TInst(_,[]) | TEnum(_,[]) | TAbstract(_,[]) ->
 	| TInst(_,[]) | TEnum(_,[]) | TAbstract(_,[]) ->
 		t
 		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) ->
 	| 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,tl) ->
 		TInst(c,List.map (filter_param stack) tl)
 		TInst(c,List.map (filter_param stack) tl)
 	| TEnum(e,tl) ->
 	| TEnum(e,tl) ->
@@ -51,7 +49,7 @@ let rec filter_param stack t =
 		TAbstract(a, List.map (filter_param stack) tl)
 		TAbstract(a, List.map (filter_param stack) tl)
 	| TAbstract({a_path = [],"Null"} as a,[t]) ->
 	| TAbstract({a_path = [],"Null"} as a,[t]) ->
 		TAbstract(a,[filter_param stack 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)
 		filter_param stack (Abstract.get_underlying_type a tl)
 	| TAbstract(a,tl) ->
 	| TAbstract(a,tl) ->
 		TAbstract(a, List.map (filter_param stack) 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
 	) path
 
 
 let add_libs com libs =
 let add_libs com libs =
+	let global_repo = List.exists (fun a -> a = "--haxelib-global") com.args in
 	let call_haxelib() =
 	let call_haxelib() =
 		let t = Timer.timer ["haxelib"] in
 		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 pin, pout, perr = Unix.open_process_full cmd (Unix.environment()) in
 		let lines = Std.input_list pin in
 		let lines = Std.input_list pin in
 		let err = Std.input_list perr in
 		let err = Std.input_list perr in
@@ -939,6 +940,7 @@ try
 		("Compilation",["-C";"--cwd"],[], Arg.String (fun dir ->
 		("Compilation",["-C";"--cwd"],[], Arg.String (fun dir ->
 			assert false
 			assert false
 		),"<dir>","set current working directory");
 		),"<dir>","set current working directory");
+		("Compilation",["--haxelib-global"],[], Arg.Unit (fun () -> ()),"","pass --global argument to haxelib");
 	] in
 	] in
 	let args_callback cl =
 	let args_callback cl =
 		begin try
 		begin try

+ 2 - 22
src/compiler/server.ml

@@ -183,8 +183,6 @@ module ServerCompilationContext = struct
 		mutable compilation_mark : int;
 		mutable compilation_mark : int;
 		(* A list of delays which are run after compilation *)
 		(* A list of delays which are run after compilation *)
 		mutable delays : (unit -> unit) list;
 		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 *)
 		(* True if it's an actual compilation, false if it's a display operation *)
 		mutable was_compilation : bool;
 		mutable was_compilation : bool;
 	}
 	}
@@ -198,7 +196,6 @@ module ServerCompilationContext = struct
 		compilation_mark = 0;
 		compilation_mark = 0;
 		mark_loop = 0;
 		mark_loop = 0;
 		delays = [];
 		delays = [];
-		removed_modules = [];
 		was_compilation = false;
 		was_compilation = false;
 	}
 	}
 
 
@@ -210,9 +207,6 @@ module ServerCompilationContext = struct
 		sctx.delays <- [];
 		sctx.delays <- [];
 		List.iter (fun f -> f()) fl
 		List.iter (fun f -> f()) fl
 
 
-	let is_removed_module sctx m =
-		List.exists (fun (_,_,m') -> m == m') sctx.removed_modules
-
 	let reset sctx =
 	let reset sctx =
 		Hashtbl.clear sctx.changed_directories;
 		Hashtbl.clear sctx.changed_directories;
 		sctx.was_compilation <- false
 		sctx.was_compilation <- false
@@ -417,8 +411,6 @@ let add_modules sctx ctx m p =
 			| MCode, MMacro | MMacro, MCode ->
 			| MCode, MMacro | MMacro, MCode ->
 				(* this was just a dependency to check : do not add to the context *)
 				(* 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;
 				PMap.iter (Hashtbl.replace com.resources) m.m_extra.m_binded_res;
-			| _ when is_removed_module sctx m ->
-				()
 			| _ ->
 			| _ ->
 				ServerMessage.reusing com tabs m;
 				ServerMessage.reusing com tabs m;
 				m.m_extra.m_added <- sctx.compilation_step;
 				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. *)
 (* Sets up the per-compilation context. *)
 let create sctx write params =
 let create sctx write params =
 	let cs = sctx.cs in
 	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 =
 	let maybe_cache_context com =
 		if com.display.dms_full_typing then begin
 		if com.display.dms_full_typing then begin
 			CommonCache.cache_context sctx.cs com;
 			CommonCache.cache_context sctx.cs com;
 			ServerMessage.cached_modules com "" (List.length com.modules);
 			ServerMessage.cached_modules com "" (List.length com.modules);
-			sctx.removed_modules <- [];
-		end else
-			recache_removed_modules ()
+		end
 	in
 	in
 	let ctx = create_context params in
 	let ctx = create_context params in
 	ctx.flush <- (fun() ->
 	ctx.flush <- (fun() ->
@@ -523,8 +504,7 @@ let create sctx write params =
 			let file = (DisplayPosition.display_position#get).pfile in
 			let file = (DisplayPosition.display_position#get).pfile in
 			(* force parsing again : if the completion point have been changed *)
 			(* force parsing again : if the completion point have been changed *)
 			cs#remove_files file;
 			cs#remove_files file;
-			sctx.removed_modules <- cs#filter_modules file;
-			add_delay sctx recache_removed_modules;
+			cs#taint_modules file;
 		end;
 		end;
 		try
 		try
 			if (Hashtbl.find sctx.class_paths sign) <> ctx.com.class_path then begin
 			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
 			) cc#get_modules
 		) contexts
 		) 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 *)
 	(* haxelibs *)
 
 
 	method find_haxelib key =
 	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
 		true
 	else if not in_overload && ctx.com.config.pf_overload && Meta.has Meta.Overload cf.cf_meta then
 	else if not in_overload && ctx.com.config.pf_overload && Meta.has Meta.Overload cf.cf_meta then
 		true
 		true
+	else if c == ctx.curclass then
+		true
 	else
 	else
-	(* TODO: should we add a c == ctx.curclass short check here? *)
 	(* has metadata path *)
 	(* has metadata path *)
 	let rec make_path c f = match c.cl_kind with
 	let rec make_path c f = match c.cl_kind with
 		| KAbstractImpl a -> fst a.a_path @ [snd a.a_path; f.cf_name]
 		| 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 Meta
-open Type
+open TType
+open TFunctions
+open TPrinting
+open TUnification
 open Error
 open Error
 
 
 let build_abstract a = match a.a_impl with
 let build_abstract a = match a.a_impl with

+ 8 - 4
src/core/error.ml

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

+ 2 - 2
src/core/globals.ml

@@ -24,11 +24,11 @@ type platform =
 	| Hl
 	| Hl
 	| Eval
 	| Eval
 
 
-let version = 4000
+let version = 4100
 let version_major = version / 1000
 let version_major = version / 1000
 let version_minor = (version mod 1000) / 100
 let version_minor = (version mod 1000) / 100
 let version_revision = (version mod 100)
 let version_revision = (version mod 100)
-let version_pre = Some "rc.5"
+let version_pre = Some "rc.1"
 
 
 let macro_platform = ref Neko
 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 Globals
 open Ast
 open Ast
-open Type
+open TType
+open TFunctions
+open TUnification
+open TPrinting
 open Error
 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
 let equal_fa fa1 fa2 = match fa1,fa2 with
 	| FStatic(c1,cf1),FStatic(c2,cf2) -> c1 == c2 && cf1.cf_name == cf2.cf_name
 	| 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
 	| 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 _
 	| TParenthesis _ | TTypeExpr _ | TLocal _ | TMeta _
 	| TConst _ | TContinue | TBreak | TCast _ | TIdent _ ->
 	| TConst _ | TContinue | TBreak | TCast _ | TIdent _ ->
 		try
 		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;
 			false;
 		with Exit ->
 		with Exit ->
 			true
 			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 (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 enext = mk (TCall(enext,[])) v.v_type e1.epos in
 	let eassign = mk (TVar(v,Some enext)) basic.tvoid p 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 (TBlock [
 		mk (TVar (v',Some e1)) basic.tvoid e1.epos;
 		mk (TVar (v',Some e1)) basic.tvoid e1.epos;
 		mk (TWhile((mk (TParenthesis ehasnext) ehasnext.etype ehasnext.epos),ebody,NormalWhile)) 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;
 				loop e;
 			) catches
 			) catches
 		| _ ->
 		| _ ->
-			Type.iter loop e
+			iter loop e
 	in
 	in
 	loop e;
 	loop e;
 	List.rev !unknown,!accesses_this
 	List.rev !unknown,!accesses_this

+ 4 - 0
src/core/timer.ml

@@ -77,6 +77,10 @@ let timer id =
 	) else
 	) else
 		(fun() -> ())
 		(fun() -> ())
 
 
+let current_id() =
+	match !curtime with
+	| [] -> None
+	| t :: _ -> Some t.id
 
 
 let rec close_times() =
 let rec close_times() =
 	let now = get_time() in
 	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.
 	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_bind_ref := Monomorph.bind;;
 monomorph_create_ref := Monomorph.create;;
 monomorph_create_ref := Monomorph.create;;

+ 11 - 3
src/dune

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

+ 2 - 2
src/filters/filters.ml

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

+ 3 - 6
src/generators/genjs.ml

@@ -962,11 +962,8 @@ and gen_syntax ctx meth args pos =
 		in
 		in
 		begin
 		begin
 			match args with
 			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
 				Codegen.interpolate_code ctx.com code args (spr ctx) (gen_value ctx) code_pos
 		end
 		end
@@ -1776,7 +1773,7 @@ let generate com =
 	end;
 	end;
 	if has_feature ctx "$global.$haxeUID" then begin
 	if has_feature ctx "$global.$haxeUID" then begin
 		add_feature ctx "js.Lib.global";
 		add_feature ctx "js.Lib.global";
-		print ctx "if(typeof $global.$haxeUID == \"undefined\") $global.$haxeUID = 0;\n";
+		print ctx "$global.$haxeUID |= 0;\n";
 	end;
 	end;
 	List.iter (gen_block_element ~after:true ctx) (List.rev ctx.inits);
 	List.iter (gen_block_element ~after:true ctx) (List.rev ctx.inits);
 	List.iter (generate_static ctx) (List.rev ctx.statics);
 	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 java_hash s =
 	let h = ref Int32.zero in
 	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
 	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;
 	) s;
 	!h
 	!h
 
 

+ 6 - 3
src/generators/genlua.ml

@@ -917,8 +917,8 @@ and gen_expr ?(local=true) ctx e = begin
     | TWhile (cond,e,Ast.NormalWhile) ->
     | TWhile (cond,e,Ast.NormalWhile) ->
         gen_loop ctx "while" cond e
         gen_loop ctx "while" cond e
     | TWhile (cond,e,Ast.DoWhile) ->
     | TWhile (cond,e,Ast.DoWhile) ->
-        println ctx "while true do ";
         gen_block_element ctx e;
         gen_block_element ctx e;
+        newline ctx;
         gen_loop ctx "while" cond e
         gen_loop ctx "while" cond e
     | TObjectDecl [] ->
     | TObjectDecl [] ->
         spr ctx "_hx_e()";
         spr ctx "_hx_e()";
@@ -2060,11 +2060,14 @@ let generate com =
     List.iter (transform_multireturn ctx) com.types;
     List.iter (transform_multireturn ctx) com.types;
     List.iter (generate_type 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");
         print_file (Common.find_file com "lua/_lua/_hx_bit.lua");
     end;
     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 *)
     (* Array is required, always patch it *)
     println ctx "_hx_array_mt.__index = Array.prototype";
     println ctx "_hx_array_mt.__index = Array.prototype";
     newline ctx;
     newline ctx;

+ 24 - 18
src/generators/genphp7.ml

@@ -911,20 +911,18 @@ class class_wrapper (cls) =
 				match cls.cl_init with
 				match cls.cl_init with
 					| Some _ -> true
 					| Some _ -> true
 					| None ->
 					| None ->
-						let needs = ref false in
-						PMap.iter
-							(fun _ field ->
+						List.exists
+							(fun field ->
 								(* Skip `inline var` fields *)
 								(* 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
 			Returns expression of a user-defined static __init__ method
 			@see http://old.haxe.org/doc/advanced/magic#initialization-magic
 			@see http://old.haxe.org/doc/advanced/magic#initialization-magic
@@ -3531,13 +3529,13 @@ class class_builder ctx (cls:tclass) =
 				);
 				);
 				writer#write ";\n"
 				writer#write ";\n"
 			in
 			in
-			PMap.iter
-				(fun _ field ->
+			List.iter
+				(fun field ->
 					match field.cf_kind with
 					match field.cf_kind with
 						| Method MethDynamic -> write_dynamic_method_initialization field
 						| Method MethDynamic -> write_dynamic_method_initialization field
 						| _ -> ()
 						| _ -> ()
 				)
 				)
-				cls.cl_statics;
+				cls.cl_ordered_statics;
 			(* `static var` initialization *)
 			(* `static var` initialization *)
 			let write_var_initialization field =
 			let write_var_initialization field =
 				let write_assign expr =
 				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.
 					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.
 					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
 					(match field.cf_expr with
 						| None -> ()
 						| None -> ()
 						(* There can be not-inlined blocks when compiling with `-debug` *)
 						(* There can be not-inlined blocks when compiling with `-debug` *)
@@ -3569,6 +3567,11 @@ class class_builder ctx (cls:tclass) =
 					);
 					);
 					writer#write ";\n"
 					writer#write ";\n"
 				end
 				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
 			in
 			List.iter write_var_initialization cls.cl_ordered_statics
 			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
 			let visibility = get_visibility field.cf_meta in
 			writer#write (visibility ^ " $" ^ (field_name field));
 			writer#write (visibility ^ " $" ^ (field_name field));
 			match field.cf_expr with
 			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 ->
 				| Some expr ->
 					match expr.eexpr with
 					match expr.eexpr with
 						| TConst _ ->
 						| 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)" (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))
 				| _,_ -> 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) ->
 			| 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 *)
 					(* constant optimization *)
 					Printf.sprintf "%s %% %s" (print_expr pctx e1) (print_expr pctx e2)
 					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
 		if has_feature ctx "closure_Array" || has_feature ctx "closure_String" then
 			spr ctx "from functools import partial as _hx_partial\n";
 			spr ctx "from functools import partial as _hx_partial\n";
 		spr ctx "import sys\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_imports ctx;
 		gen_resources ctx;
 		gen_resources ctx;
 		gen_types ctx;
 		gen_types ctx;

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

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

+ 5 - 0
src/macro/macroApi.ml

@@ -1951,6 +1951,11 @@ let macro_api ccom get_api =
 			);
 			);
 			vnull
 			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
 			let bb_next = if bb_breaks = [] then begin
 				(* The loop appears to be infinite, let's assume that something within it throws.
 				(* 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. *)
 				   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
 				g.g_unreachable
 			end else
 			end else
 				create_node BKNormal bb.bb_type bb.bb_pos
 				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));
 			set_syntax_edge bb_loop_pre (SEWhile(bb_loop_head,bb_loop_body,bb_next));
 			close_node g bb_loop_pre;
 			close_node g bb_loop_pre;
 			add_texpr bb_loop_pre {e with eexpr = TWhile(e1,make_block_meta bb_loop_body,NormalWhile)};
 			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;
 			add_cfg_edge bb_loop_head bb_loop_body CFGGoto;
 			close_node g bb_loop_body_next;
 			close_node g bb_loop_body_next;
 			close_node g bb_loop_head;
 			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
 			mk (TBinop (Ast.OpEq, tof, (mk (TConst (TString t)) tstring p))) tbool p
 		in
 		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 *)
 		(* generate simple typeof checks for basic types *)
 		| TTypeExpr (TClassDecl ({ cl_path = [],"String" })) -> Some (typeof "string")
 		| TTypeExpr (TClassDecl ({ cl_path = [],"String" })) -> Some (typeof "string")
 		| TTypeExpr (TAbstractDecl ({ a_path = [],"Bool" })) -> Some (typeof "boolean")
 		| 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" })) ->
 		| TTypeExpr (TClassDecl ({ cl_path = [],"Array" })) ->
 			(* generate (o instanceof Array) && o.__enum__ == null check *)
 			(* generate (o instanceof Array) && o.__enum__ == null check *)
 			let iof = Texpr.Builder.fcall (eJsSyntax()) "instanceof" [o;t] tbool p in
 			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) ->
 		| TTypeExpr (TClassDecl cls) ->
 			if cls.cl_interface then
 			if cls.cl_interface then
 				Some (Texpr.Builder.fcall (eJsBoot()) "__implements" [o;t] tbool p)
 				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
 		let cto = (match s with parser
 			| [< '(DblDot,_); s >] ->
 			| [< '(DblDot,_); s >] ->
 				(match s with parser
 				(match s with parser
+				| [< '(POpen,p1); t = parse_complex_type; '(PClose,p2) >] -> Some t
 				| [< t = parse_complex_type >] -> Some t
 				| [< t = parse_complex_type >] -> Some t
 				| [< >] -> serror())
 				| [< >] -> serror())
 			| [< >] -> None
 			| [< >] -> 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 params = List.map snd c.cl_params in
 		let ethis = mk (TConst TThis) (TInst(c,params)) p 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
 		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 _ ->
 			| Var _ ->
 				let has_default_expr = field_has_default_expr cf.cf_name in
 				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
 				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
 	| EAInt of int ref
 	| EAOther
 	| EAOther
 
 
+type enum_constructor_visibility =
+	| VUnknown
+	| VPublic of placed_access
+	| VPrivate of placed_access
+
 let build_enum_abstract ctx c a fields p =
 let build_enum_abstract ctx c a fields p =
 	let mode =
 	let mode =
 		if does_unify a.a_this ctx.t.tint then EAInt (ref 0)
 		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 ->
 	List.iter (fun field ->
 		match field.cff_kind with
 		match field.cff_kind with
 		| FVar(ct,eo) when not (List.mem_assoc AStatic field.cff_access) ->
 		| 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;
 			field.cff_meta <- (Meta.Enum,[],null_pos) :: (Meta.Impl,[],null_pos) :: field.cff_meta;
 			let ct = match ct with
 			let ct = match ct with
 				| Some _ -> ct
 				| 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;
 					a.a_from_field <- (TLazy r,cf) :: a.a_from_field;
 				| (Meta.To,_,_) :: _ ->
 				| (Meta.To,_,_) :: _ ->
 					if fctx.is_macro then error (cf.cf_name ^ ": Macro cast functions are not supported") p;
 					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... *)
 					(* 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;
 					if not (Meta.has Meta.Impl cf.cf_meta) then cf.cf_meta <- (Meta.Impl,[],null_pos) :: cf.cf_meta;
 					let resolve_m args =
 					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
 			let rec loop e = match e.eexpr with
 				| TConst _ -> Some e
 				| TConst _ -> Some e
 				| TField({eexpr = TTypeExpr _},FEnum _) -> 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
 				| 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
 					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
 				| Yes -> true
 				| YesButPrivate ->
 				| YesButPrivate ->
 					if (Meta.has Meta.PrivateAccess ctx.meta) then true
 					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
 				| No -> false
 				| Maybe ->
 				| Maybe ->
 					begin try
 					begin try

+ 4 - 2
std/cs/Boot.hx

@@ -41,8 +41,10 @@ import Reflect;
 @:dox(hide)
 @:dox(hide)
 class Boot {
 class Boot {
 	@:keep public static function init():Void {
 	@: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();
 		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) {
 	public override function request(?post:Bool) {
+		responseAsString = null;
 		responseBytes = null;
 		responseBytes = null;
 		var loader = req = new flash.net.URLLoader();
 		var loader = req = new flash.net.URLLoader();
 		loader.dataFormat = BINARY;
 		loader.dataFormat = BINARY;

+ 22 - 7
std/haxe/CallStack.hx

@@ -43,15 +43,15 @@ class CallStack {
 	static function getStack(e:js.lib.Error):Array<StackItem> {
 	static function getStack(e:js.lib.Error):Array<StackItem> {
 		if (e == null)
 		if (e == null)
 			return [];
 			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 = [];
 			var stack = [];
 			for (site in callsites) {
 			for (site in callsites) {
 				if (wrapCallSite != null)
 				if (wrapCallSite != null)
 					site = wrapCallSite(site);
 					site = wrapCallSite(site);
 				var method = null;
 				var method = null;
-				var fullName:String = site.getFunctionName();
+				var fullName = site.getFunctionName();
 				if (fullName != null) {
 				if (fullName != null) {
 					var idx = fullName.lastIndexOf(".");
 					var idx = fullName.lastIndexOf(".");
 					if (idx >= 0) {
 					if (idx >= 0) {
@@ -60,7 +60,7 @@ class CallStack {
 						method = Method(className, methodName);
 						method = Method(className, methodName);
 					}
 					}
 				}
 				}
-				var fileName:String = site.getFileName();
+				var fileName = site.getFileName();
 				var fileAddr = fileName == null ? -1 : fileName.indexOf("file:");
 				var fileAddr = fileName == null ? -1 : fileName.indexOf("file:");
 				if (wrapCallSite != null && fileAddr > 0)
 				if (wrapCallSite != null && fileAddr > 0)
 					fileName = fileName.substr(fileAddr + 6);
 					fileName = fileName.substr(fileAddr + 6);
@@ -69,13 +69,13 @@ class CallStack {
 			return stack;
 			return stack;
 		}
 		}
 		var a = makeStack(e.stack);
 		var a = makeStack(e.stack);
-		(untyped Error).prepareStackTrace = oldValue;
+		V8Error.prepareStackTrace = oldValue;
 		return a;
 		return a;
 	}
 	}
 
 
 	// support for source-map-support module
 	// support for source-map-support module
 	@:noCompletion
 	@:noCompletion
-	public static var wrapCallSite:Dynamic->Dynamic;
+	public static var wrapCallSite:V8CallSite->V8CallSite;
 	#end
 	#end
 
 
 	#if eval
 	#if eval
@@ -413,3 +413,18 @@ class CallStack {
 		#end
 		#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 {
 	@:keep public static function run() @:privateAccess {
 		#if js
 		#if js
 		var nextTick = processEvents();
 		var nextTick = processEvents();
-
+		inline function setTimeoutNextTick() {
+			if (nextTick >= 0) {
+				(untyped setTimeout)(run, nextTick);
+			}
+		}
 		#if nodejs
 		#if nodejs
-		if (nextTick < 0)
-			return;
-		(untyped setTimeout)(run, nextTick);
+		setTimeoutNextTick();
 		#else
 		#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
 		#end
 		#elseif flash
 		#elseif flash
 		flash.Lib.current.stage.addEventListener(flash.events.Event.ENTER_FRAME, function(_) processEvents());
 		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) {
 	function new(f, p) {
 		this.f = f;
 		this.f = f;
 		this.priority = p;
 		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.
 		If t is null, the event will be run at tick() time.
 	**/
 	**/
 	public function delay(t:Null<Float>) {
 	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)
 		if (f == null)
 			return;
 			return;
 		f = null;
 		f = null;
-		nextRun = -1;
+		nextRun = Math.NEGATIVE_INFINITY;
 		if (prev == null)
 		if (prev == null)
 			@:privateAccess MainLoop.pending = next;
 			@:privateAccess MainLoop.pending = next;
 		else
 		else
@@ -169,7 +169,7 @@ class MainLoop {
 		while (e != null) {
 		while (e != null) {
 			var next = e.next;
 			var next = e.next;
 			var wt = e.nextRun - now;
 			var wt = e.nextRun - now;
-			if (e.nextRun < 0 || wt <= 0) {
+			if (wt <= 0) {
 				wait = 0;
 				wait = 0;
 				e.call();
 				e.call();
 			} else if (wait > wt)
 			} else if (wait > wt)

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

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

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

@@ -585,6 +585,24 @@ class Context {
 		load("register_module_dependency", 2)(modulePath, externFile);
 		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
 	@:deprecated
 	public static function registerModuleReuseCall(modulePath:String, macroCall:String) {
 	public static function registerModuleReuseCall(modulePath:String, macroCall:String) {
 		throw "This method is no longer supported. See https://github.com/HaxeFoundation/haxe/issues/5746";
 		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.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(" ") + " " : "")
 			+ (field.access != null && field.access.length > 0 ? field.access.map(printAccess).join(" ") + " " : "")
 			+ switch (field.kind) {
 			+ 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 FProp(get, set, t, eo): 'var ${field.name}($get, $set)' + opt(t, printComplexType, " : ") + opt(eo, printExpr, " = ");
 				case FFun(func): 'function ${field.name}' + printFunction(func);
 				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) {
 	public function make(lengths, pos, nlengths, maxbits) {
+		if (nlengths == 1) {
+			return NeedBit(Found(0), Found(0));
+		}
 		var counts = new Array();
 		var counts = new Array();
 		var tmp = new Array();
 		var tmp = new Array();
 		if (maxbits > 32)
 		if (maxbits > 32)

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

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

+ 2 - 0
std/java/Init.hx

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

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

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

+ 1 - 1
std/js/Lib.hx

@@ -135,7 +135,7 @@ class Lib {
 		Generate next unique id
 		Generate next unique id
 	**/
 	**/
 	@:allow(haxe.ds.ObjectMap.assignId)
 	@: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));
 		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:Uint32Array, ?offset:Int):Void {})
 	@:overload(function(array:Float32Array, ?offset:Int):Void {})
 	@:overload(function(array:Float32Array, ?offset:Int):Void {})
 	@:overload(function(array:Float64Array, ?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.
 		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:Uint32Array, ?offset:Int):Void {})
 	@:overload(function(array:Float32Array, ?offset:Int):Void {})
 	@:overload(function(array:Float32Array, ?offset:Int):Void {})
 	@:overload(function(array:Float64Array, ?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.
 		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());
 		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
 		A 32 bit clamp function for numbers
 	**/
 	**/
 	public inline static function clampInt32(x:Float) {
 	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.
 		function must be a Lua function without upvalues.
 	**/
 	**/
 	public static function dump(d:Dynamic):Dynamic;
 	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 {
 @:multiReturn extern class StringFind {

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

@@ -1,11 +1,16 @@
 -- require this for lua 5.1
 -- require this for lua 5.1
 pcall(require, 'bit')
 pcall(require, 'bit')
 if bit then
 if bit then
-  _hx_bit = bit
+  _hx_bit_raw = bit
+  _hx_bit = setmetatable({}, { __index = _hx_bit_raw });
 else
 else
-  local _hx_bit_raw = _G.require('bit32')
+  _hx_bit_raw = _G.require('bit32')
   _hx_bit = setmetatable({}, { __index = _hx_bit_raw });
   _hx_bit = setmetatable({}, { __index = _hx_bit_raw });
   -- lua 5.2 weirdness
   -- lua 5.2 weirdness
   _hx_bit.bnot = function(...) return _hx_bit_clamp(_hx_bit_raw.bnot(...)) end;
   _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;
   _hx_bit.bxor = function(...) return _hx_bit_clamp(_hx_bit_raw.bxor(...)) end;
 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
-  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;
 import lua.lib.lrexlib.Rex;
 import lua.lib.lrexlib.Rex;
 // Note - lrexlib gives ascii-based offsets.  Use native string tools.
 // Note - lrexlib gives ascii-based offsets.  Use native string tools.
-import lua.NativeStringTools;
+import lua.NativeStringTools.*;
 
 
 @:coreApi
 @:coreApi
 class EReg {
 class EReg {
@@ -61,9 +61,13 @@ class EReg {
 	}
 	}
 
 
 	public function match(s:String):Bool {
 	public function match(s:String):Bool {
+		return matchFromByte(s, 1);
+	}
+
+	inline function matchFromByte(s:String, offset:Int):Bool {
 		if (s == null)
 		if (s == null)
 			return false;
 			return false;
-		this.m = lua.TableTools.pack(r.exec(s));
+		this.m = lua.TableTools.pack(r.exec(s, offset));
 		this.s = s;
 		this.s = s;
 		return m[1] != null;
 		return m[1] != null;
 	}
 	}
@@ -72,13 +76,13 @@ class EReg {
 		if (m[1] == null || n < 0)
 		if (m[1] == null || n < 0)
 			throw "EReg::matched";
 			throw "EReg::matched";
 		else if (n == 0) {
 		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;
 			return k;
 		} else if (Std.is(m[3], lua.Table)) {
 		} else if (Std.is(m[3], lua.Table)) {
 			var mn = 2 * (n - 1);
 			var mn = 2 * (n - 1);
 			if (Std.is(untyped m[3][mn + 1], Bool))
 			if (Std.is(untyped m[3][mn + 1], Bool))
 				return null;
 				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 {
 		} else {
 			throw "EReg:matched";
 			throw "EReg:matched";
 		}
 		}
@@ -87,13 +91,13 @@ class EReg {
 	public function matchedLeft():String {
 	public function matchedLeft():String {
 		if (m[1] == null)
 		if (m[1] == null)
 			throw "No string matched";
 			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 {
 	public function matchedRight():String {
 		if (m[1] == null)
 		if (m[1] == null)
 			throw "No string matched";
 			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} {
 	public function matchedPos():{pos:Int, len:Int} {
@@ -145,6 +149,33 @@ class EReg {
 	}
 	}
 
 
 	public function map(s:String, f:EReg->String):String {
 	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 offset = 0;
 		var buf = new StringBuf();
 		var buf = new StringBuf();
 		do {
 		do {

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

@@ -23,7 +23,7 @@
 import lua.Boot;
 import lua.Boot;
 import lua.Io;
 import lua.Io;
 import lua.Lua;
 import lua.Lua;
-import lua.Os;
+import lua.lib.luv.Os;
 import lua.lib.luv.Misc;
 import lua.lib.luv.Misc;
 import sys.io.FileInput;
 import sys.io.FileInput;
 import sys.io.FileOutput;
 import sys.io.FileOutput;
@@ -109,11 +109,11 @@ class Sys {
 		Misc.chdir(s);
 		Misc.chdir(s);
 
 
 	public inline static function getEnv(s:String):String {
 	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 {
 	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 {
 	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 {
 extern class Misc {
 	public static function chdir(path:String):Bool;
 	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 cpu_info():Table<Int, CpuInfo>;
 
 
 	public static function cwd():String;
 	public static function cwd():String;
@@ -38,9 +35,6 @@ extern class Misc {
 	public static function get_free_memory():Int;
 	public static function get_free_memory():Int;
 	public static function getpid():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?
 	// TODO Windows only?
 	public static function getuid():Int;
 	public static function getuid():Int;
 	public static function setuid(from:Int, to:Int):String;
 	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 {
 	static public inline function unsafeOrd(s:NativeString):Int {
 		var code = Global.ord(s[0]);
 		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;
 package php;
 
 
+/**
+	@see https://www.php.net/manual/en/class.iteratoraggregate.php
+**/
 @:native('IteratorAggregate')
 @: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 ;)
 		This method is not public to not induce Haxe users to use it ;)
 		Use iterator() instead.
 		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 {
 	inline function __toObject():T {
 		return Boot.createAnon(this);
 		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.*;
+import php.ArrayIterator as NativeArrayIterator;
 
 
 using php.Global;
 using php.Global;
 
 
 @:coreApi
 @: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;
 	public var length(default, null):Int;
 
 
 	var arr:NativeIndexedArray<T>;
 	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));
 		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);
 		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> {
 	static function wrap<T>(arr:NativeIndexedArray<T>):Array<T> {
 		var a = new Array();
 		var a = new Array();
 		a.arr = arr;
 		a.arr = arr;
@@ -272,4 +283,4 @@ private extern interface ArrayAccess<K, V> {
 	private function offsetGet(offset:K):V;
 	private function offsetGet(offset:K):V;
 	private function offsetSet(offset:K, value:V):Void;
 	private function offsetSet(offset:K, value:V):Void;
 	private function offsetUnset(offset:K):Void;
 	private function offsetUnset(offset:K):Void;
-}
+}

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

@@ -37,11 +37,16 @@ import php.*;
 		this.pattern = r;
 		this.pattern = r;
 		options = Global.str_replace('g', '', opt);
 		options = Global.str_replace('g', '', opt);
 		global = options != opt;
 		global = options != opt;
+		options = Global.str_replace('u', '', options);
 		this.re = '"' + Global.str_replace('"', '\\"', r) + '"' + options;
 		this.re = '"' + Global.str_replace('"', '\\"', r) + '"' + options;
 	}
 	}
 
 
 	public function match(s:String):Bool {
 	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) {
 		if (p == false) {
 			handlePregError();
 			handlePregError();
 			p = Global.preg_match(re, s, matches, Const.PREG_OFFSET_CAPTURE);
 			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) {
 		} else if (e == Const.PREG_JIT_STACKLIMIT_ERROR) {
 			throw 'failed due to limited JIT stack space';
 			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';
 		// 	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';
 		// 	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 {
 	public function matched(n:Int):String {
 		if (matches == null || n < 0)
 		if (matches == null || n < 0)
 			throw "EReg::matched";
 			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
 		// that have not been matched and invalid groups
 		if (n >= Global.count(matches))
 		if (n >= Global.count(matches))
 			return null;
 			return null;
@@ -143,30 +148,25 @@ import php.*;
 	}
 	}
 
 
 	public function map(s:String, f:EReg->String):String {
 	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 {
 		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 {
 			} 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 {
 	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();
 		return Global.array_keys(data).iterator();
 	}
 	}
 
 
+	@:ifFeature("dynamic_read.iterator", "anon_optional_read.iterator", "anon_read.iterator")
 	public inline function iterator():Iterator<T> {
 	public inline function iterator():Iterator<T> {
 		return Global.array_values(data).iterator();
 		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);
 		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();
 		return _keys.iterator();
 	}
 	}
 
 
+	@:ifFeature("dynamic_read.iterator", "anon_optional_read.iterator", "anon_read.iterator")
 	public inline function iterator():Iterator<V> {
 	public inline function iterator():Iterator<V> {
 		return _values.iterator();
 		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);
 		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();
 		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> {
 	public inline function iterator():Iterator<T> {
 		return data.iterator();
 		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);
 		return new haxe.iterators.MapKeyValueIterator(this);
 	}
 	}
 
 

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

@@ -26,70 +26,70 @@ import php.*;
 
 
 @:native('PDO')
 @:native('PDO')
 extern class 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 new(dns:String, ?username:String, ?password:String, ?options:NativeArray):Void;
 	function beginTransaction():Bool;
 	function beginTransaction():Bool;

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

@@ -26,9 +26,9 @@ import haxe.extern.Rest;
 
 
 @:native('ReflectionClass')
 @:native('ReflectionClass')
 extern class ReflectionClass implements Reflector {
 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;
 	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')
 @:native('ReflectionMethod')
 extern class ReflectionMethod extends ReflectionFunctionAbstract {
 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 var class : String;
 	public static function export(className:String, name:String, ?returnValue:Bool):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')
 @:native('ReflectionProperty')
 extern class ReflectionProperty implements Reflector {
 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;
 	var name:String;
 
 

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

@@ -142,12 +142,34 @@ class HxOverrides {
 
 
 	@:ifFeature("binop_%")
 	@:ifFeature("binop_%")
 	static public function modf(a:Float, b:Float) {
 	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_%")
 	@:ifFeature("binop_%")
 	static public function mod(a:Int, b:Int) {
 	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")
 	@: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 : Class<C> should be { member : Int }
 Main.hx:16: characters 9-29 : The field member is not public
 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
 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'

部分文件因文件數量過多而無法顯示