Parcourir la source

Module-level static declarations (closes #7452) (#8460)

* module-level static declarations (squashed)

* add module statics to the test for #9367

* fix #9367 for lowercase access
Dan Korostelev il y a 5 ans
Parent
commit
01cfe550b9
80 fichiers modifiés avec 1186 ajouts et 263 suppressions
  1. 3 3
      src/codegen/gencommon/gencommon.ml
  2. 1 1
      src/codegen/gencommon/overloadingConstructor.ml
  3. 3 3
      src/codegen/gencommon/reflectionCFs.ml
  4. 5 1
      src/context/common.ml
  5. 1 1
      src/context/display/displayToplevel.ml
  6. 21 10
      src/context/display/documentSymbols.ml
  7. 6 1
      src/context/display/syntaxExplorer.ml
  8. 1 0
      src/context/typecore.ml
  9. 1 0
      src/core/ast.ml
  10. 18 0
      src/core/display/completionItem.ml
  11. 1 0
      src/core/json/genjson.ml
  12. 1 0
      src/core/tFunctions.ml
  13. 2 0
      src/core/tPrinting.ml
  14. 2 0
      src/core/tType.ml
  15. 13 9
      src/filters/renameVars.ml
  16. 8 0
      src/generators/gencs.ml
  17. 8 0
      src/generators/genjava.ml
  18. 67 10
      src/generators/genjs.ml
  19. 13 16
      src/generators/genjvm.ml
  20. 18 11
      src/macro/macroApi.ml
  21. 59 1
      src/syntax/grammar.mly
  22. 20 0
      src/syntax/parser.ml
  23. 6 2
      src/typing/fields.ml
  24. 30 15
      src/typing/finalization.ml
  25. 2 0
      src/typing/generic.ml
  26. 22 11
      src/typing/macroContext.ml
  27. 1 1
      src/typing/typeload.ml
  28. 1 1
      src/typing/typeloadFields.ml
  29. 101 11
      src/typing/typeloadModule.ml
  30. 3 1
      src/typing/typeloadParse.ml
  31. 9 0
      src/typing/typer.ml
  32. 29 12
      src/typing/typerDotPath.ml
  33. 5 0
      std/haxe/macro/Expr.hx
  34. 8 0
      std/haxe/macro/Printer.hx
  35. 2 4
      std/haxe/macro/Type.hx
  36. 1 1
      tests/misc/projects/Issue7968/compile-fail.hxml.stderr
  37. 4 2
      tests/misc/resolution/projects/Issue9367/Main.hx
  38. 11 0
      tests/misc/resolution/projects/Issue9367/pack/Mod3.hx
  39. 6 1
      tests/misc/resolution/projects/Issue9367/pack/UsageImport.hx
  40. 5 1
      tests/misc/resolution/projects/Issue9367/pack/UsageNoImport.hx
  41. 1 0
      tests/misc/resolution/projects/modulestatics/.gitignore
  42. 2 0
      tests/misc/resolution/projects/modulestatics/Duplicate.hx
  43. 58 0
      tests/misc/resolution/projects/modulestatics/Macro.hx
  44. 105 0
      tests/misc/resolution/projects/modulestatics/Main.hx
  45. 7 0
      tests/misc/resolution/projects/modulestatics/ModWithPrivate.hx
  46. 17 0
      tests/misc/resolution/projects/modulestatics/ModWithStaticAndClassStatic.hx
  47. 17 0
      tests/misc/resolution/projects/modulestatics/ModWithStaticAndClassStatic2.hx
  48. 5 0
      tests/misc/resolution/projects/modulestatics/ModuleStaticWithNoMainClass.hx
  49. 5 0
      tests/misc/resolution/projects/modulestatics/ModuleStaticWithNoMainClassInPack.hx
  50. 5 0
      tests/misc/resolution/projects/modulestatics/PrivateAccess.hx
  51. 7 0
      tests/misc/resolution/projects/modulestatics/PrivateImport.hx
  52. 7 0
      tests/misc/resolution/projects/modulestatics/RootMod1.hx
  53. 7 0
      tests/misc/resolution/projects/modulestatics/RootMod2.hx
  54. 7 0
      tests/misc/resolution/projects/modulestatics/RootMod3.hx
  55. 7 0
      tests/misc/resolution/projects/modulestatics/RootMod4.hx
  56. 13 0
      tests/misc/resolution/projects/modulestatics/Wildcard.hx
  57. 2 0
      tests/misc/resolution/projects/modulestatics/compile-duplicate-fail.hxml
  58. 2 0
      tests/misc/resolution/projects/modulestatics/compile-duplicate-fail.hxml.stderr
  59. 2 0
      tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClass-fail.hxml
  60. 1 0
      tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClass-fail.hxml.stderr
  61. 2 0
      tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClassInPack-fail.hxml
  62. 1 0
      tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClassInPack-fail.hxml.stderr
  63. 2 0
      tests/misc/resolution/projects/modulestatics/compile-private-fail.hxml
  64. 1 0
      tests/misc/resolution/projects/modulestatics/compile-private-fail.hxml.stderr
  65. 2 0
      tests/misc/resolution/projects/modulestatics/compile-private2-fail.hxml
  66. 1 0
      tests/misc/resolution/projects/modulestatics/compile-private2-fail.hxml.stderr
  67. 8 0
      tests/misc/resolution/projects/modulestatics/compile.hxml
  68. 11 0
      tests/misc/resolution/projects/modulestatics/pack/InnerMod.hx
  69. 9 0
      tests/misc/resolution/projects/modulestatics/pack/Mod1.hx
  70. 11 0
      tests/misc/resolution/projects/modulestatics/pack/Mod2.hx
  71. 9 0
      tests/misc/resolution/projects/modulestatics/pack/inner/InnerMod.hx
  72. 31 0
      tests/misc/resolution/projects/modulestatics/pack/inner/Test.hx
  73. 9 0
      tests/misc/resolution/projects/modulestatics/pack/shadow/Test.hx
  74. 1 0
      tests/misc/resolution/projects/spec/ModWithPrivate.hx
  75. 7 0
      tests/misc/resolution/projects/spec/PrivateImport.hx
  76. 2 0
      tests/misc/resolution/projects/spec/compile-private-fail.hxml
  77. 2 0
      tests/misc/resolution/projects/spec/compile-private-fail.hxml.stderr
  78. 132 133
      tests/unit/src/unit/TestMain.hx
  79. 109 0
      tests/unit/src/unit/TestModuleStatics.hx
  80. 41 0
      tests/unit/src/unit/TestModuleStaticsMacro.hx

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

@@ -620,11 +620,11 @@ let new_ctx con =
 		gadd_type = (fun md should_filter ->
 			if should_filter then begin
 				gen.gtypes_list <- md :: gen.gtypes_list;
-				gen.gmodules <- { m_id = alloc_mid(); m_path = (t_path md); m_types = [md]; m_extra = module_extra "" "" 0. MFake [] } :: gen.gmodules;
+				gen.gmodules <- { m_id = alloc_mid(); m_path = (t_path md); m_types = [md]; m_statics = None; m_extra = module_extra "" "" 0. MFake [] } :: gen.gmodules;
 				Hashtbl.add gen.gtypes (t_path md) md;
 			end else gen.gafter_filters_ended <- (fun () ->
 				gen.gtypes_list <- md :: gen.gtypes_list;
-				gen.gmodules <- { m_id = alloc_mid(); m_path = (t_path md); m_types = [md]; m_extra = module_extra "" "" 0. MFake [] } :: gen.gmodules;
+				gen.gmodules <- { m_id = alloc_mid(); m_path = (t_path md); m_types = [md]; m_statics = None; m_extra = module_extra "" "" 0. MFake [] } :: gen.gmodules;
 				Hashtbl.add gen.gtypes (t_path md) md;
 			) :: gen.gafter_filters_ended;
 		);
@@ -685,7 +685,7 @@ let reorder_modules gen =
 	Hashtbl.iter (fun md_path md ->
 		if not (Hashtbl.mem processed md_path) then begin
 			Hashtbl.add processed md_path true;
-			gen.gmodules <- { m_id = alloc_mid(); m_path = md_path; m_types = List.rev ( Hashtbl.find_all modules md_path ); m_extra = (t_infos md).mt_module.m_extra } :: gen.gmodules
+			gen.gmodules <- { m_id = alloc_mid(); m_path = md_path; m_types = List.rev ( Hashtbl.find_all modules md_path ); m_statics = None; m_extra = (t_infos md).mt_module.m_extra } :: gen.gmodules
 		end
 	) modules
 

+ 1 - 1
src/codegen/gencommon/overloadingConstructor.ml

@@ -284,7 +284,7 @@ let ensure_super_is_first com cf =
 
 let init com (empty_ctor_type : t) (empty_ctor_expr : texpr) (follow_type : t -> t) =
 	let basic = com.basic in
-	let should_change cl = not cl.cl_interface && (not cl.cl_extern || is_hxgen (TClassDecl cl)) && (match cl.cl_kind with KAbstractImpl _ -> false | _ -> true) in
+	let should_change cl = not cl.cl_interface && (not cl.cl_extern || is_hxgen (TClassDecl cl)) && (match cl.cl_kind with KAbstractImpl _ | KModuleStatics _ -> false | _ -> true) in
 	let msize = List.length com.types in
 	let processed, empty_ctors = Hashtbl.create msize, Hashtbl.create msize in
 

+ 3 - 3
src/codegen/gencommon/reflectionCFs.ml

@@ -1482,8 +1482,8 @@ struct
 				match md with
 				| TClassDecl ({ cl_interface = true } as cl) when cl.cl_path <> baseclass.cl_path && cl.cl_path <> baseinterface.cl_path && cl.cl_path <> basedynamic.cl_path ->
 					cl.cl_implements <- (baseinterface, []) :: cl.cl_implements
-				| TClassDecl ({ cl_kind = KAbstractImpl _ }) ->
-					(* don't add any base classes to abstract implementations *)
+				| TClassDecl ({ cl_kind = KAbstractImpl _ | KModuleStatics _ }) ->
+					(* don't add any base classes to abstract implementations and module statics *)
 					()
 				| TClassDecl ({ cl_super = None } as cl) when cl.cl_path <> baseclass.cl_path && cl.cl_path <> baseinterface.cl_path && cl.cl_path <> basedynamic.cl_path ->
 					cl.cl_super <- Some (baseclass,[])
@@ -1518,7 +1518,7 @@ let has_field_override cl name =
 let configure ctx baseinterface ~slow_invoke =
 	let run md =
 		(match md with
-		| TClassDecl ({ cl_extern = false } as cl) when is_hxgen md && ( not cl.cl_interface || cl.cl_path = baseinterface.cl_path ) && (match cl.cl_kind with KAbstractImpl _ -> false | _ -> true) ->
+		| TClassDecl ({ cl_extern = false } as cl) when is_hxgen md && ( not cl.cl_interface || cl.cl_path = baseinterface.cl_path ) && (match cl.cl_kind with KAbstractImpl _ | KModuleStatics _ -> false | _ -> true) ->
 			if is_some cl.cl_super then begin
 				ignore (has_field_override cl (mk_internal_name "hx" "setField"));
 				ignore (has_field_override cl (mk_internal_name "hx" "setField_f"));

+ 5 - 1
src/context/common.ml

@@ -1027,7 +1027,11 @@ let is_legacy_completion com = match com.json_out with
 let get_entry_point com =
 	Option.map (fun path ->
 		let m = List.find (fun m -> m.m_path = path) com.modules in
-		let c = ExtList.List.find_map (fun t -> match t with TClassDecl c when c.cl_path = path -> Some c | _ -> None) m.m_types in
+		let c = 
+			match m.m_statics with
+			| Some c when (PMap.mem "main" c.cl_statics) -> c
+			| _ -> ExtList.List.find_map (fun t -> match t with TClassDecl c when c.cl_path = path -> Some c | _ -> None) m.m_types
+		in
 		let e = Option.get com.main in (* must be present at this point *)
 		(snd path, c, e)
 	) com.main_class

+ 1 - 1
src/context/display/displayToplevel.ml

@@ -213,7 +213,7 @@ let collect ctx tk with_type sort =
 
 	let add_type mt =
 		match mt with
-		| TClassDecl {cl_kind = KAbstractImpl _} -> ()
+		| TClassDecl {cl_kind = KAbstractImpl _ | KModuleStatics _} -> ()
 		| _ ->
 			let path = (t_infos mt).mt_path in
 			let mname = snd (t_infos mt).mt_module.m_path in

+ 21 - 10
src/context/display/documentSymbols.ml

@@ -42,21 +42,21 @@ let collect_module_symbols with_locals (pack,decls) =
 		expr_opt parent f.f_expr
 	in
 	let is_deprecated meta = Meta.has Meta.Deprecated meta in
-	let field parent parent_kind cff =
-		let field_parent = parent ^ "." ^ (fst cff.cff_name) in
-		let add_field kind = add (fst cff.cff_name) kind cff.cff_pos parent (is_deprecated cff.cff_meta) in
-		match cff.cff_kind with
+	let field' parent parent_kind cff_name cff_kind cff_access cff_pos cff_meta =
+		let field_parent = parent ^ "." ^ (fst cff_name) in
+		let add_field kind = add (fst cff_name) kind cff_pos parent (is_deprecated cff_meta) in
+		match cff_kind with
 		| FVar(_,eo) ->
 			add_field (
-				if parent_kind = EnumAbstract && not (List.mem_assoc AStatic cff.cff_access) then EnumMember
-				else if (List.mem_assoc AInline cff.cff_access) then Constant
+				if parent_kind = EnumAbstract && not (List.mem_assoc AStatic cff_access) then EnumMember
+				else if (List.mem_assoc AInline cff_access) then Constant
 				else Field
 			);
 			if with_locals then expr_opt field_parent eo
 		| FFun f ->
 			add_field (
-				if fst cff.cff_name = "new" then Constructor
-				else if ((parent_kind = EnumAbstract or parent_kind = Abstract) && Meta.has_one_of [Meta.Op; Meta.ArrayAccess; Meta.Resolve] cff.cff_meta) then Operator
+				if fst cff_name = "new" then Constructor
+				else if ((parent_kind = EnumAbstract or parent_kind = Abstract) && Meta.has_one_of [Meta.Op; Meta.ArrayAccess; Meta.Resolve] cff_meta) then Operator
 				else Method
 			);
 			if with_locals then func field_parent f
@@ -64,13 +64,20 @@ let collect_module_symbols with_locals (pack,decls) =
 			add_field Property;
 			if with_locals then expr_opt field_parent eo
 	in
+	let field parent parent_kind cff =
+		field' parent parent_kind cff.cff_name cff.cff_kind cff.cff_access cff.cff_pos cff.cff_meta
+	in
 	List.iter (fun (td,p) ->
-		let add_type d kind =
-			let string_of_path l = String.concat "." l in
+		let get_decl_path d =
 			let module_name = Path.module_name_of_file p.pfile in
 			let type_name = fst d.d_name in
 			let is_primary_type = type_name = module_name in
 			let type_path = if is_primary_type then pack else pack @ [module_name] in
+			type_path, type_name
+		in
+		let string_of_path l = String.concat "." l in
+		let add_type d kind =
+			let type_path, type_name = get_decl_path d in
 			add type_name kind p (string_of_path type_path) (is_deprecated d.d_meta);
 			string_of_path (type_path @ [type_name])
 		in
@@ -98,6 +105,10 @@ let collect_module_symbols with_locals (pack,decls) =
 			let kind = if Meta.has Meta.Enum d.d_meta then EnumAbstract else Abstract in
 			let parent = add_type d kind in
 			List.iter (field parent kind) d.d_data
+		| EStatic d ->
+			let path, name = get_decl_path d in
+			let dotpath = string_of_path (path @ [name]) in
+			field' dotpath Class d.d_name d.d_data d.d_flags p d.d_meta
 	) decls;
 	l
 

+ 6 - 1
src/context/display/syntaxExplorer.ml

@@ -97,7 +97,9 @@ let find_in_syntax symbols (pack,decls) =
 		expr_opt f.f_expr
 	and field cff =
 		check KClassField (fst cff.cff_name);
-		match cff.cff_kind with
+		field_kind cff.cff_kind
+	and field_kind cff_kind =
+		match cff_kind with
 		| FVar(tho,eo) ->
 			Option.may type_hint tho;
 			expr_opt eo
@@ -152,6 +154,9 @@ let find_in_syntax symbols (pack,decls) =
 				| AbFrom th | AbTo th | AbOver th -> type_hint th
 				| _ -> ()
 			) d.d_flags;
+		| EStatic d ->
+			check KModuleType (fst d.d_name);
+			field_kind d.d_data
 	) decls
 
 let explore_uncached_modules tctx cs symbols =

+ 1 - 0
src/context/typecore.ml

@@ -359,6 +359,7 @@ let create_fake_module ctx file =
 			m_id = alloc_mid();
 			m_path = (["$DEP"],file);
 			m_types = [];
+			m_statics = None;
 			m_extra = module_extra file (Define.get_signature ctx.com.defines) (file_time file) MFake [];
 		} in
 		Hashtbl.add fake_modules key mdep;

+ 1 - 0
src/core/ast.ml

@@ -321,6 +321,7 @@ type type_def =
 	| EEnum of (enum_flag, enum_constructor list) definition
 	| ETypedef of (enum_flag, type_hint) definition
 	| EAbstract of (abstract_flag, class_field list) definition
+	| EStatic of (placed_access, class_field_kind) definition
 	| EImport of import
 	| EUsing of placed_name list
 

+ 18 - 0
src/core/display/completionItem.ml

@@ -20,6 +20,7 @@ module CompletionModuleKind = struct
 		| TypeAlias
 		| Struct
 		| TypeParameter
+		| Static
 
 	let to_int = function
 		| Class -> 0
@@ -30,6 +31,7 @@ module CompletionModuleKind = struct
 		| TypeAlias -> 5
 		| Struct -> 6
 		| TypeParameter -> 7
+		| Static -> 8
 end
 
 module ImportStatus = struct
@@ -154,6 +156,22 @@ module CompletionModuleType = struct
 				has_constructor = ctor;
 				source = Syntax td;
 			}
+		| EStatic d ->
+			{
+				pack = pack;
+				name = fst d.d_name;
+				module_name = module_name;
+				pos = p;
+				is_private = List.exists (fun (f,_) -> f = APrivate) d.d_flags;
+				params = d.d_params;
+				meta = d.d_meta;
+				doc = d.d_doc;
+				is_extern = List.exists (fun (f,_) -> f = AExtern) d.d_flags;
+				is_final = true;
+				kind = Static;
+				has_constructor = No;
+				source = Syntax td;
+			}
 		| EImport _ | EUsing _ ->
 			raise Exit
 

+ 1 - 0
src/core/json/genjson.ml

@@ -584,6 +584,7 @@ let generate_class ctx c =
 		| KMacroType -> "KMacroType",None
 		| KGenericBuild _ -> "KGenericBuild",None
 		| KAbstractImpl a -> "KAbstractImpl",Some (abstract_ref ctx a)
+		| KModuleStatics m -> "KModuleStatics",Some (generate_module_path m.m_path)
 		in
 		generate_adt ctx (Some (["haxe";"macro"],"ClassKind")) ctor args
 	in

+ 1 - 0
src/core/tFunctions.ml

@@ -156,6 +156,7 @@ let null_module = {
 		m_id = alloc_mid();
 		m_path = [] , "";
 		m_types = [];
+		m_statics = None;
 		m_extra = module_extra "" "" 0. MFake [];
 	}
 

+ 2 - 0
src/core/tPrinting.ml

@@ -429,6 +429,8 @@ let s_class_kind = function
 		"KGenericBuild"
 	| KAbstractImpl a ->
 		Printf.sprintf "KAbstractImpl %s" (s_type_path a.a_path)
+	| KModuleStatics m ->
+		Printf.sprintf "KModuleStatics %s" (s_type_path m.m_path)
 
 module Printer = struct
 

+ 2 - 0
src/core/tType.ml

@@ -185,6 +185,7 @@ and tclass_kind =
 	| KMacroType
 	| KGenericBuild of class_field list
 	| KAbstractImpl of tabstract
+	| KModuleStatics of module_def
 
 and metadata = Ast.metadata
 
@@ -312,6 +313,7 @@ and module_def = {
 	m_id : int;
 	m_path : path;
 	mutable m_types : module_type list;
+	mutable m_statics : tclass option;
 	m_extra : module_def_extra;
 }
 

+ 13 - 9
src/filters/renameVars.ml

@@ -29,15 +29,19 @@ let reserve_all_types ri com path_to_name =
 	List.iter (fun mt ->
 		let tinfos = t_infos mt in
 		let native_name = try fst (TypeloadCheck.get_native_name tinfos.mt_meta) with Not_found -> path_to_name tinfos.mt_path in
-		if native_name = "" then
-			match mt with
-			| TClassDecl c ->
-				List.iter (fun cf ->
-					let native_name = try fst (TypeloadCheck.get_native_name cf.cf_meta) with Not_found -> cf.cf_name in
-					reserve_init ri native_name
-				) c.cl_ordered_statics;
-			| _ -> ()
-		else
+		match mt with
+		| TClassDecl c when native_name = "" ->
+			List.iter (fun cf ->
+				let native_name = try fst (TypeloadCheck.get_native_name cf.cf_meta) with Not_found -> cf.cf_name in
+				reserve_init ri native_name
+			) c.cl_ordered_statics
+		| TClassDecl { cl_kind = KModuleStatics m; cl_ordered_statics = fl } ->
+			let prefix = Path.flat_path m.m_path ^ "_" in
+			List.iter (fun cf ->
+				let name = try fst (TypeloadCheck.get_native_name cf.cf_meta) with Not_found -> prefix ^ cf.cf_name in
+				reserve_init ri name
+			) fl
+		| _ ->
 			reserve_init ri native_name
 	) com.types
 

+ 8 - 0
src/generators/gencs.ml

@@ -3484,6 +3484,14 @@ let generate con =
 			let old_dir = Sys.getcwd() in
 			Sys.chdir gen.gcon.file;
 			let cmd = "haxelib run hxcs hxcs_build.txt --haxe-version " ^ (string_of_int gen.gcon.version) ^ " --feature-level 1" in
+			let cmd =
+				match gen.gentry_point with
+				| Some (name,_,_) ->
+					let name = if gen.gcon.debug then name ^ "-Debug" else name in
+					cmd ^ " --out " ^ gen.gcon.file ^ "/bin/" ^ name
+				| _ ->
+					cmd
+			in
 			print_endline cmd;
 			if gen.gcon.run_command cmd <> 0 then failwith "Build failed";
 			Sys.chdir old_dir;

+ 8 - 0
src/generators/genjava.ml

@@ -2667,6 +2667,14 @@ let generate con =
 		let old_dir = Sys.getcwd() in
 		Sys.chdir gen.gcon.file;
 		let cmd = "haxelib run hxjava hxjava_build.txt --haxe-version " ^ (string_of_int gen.gcon.version) ^ " --feature-level 1" in
+		let cmd =
+			match gen.gentry_point with
+			| Some (name,_,_) ->
+				let name = if gen.gcon.debug then name ^ "-Debug" else name in
+				cmd ^ " --out " ^ gen.gcon.file ^ "/" ^ name
+			| _ ->
+				cmd
+		in
 		print_endline cmd;
 		if gen.gcon.run_command cmd <> 0 then failwith "Build failed";
 		Sys.chdir old_dir;

+ 67 - 10
src/generators/genjs.ml

@@ -151,6 +151,18 @@ let static_field ctx c f =
 	| s ->
 		field s
 
+let module_static m f =
+	try
+		fst (TypeloadCheck.get_native_name f.cf_meta)
+	with Not_found ->
+		Path.flat_path m.m_path ^ "_" ^ f.cf_name
+
+let module_static_expose_path mpath f =
+	try
+		fst (TypeloadCheck.get_native_name f.cf_meta)
+	with Not_found ->
+		(dot_path mpath) ^ "." ^ f.cf_name
+
 let has_feature ctx = Common.has_feature ctx.com
 let add_feature ctx = Common.add_feature ctx.com
 
@@ -601,6 +613,8 @@ and gen_expr ctx e =
 		spr ctx f.cf_name;
 	| TField (x, (FInstance(_,_,f) | FStatic(_,f) | FAnon(f))) when Meta.has Meta.SelfCall f.cf_meta ->
 		gen_value ctx x;
+	| TField (_,FStatic ({ cl_kind = KModuleStatics m },f)) ->
+		spr ctx (module_static m f)
 	| TField (x,f) ->
 		let rec skip e = match e.eexpr with
 			| TCast(e1,None) | TMeta(_,e1) -> skip e1
@@ -1085,6 +1099,31 @@ let path_to_brackets path =
 	let parts = ExtString.String.nsplit path "." in
 	"[\"" ^ (String.concat "\"][\"" parts) ^ "\"]"
 
+let gen_module_statics ctx m c fl =
+	List.iter (fun f ->
+		let name = module_static m f in
+		match f.cf_expr with
+		| None when not (is_physical_field f) ->
+			()
+		| None ->
+			print ctx "var %s = null" name;
+			newline ctx
+		| Some e ->
+			match e.eexpr with
+			| TFunction fn ->
+				ctx.id_counter <- 0;
+				print ctx "function %s" name;
+				gen_function ~keyword:"" ctx fn e.epos;
+				ctx.separator <- false;
+				newline ctx;
+				process_expose f.cf_meta (fun () -> module_static_expose_path m.m_path f) (fun s ->
+					print ctx "$hx_exports%s = %s" (path_to_brackets s) name;
+					newline ctx
+				)
+			| _ -> 
+				ctx.statics <- (c,f,e) :: ctx.statics
+	) fl
+
 let gen_class_static_field ctx c cl_path f =
 	match f.cf_expr with
 	| None | Some { eexpr = TConst TNull } when not (has_feature ctx "Type.getClassFields") ->
@@ -1440,10 +1479,14 @@ let generate_class ctx c =
 	(match c.cl_path with
 	| [],"Function" -> abort "This class redefine a native one" c.cl_pos
 	| _ -> ());
-	if ctx.es_version >= 6 then
-		generate_class_es6 ctx c
-	else
-		generate_class_es3 ctx c
+	match c.cl_kind with
+	| KModuleStatics m ->
+		gen_module_statics ctx m c c.cl_ordered_statics
+	| _ ->
+		if ctx.es_version >= 6 then
+			generate_class_es6 ctx c
+		else
+			generate_class_es3 ctx c
 
 let generate_enum ctx e =
 	let p = s_path ctx e.e_path in
@@ -1537,9 +1580,16 @@ let generate_enum ctx e =
 	flush ctx
 
 let generate_static ctx (c,f,e) =
-	let cl_path = get_generated_class_path c in
-	process_expose f.cf_meta (fun () -> (dot_path cl_path) ^ "." ^ f.cf_name) (fun s -> print ctx "$hx_exports%s = " (path_to_brackets s));
-	print ctx "%s%s = " (s_path ctx cl_path) (static_field ctx c f);
+	begin
+	match c.cl_kind with 
+	| KModuleStatics m ->
+		print ctx "var %s = " (module_static m f);
+		process_expose f.cf_meta (fun () -> module_static_expose_path m.m_path f) (fun s -> print ctx "$hx_exports%s = " (path_to_brackets s));
+	| _ ->
+		let cl_path = get_generated_class_path c in
+		process_expose f.cf_meta (fun () -> (dot_path cl_path) ^ "." ^ f.cf_name) (fun s -> print ctx "$hx_exports%s = " (path_to_brackets s));
+		print ctx "%s%s = " (s_path ctx cl_path) (static_field ctx c f);
+	end;
 	gen_value ctx e;
 	newline ctx
 
@@ -1683,11 +1733,18 @@ let generate com =
 		List.iter (
 			function
 			| TClassDecl c ->
-				let path = dot_path c.cl_path in
 				let add s = r := s :: !r in
-				process_expose c.cl_meta (fun () -> path) add;
+				let get_expose_path = 
+					match c.cl_kind with
+					| KModuleStatics m ->
+						module_static_expose_path m.m_path
+					| _ ->
+						let path = dot_path c.cl_path in
+						process_expose c.cl_meta (fun () -> path) add;
+						fun f -> path ^ "." ^ f.cf_name
+				in
 				List.iter (fun f ->
-					process_expose f.cf_meta (fun () -> path ^ "." ^ f.cf_name) add
+					process_expose f.cf_meta (fun () -> get_expose_path f) add
 				) c.cl_ordered_statics
 			| _ -> ()
 		) com.types;

+ 13 - 16
src/generators/genjvm.ml

@@ -2799,14 +2799,9 @@ let file_name_and_extension file =
 
 let generate com =
 	mkdir_from_path com.file;
-	let jar_name,manifest_suffix,entry_point = match get_entry_point com with
-		| Some (jarname,cl,expr) ->
-			let pack = match fst cl.cl_path with
-				| [] -> ["haxe";"root"]
-				| pack -> pack
-			in
-			jarname,"\nMain-Class: " ^ (s_type_path (pack,snd cl.cl_path)), Some (cl,expr)
-		| None -> "jar","",None
+	let jar_name,entry_point = match get_entry_point com with
+		| Some (jarname,cl,expr) -> jarname, Some (cl,expr)
+		| None -> "jar",None
 	in
 	let jar_name = if com.debug then jar_name ^ "-Debug" else jar_name in
 	let jar_dir = add_trailing_slash com.file in
@@ -2848,14 +2843,6 @@ let generate com =
 			Some (Printf.sprintf "lib/%s" name)
 		end
 	) com.native_libs.java_libs in
-	let manifest_content =
-		"Manifest-Version: 1.0\n" ^
-		(match class_paths with [] -> "" | _ -> "Class-Path: " ^ (String.concat " " class_paths ^ "\n")) ^
-		"Created-By: Haxe (Haxe Foundation)" ^
-		manifest_suffix ^
-		"\n\n"
-	in
-	Zip.add_entry manifest_content gctx.jar "META-INF/MANIFEST.MF";
 	Hashtbl.iter (fun name v ->
 		let filename = Codegen.escape_res_name name true in
 		Zip.add_entry v gctx.jar filename;
@@ -2871,4 +2858,14 @@ let generate com =
 	Std.finally (Timer.timer ["generate";"java";"typed interfaces"]) generate_typed_interfaces ();
 	Std.finally (Timer.timer ["generate";"java";"anons"]) generate_anons gctx;
 	Std.finally (Timer.timer ["generate";"java";"typed functions"]) generate_typed_functions gctx;
+
+	let manifest_content =
+		"Manifest-Version: 1.0\n" ^
+		(match class_paths with [] -> "" | _ -> "Class-Path: " ^ (String.concat " " class_paths ^ "\n")) ^
+		"Created-By: Haxe (Haxe Foundation)" ^
+		(Option.map_default (fun (cl,_) ->  "\nMain-Class: " ^ (s_type_path cl.cl_path)) "" entry_point) ^
+		"\n\n"
+	in
+	Zip.add_entry manifest_content gctx.jar "META-INF/MANIFEST.MF";
+
 	Zip.close_out gctx.jar

+ 18 - 11
src/macro/macroApi.ml

@@ -674,17 +674,19 @@ and decode_meta_content m = decode_opt_array decode_meta_entry m
 
 and decode_doc = opt (fun s -> { doc_own = Some (decode_string s); doc_inherited = [] })
 
+and decode_class_field_kind v = 
+	match decode_enum v with
+	| 0, [t;e] ->
+		FVar (opt decode_ctype t, opt decode_expr e)
+	| 1, [f] ->
+		FFun (decode_fun f)
+	| 2, [get;set; t; e] ->
+		FProp (decode_placed_name vnull get, decode_placed_name vnull set, opt decode_ctype t, opt decode_expr e)
+	| _ ->
+		raise Invalid_expr
+
 and decode_field v =
-	let fkind = match decode_enum (field v "kind") with
-		| 0, [t;e] ->
-			FVar (opt decode_ctype t, opt decode_expr e)
-		| 1, [f] ->
-			FFun (decode_fun f)
-		| 2, [get;set; t; e] ->
-			FProp (decode_placed_name vnull get, decode_placed_name vnull set, opt decode_ctype t, opt decode_expr e)
-		| _ ->
-			raise Invalid_expr
-	in
+	let fkind = decode_class_field_kind (field v "kind") in
 	let pos = decode_pos (field v "pos") in
 	{
 		cff_name = (decode_string (field v "name"),decode_pos_default (field v "name_pos") pos);
@@ -974,7 +976,7 @@ and encode_class_kind k =
 	let tag, pl = (match k with
 		| KNormal -> 0, []
 		| KTypeParameter pl -> 1, [encode_tparams pl]
-		(* KExtension was here *)
+		| KModuleStatics m -> 2, [encode_string (s_type_path m.m_path)]
 		| KExpr e -> 3, [encode_expr e]
 		| KGeneric -> 4, []
 		| KGenericInstance (cl, params) -> 5, [encode_clref cl; encode_tparams params]
@@ -1473,6 +1475,11 @@ let decode_type_def v =
 		let flags = match opt decode_array tto with None -> flags | Some ta -> (List.map (fun t -> AbTo (decode_ctype t)) ta) @ flags in
 		let flags = match opt decode_ctype tthis with None -> flags | Some t -> (AbOver t) :: flags in
 		EAbstract(mk flags fields)
+	| 5, [fk;al] ->
+		let fk = decode_class_field_kind fk in
+		let al = List.map decode_access (opt_list decode_array al) in
+		(* let al = if isExtern then (AExtern,pos) :: al else al in *)
+		EStatic (mk al fk)
 	| _ ->
 		raise Invalid_expr
 	) in

+ 59 - 1
src/syntax/grammar.mly

@@ -178,6 +178,47 @@ and parse_type_decl mode s =
 	| [< '(Kwd Using,p1) >] -> parse_using s p1
 	| [< doc = get_doc; meta = parse_meta; c = parse_common_flags; s >] ->
 		match s with parser
+		| [< '(Kwd Function,p1); name = dollar_ident; pl = parse_constraint_params; '(POpen,_); args = psep Comma parse_fun_param; '(PClose,_); t = popt parse_type_hint; s >] ->
+			let e, p2 = (match s with parser
+				| [< e = expr; s >] ->
+					ignore(semicolon s);
+					Some e, pos e
+				| [< p = semicolon >] -> None, p
+				| [< >] -> serror()
+			) in
+			let f = {
+				f_params = pl;
+				f_args = args;
+				f_type = t;
+				f_expr = e;
+			} in
+			(EStatic {
+				d_name = name;
+				d_doc = doc_from_string_opt doc;
+				d_meta = meta;
+				d_params = pl;
+				d_flags = List.map decl_flag_to_global_flag c;
+				d_data = FFun f;
+			}, punion p1 p2)
+		| [< '(Kwd Var,p1); name = dollar_ident; s >] ->
+			let p2,t =
+				match s with parser
+				| [< '(POpen,_); i1 = property_ident; '(Comma,_); i2 = property_ident; '(PClose,_) >] ->
+					let t = popt parse_type_hint s in
+					let e,p2 = parse_var_field_assignment s in
+					p2,FProp (i1,i2,t,e)
+				| [< t = popt parse_type_hint; s >] ->
+					let e,p2 = parse_var_field_assignment s in
+					p2,FVar (t,e)
+			in
+			(EStatic {
+				d_name = name;
+				d_doc = doc_from_string_opt doc;
+				d_meta = meta;
+				d_params = [];
+				d_flags = List.map decl_flag_to_global_flag c;
+				d_data = t;
+			}, punion p1 p2)
 		| [< '(Kwd Enum,p1) >] ->
 			begin match s with parser
 			| [< a,p = parse_abstract doc ((Meta.Enum,[],null_pos) :: meta) c >] ->
@@ -248,7 +289,21 @@ and parse_type_decl mode s =
 		| [< a,p = parse_abstract doc meta c >] ->
 			EAbstract a,p
 		| [< >] ->
-			check_type_decl_flag_completion mode c s
+			match List.rev c with
+			| (DFinal,p1) :: crest ->
+				(match s with parser 
+				| [< name = dollar_ident; t = popt parse_type_hint; e,p2 = parse_var_field_assignment >] ->
+					(EStatic {
+						d_name = name;
+						d_doc = doc_from_string_opt doc;
+						d_meta = meta;
+						d_params = [];
+						d_flags = (List.map decl_flag_to_global_flag (List.rev crest)) @ [AFinal,p1];
+						d_data = FVar(t,e);
+					}, punion p1 p2)
+				| [< >] -> check_type_decl_flag_completion mode c s)
+			| _ ->
+				check_type_decl_flag_completion mode c s
 
 
 and parse_class doc meta cflags need_name s =
@@ -453,6 +508,9 @@ and parse_common_flags = parser
 	| [< '(Kwd Private,p); l = parse_common_flags >] -> (DPrivate,p) :: l
 	| [< '(Kwd Extern,p); l = parse_common_flags >] -> (DExtern,p) :: l
 	| [< '(Kwd Final,p); l = parse_common_flags >] -> (DFinal,p) :: l
+	| [< '(Kwd Macro,p); l = parse_common_flags >] -> (DMacro,p) :: l
+	| [< '(Kwd Dynamic,p); l = parse_common_flags >] -> (DDynamic,p) :: l
+	| [< '(Kwd Inline,p); l = parse_common_flags >] -> (DInline,p) :: l
 	| [< >] -> []
 
 and parse_meta_argument_expr s =

+ 20 - 0
src/syntax/parser.ml

@@ -38,6 +38,9 @@ type decl_flag =
 	| DPrivate
 	| DExtern
 	| DFinal
+	| DMacro
+	| DDynamic
+	| DInline
 
 type type_decl_completion_mode =
 	| TCBeforePackage
@@ -98,16 +101,33 @@ let decl_flag_to_class_flag (flag,p) = match flag with
 	| DPrivate -> HPrivate
 	| DExtern -> HExtern
 	| DFinal -> HFinal
+	| DMacro -> error (Custom "macro on classes is not allowed") p
+	| DDynamic -> error (Custom "dynamic on classes is not allowed") p
+	| DInline -> error (Custom "inline on classes is not allowed") p
 
 let decl_flag_to_enum_flag (flag,p) = match flag with
 	| DPrivate -> EPrivate
 	| DExtern -> EExtern
 	| DFinal -> error (Custom "final on enums is not allowed") p
+	| DMacro -> error (Custom "macro on enums is not allowed") p
+	| DDynamic -> error (Custom "dynamic on enums is not allowed") p
+	| DInline -> error (Custom "inline on enums is not allowed") p
 
 let decl_flag_to_abstract_flag (flag,p) = match flag with
 	| DPrivate -> AbPrivate
 	| DExtern -> AbExtern
 	| DFinal -> error (Custom "final on abstracts is not allowed") p
+	| DMacro -> error (Custom "macro on abstracts is not allowed") p
+	| DDynamic -> error (Custom "dynamic on abstracts is not allowed") p
+	| DInline -> error (Custom "inline on abstracts is not allowed") p
+
+let decl_flag_to_global_flag (flag,p) = match flag with
+	| DPrivate -> (APrivate,p)
+	| DMacro -> (AMacro,p)
+	| DDynamic -> (ADynamic,p)
+	| DInline -> (AInline,p)
+	| DExtern -> error (Custom "extern on module-statics is not allowed") p (* TODO: would be nice to have this actually, but we need some design for it *)
+	| DFinal -> error (Custom "final on module-statics is not allowed") p
 
 module TokenCache = struct
 	let cache = ref (DynArray.create ())

+ 6 - 2
src/typing/fields.ml

@@ -352,6 +352,10 @@ let rec using_field ctx mode e i p =
 		if not !check_constant_struct then raise Not_found;
 		remove_constant_flag e.etype (fun ok -> if ok then using_field ctx mode e i p else raise Not_found)
 
+let check_field_access ctx c f stat p =
+	if not ctx.untyped && not (can_access ctx c f stat) then
+		display_error ctx ("Cannot access private field " ^ f.cf_name) p
+
 (* Resolves field [i] on typed expression [e] using the given [mode]. *)
 let rec type_field cfg ctx e i p mode =
 	let pfield = if (e.epos = p) then p else {p with pmin = p.pmax - (String.length i)} in
@@ -437,7 +441,7 @@ let rec type_field cfg ctx e i p mode =
 					display_error ctx "Cannot create closure on super method" p
 				| _ ->
 					display_error ctx "Normal variables cannot be accessed with 'super', use 'this' instead" pfield);
-			if not (can_access ctx c f false) && not ctx.untyped then display_error ctx ("Cannot access private field " ^ i) pfield;
+			check_field_access ctx c f false pfield;
 			field_access ctx mode f (match c2 with None -> FAnon f | Some (c,tl) -> FInstance (c,tl,f)) (apply_params c.cl_params params t) e p
 		with Not_found -> try
 			begin match e.eexpr with
@@ -535,7 +539,7 @@ let rec type_field cfg ctx e i p mode =
 		(try
 			let c = (match a.a_impl with None -> raise Not_found | Some c -> c) in
 			let f = PMap.find i c.cl_statics in
-			if not (can_access ctx c f true) && not ctx.untyped then display_error ctx ("Cannot access private field " ^ i) pfield;
+			check_field_access ctx c f true pfield;
 			let field_type f =
 				if not (Meta.has Meta.Impl f.cf_meta) then begin
 					static_abstract_access_through_instance := true;

+ 30 - 15
src/typing/finalization.ml

@@ -12,22 +12,37 @@ open Typecore
 let get_main ctx types =
 	match ctx.com.main_class with
 	| None -> None
-	| Some cl ->
-		let t = Typeload.load_type_def ctx null_pos (mk_type_path cl) in
-		let fmode, ft, r = (match t with
-		| TEnumDecl _ | TTypeDecl _ | TAbstractDecl _ ->
-			error ("Invalid -main : " ^ s_type_path cl ^ " is not a class") null_pos
-		| TClassDecl c ->
+	| Some path ->
+		let p = null_pos in
+		let pack,name = path in
+		let m = Typeload.load_module ctx (pack,name) p in
+		let c,f =
+			let p = ref p in
 			try
-				let f = PMap.find "main" c.cl_statics in
-				let t = Type.field_type f in
-				(match follow t with
-				| TFun ([],r) -> FStatic (c,f), t, r
-				| _ -> error ("Invalid -main : " ^ s_type_path cl ^ " has invalid main function") c.cl_pos);
-			with
-				Not_found -> error ("Invalid -main : " ^ s_type_path cl ^ " does not have static function main") c.cl_pos
-		) in
-		let emain = type_type ctx cl null_pos in
+				match m.m_statics with
+				| None ->
+					raise Not_found
+				| Some c ->
+					p := c.cl_pos;
+					c, PMap.find "main" c.cl_statics
+			with Not_found -> try
+				let t = Typeload.find_type_in_module_raise m name null_pos in
+				match t with
+				| TEnumDecl _ | TTypeDecl _ | TAbstractDecl _ ->
+					error ("Invalid -main : " ^ s_type_path path ^ " is not a class") null_pos
+				| TClassDecl c ->
+					p := c.cl_pos;
+					c, PMap.find "main" c.cl_statics
+			with Not_found ->
+				error ("Invalid -main : " ^ s_type_path path ^ " does not have static function main") !p
+		in
+		let ft = Type.field_type f in
+		let fmode, r = 
+			match follow ft with
+			| TFun ([],r) -> FStatic (c,f), r
+			| _ -> error ("Invalid -main : " ^ s_type_path path ^ " has invalid main function") c.cl_pos
+		in
+		let emain = type_module_type ctx (TClassDecl c) None null_pos in
 		let main = mk (TCall (mk (TField (emain,fmode)) ft null_pos,[])) r null_pos in
 		(* add haxe.EntryPoint.run() call *)
 		let main = (try

+ 2 - 0
src/typing/generic.ml

@@ -160,6 +160,7 @@ let static_method_container gctx c cf p =
 			m_id = alloc_mid();
 			m_path = (pack,name);
 			m_types = [];
+			m_statics = None;
 			m_extra = module_extra (s_type_path (pack,name)) m.m_extra.m_sign 0. MFake m.m_extra.m_check_policy;
 		} in
 		gctx.mg <- Some mg;
@@ -205,6 +206,7 @@ let rec build_generic ctx c p tl =
 			m_id = alloc_mid();
 			m_path = (pack,name);
 			m_types = [];
+			m_statics = None;
 			m_extra = module_extra (s_type_path (pack,name)) m.m_extra.m_sign 0. MFake m.m_extra.m_check_policy;
 		} in
 		gctx.mg <- Some mg;

+ 22 - 11
src/typing/macroContext.ml

@@ -539,20 +539,31 @@ let load_macro_module ctx cpath display p =
 let load_macro' ctx display cpath f p =
 	let api, mctx = get_macro_context ctx p in
 	let mint = Interp.get_ctx() in
-	let mpath, sub = (match List.rev (fst cpath) with
-		| name :: pack when name.[0] >= 'A' && name.[0] <= 'Z' -> (List.rev pack,name), Some (snd cpath)
-		| _ -> cpath, None
-	) in
 	let (meth,mloaded) = try Hashtbl.find mctx.com.cached_macros (cpath,f) with Not_found ->
 		let t = macro_timer ctx ["typing";s_type_path cpath ^ "." ^ f] in
-		let mloaded,restore = load_macro_module ctx mpath display p in
-		let mt = Typeload.load_type_def mctx p (mk_type_path ?sub mpath) in
-		let cl, meth = (match mt with
-			| TClassDecl c ->
-				mctx.g.do_finalize mctx;
-				c, (try PMap.find f c.cl_statics with Not_found -> error ("Method " ^ f ^ " not found on class " ^ s_type_path cpath) p)
-			| _ -> error "Macro should be called on a class" p
+		let mpath, sub = (match List.rev (fst cpath) with
+			| name :: pack when name.[0] >= 'A' && name.[0] <= 'Z' -> (List.rev pack,name), Some (snd cpath)
+			| _ -> cpath, None
 		) in
+		let mloaded,restore = load_macro_module ctx mpath display p in
+		let cl, meth =
+			try
+				if sub <> None then raise Not_found;
+				match mloaded.m_statics with
+				| None -> raise Not_found
+				| Some c ->
+					mctx.g.do_finalize mctx;
+					c, PMap.find f c.cl_statics
+			with Not_found ->
+				let name = Option.default (snd mpath) sub in
+				let path = fst mpath, name in
+				let mt = try List.find (fun t2 -> (t_infos t2).mt_path = path) mloaded.m_types with Not_found -> raise_error (Type_not_found (mloaded.m_path,name,Not_defined)) p in
+				match mt with
+				| TClassDecl c ->
+					mctx.g.do_finalize mctx;
+					c, (try PMap.find f c.cl_statics with Not_found -> error ("Method " ^ f ^ " not found on class " ^ s_type_path cpath) p)
+				| _ -> error "Macro should be called on a class" p
+		in
 		api.MacroApi.current_macro_module <- (fun() -> mloaded);
 		DeprecationCheck.check_cf mctx.com meth p;
 		let meth = (match follow meth.cf_type with TFun (args,ret) -> (args,ret,cl,meth),mloaded | _ -> error "Macro call should be a method" p) in

+ 1 - 1
src/typing/typeload.ml

@@ -930,7 +930,7 @@ let handle_using ctx path p =
 		| None ->
 			let md = ctx.g.do_load_module ctx (t.tpackage,t.tname) p in
 			let types = List.filter (fun t -> not (t_infos t).mt_private) md.m_types in
-			types
+			Option.map_default (fun c -> (TClassDecl c) :: types) types md.m_statics
 		| Some _ ->
 			let t = load_type_def ctx p t in
 			[t]

+ 1 - 1
src/typing/typeloadFields.ml

@@ -605,7 +605,7 @@ let is_public (ctx,cctx) access parent =
 		true
 	else match parent with
 		| Some cf -> (has_class_field_flag cf CfPublic)
-		| _ -> c.cl_extern || c.cl_interface || cctx.extends_public
+		| _ -> c.cl_extern || c.cl_interface || cctx.extends_public || (match c.cl_kind with KModuleStatics _ -> true | _ -> false)
 
 let rec get_parent c name =
 	match c.cl_super with

+ 101 - 11
src/typing/typeloadModule.ml

@@ -39,6 +39,7 @@ let make_module ctx mpath file loadp =
 		m_id = alloc_mid();
 		m_path = mpath;
 		m_types = [];
+		m_statics = None;
 		m_extra = module_extra (Path.get_full_path file) (Define.get_signature ctx.com.defines) (file_time file) (if ctx.in_macro then MMacro else MCode) (get_policy ctx mpath);
 	} in
 	m
@@ -192,13 +193,21 @@ end
 let module_pass_1 ctx m tdecls loadp =
 	let com = ctx.com in
 	let decls = ref [] in
-	let make_path name priv p =
+	let statics = ref [] in
+	let check_name name p =
+		let error prev_pos =
+			display_error ctx ("Name " ^ name ^ " is already defined in this module") p;
+			error "Previous declaration here" prev_pos;
+		in
 		List.iter (fun (t2,(_,p2)) ->
-			if snd (t_path t2) = name then begin
-				display_error ctx ("Type name " ^ name ^ " is already defined in this module") p;
-				error "Previous declaration here" p2;
-			end
+			if snd (t_path t2) = name then error p2
 		) !decls;
+		List.iter (fun (d,p) ->
+			if fst d.d_name = name then error p
+		) !statics
+	in
+	let make_path name priv p =
+		check_name name p;
 		if priv then (fst m.m_path @ ["_" ^ snd m.m_path], name) else (fst m.m_path, name)
 	in
 	let has_declaration = ref false in
@@ -210,8 +219,13 @@ let module_pass_1 ctx m tdecls loadp =
 		in
 		let acc = (match fst decl with
 		| EImport _ | EUsing _ ->
-			if !has_declaration then error "import and using may not appear after a type declaration" p;
+			if !has_declaration then error "import and using may not appear after a declaration" p;
 			acc
+		| EStatic d ->
+			check_name (fst d.d_name) p;
+			has_declaration := true;
+			statics := (d,p) :: !statics;
+			acc;
 		| EClass d ->
 			let name = fst d.d_name in
 			has_declaration := true;
@@ -348,6 +362,43 @@ let module_pass_1 ctx m tdecls loadp =
 		decl :: acc
 	in
 	let tdecls = List.fold_left make_decl [] tdecls in
+	let tdecls =
+		match !statics with
+		| [] ->
+			tdecls
+		| statics ->
+			let first_pos = ref null_pos in
+			let fields = List.map (fun (d,p) ->
+				first_pos := p;
+				{
+					cff_name = d.d_name;
+					cff_doc = d.d_doc;
+					cff_pos = p;
+					cff_meta = d.d_meta;
+					cff_access = (AStatic,null_pos) :: d.d_flags;
+					cff_kind = d.d_data;
+				}
+			) statics in
+			let p = let p = !first_pos in { p with pmax = p.pmin } in
+			let c = EClass {
+				d_name = (snd m.m_path) ^ "_Statics_", p;
+				d_flags = [HPrivate];
+				d_data = fields;
+				d_doc = None;
+				d_params = [];
+				d_meta = []
+			} in
+			let tdecls = make_decl tdecls (c,p) in
+			(match !decls with
+			| (TClassDecl c,_) :: _ ->
+				assert (m.m_statics = None);
+				m.m_statics <- Some c;
+				c.cl_kind <- KModuleStatics m;
+				c.cl_final <- true;
+			| _ -> assert false);
+			tdecls
+
+	in
 	let decls = List.rev !decls in
 	decls, List.rev tdecls
 
@@ -442,7 +493,8 @@ let init_module_type ctx context_init (decl,p) =
 			let md = ctx.g.do_load_module ctx (List.map fst pack,tname) p_type in
 			let types = md.m_types in
 			let no_private (t,_) = not (t_infos t).mt_private in
-			let chk_private t p = if (t_infos t).mt_private then error "You can't import a private type" p in
+			let error_private p = error "Importing private declarations from a module is not allowed" p in
+			let chk_private t p = if (t_infos t).mt_private then error_private p in
 			let has_name name t = snd (t_infos t).mt_path = name in
 			let get_type tname =
 				let t = (try List.find (has_name tname) types with Not_found -> error (StringError.string_error tname (List.map (fun mt -> snd (t_infos mt).mt_path) types) ("Module " ^ s_type_path md.m_path ^ " does not define type " ^ tname)) p_type) in
@@ -490,7 +542,16 @@ let init_module_type ctx context_init (decl,p) =
 				| [] ->
 					(match name with
 					| None ->
-						ctx.m.module_types <- List.filter no_private (List.map (fun t -> t,p) types) @ ctx.m.module_types
+						ctx.m.module_types <- List.filter no_private (List.map (fun t -> t,p) types) @ ctx.m.module_types;
+						Option.may (fun c ->
+							context_init#add (fun () ->
+								ignore(c.cl_build());
+								List.iter (fun cf ->
+									if has_class_field_flag cf CfPublic then
+										ctx.m.module_globals <- PMap.add cf.cf_name (TClassDecl c,cf.cf_name,p) ctx.m.module_globals
+								) c.cl_ordered_statics
+							);
+						) md.m_statics
 					| Some(newname,pname) ->
 						ctx.m.module_types <- (rebind (get_type tname) newname pname,p) :: ctx.m.module_types);
 				| [tsub,p2] ->
@@ -501,13 +562,39 @@ let init_module_type ctx context_init (decl,p) =
 						ctx.m.module_types <- ((match name with None -> tsub | Some(n,pname) -> rebind tsub n pname),p) :: ctx.m.module_types
 					with Not_found ->
 						(* this might be a static property, wait later to check *)
-						let tmain = get_type tname in
-						context_init#add (fun() ->
+						let find_main_type_static () =
+							let tmain = get_type tname in
 							try
 								add_static_init tmain name tsub
 							with Not_found ->
+								(* TODO: mention module-level declarations in the error message? *)
 								display_error ctx (s_type_path (t_infos tmain).mt_path ^ " has no field or subtype " ^ tsub) p
-						))
+						in
+						context_init#add (fun() ->
+							match md.m_statics with
+							| Some c ->
+								(try
+									ignore(c.cl_build());
+									let rec loop fl =
+										match fl with
+										| [] -> raise Not_found
+										| cf :: rest ->
+											if cf.cf_name = tsub then
+												if not (has_class_field_flag cf CfPublic) then
+													error_private p
+												else
+													let imported_name = match name with None -> tsub | Some (n,pname) -> n in
+													ctx.m.module_globals <- PMap.add imported_name (TClassDecl c,tsub,p) ctx.m.module_globals;
+											else
+												loop rest
+									in
+									loop c.cl_ordered_statics
+								with Not_found ->
+									find_main_type_static ())
+							| None ->
+								find_main_type_static ()
+						)
+					)
 				| (tsub,p2) :: (fname,p3) :: rest ->
 					(match rest with
 					| [] -> ()
@@ -808,6 +895,9 @@ let init_module_type ctx context_init (decl,p) =
 			else
 				error "Abstract is missing underlying type declaration" a.a_pos
 		end
+	| EStatic _ ->
+		(* nothing to do here as module statics are collected into a special EClass *)
+		()
 
 let module_pass_2 ctx m decls tdecls p =
 	(* here is an additional PASS 1 phase, which define the type parameters for all module types.

+ 3 - 1
src/typing/typeloadParse.ml

@@ -123,9 +123,10 @@ let resolve_module_file com m remap p =
 			| (EEnum d,_) :: _ -> d.d_meta
 			| (EAbstract d,_) :: _ -> d.d_meta
 			| (ETypedef d,_) :: _ -> d.d_meta
+			| (EStatic d,_) :: _ -> d.d_meta
 			| [] -> []
 		in
-		let meta =  match parse_result with
+		let meta = match parse_result with
 			| ParseSuccess((_,decls),_,_) -> loop decls
 			| ParseError _ -> []
 		in
@@ -337,6 +338,7 @@ let parse_module ctx m p =
 			| EEnum d -> build EPrivate d
 			| ETypedef d -> build EPrivate d
 			| EAbstract d -> build AbPrivate d
+			| EStatic d -> build (AStatic,null_pos) d
 			| EImport _ | EUsing _ -> acc
 		) [(EImport (List.map (fun s -> s,null_pos) (!remap @ [snd m]),INormal),null_pos)] decls)
 	else

+ 9 - 0
src/typing/typer.ml

@@ -366,6 +366,15 @@ let rec type_ident_raise ctx i p mode =
 		let e = type_type ctx ctx.curclass.cl_path p in
 		(* check_locals_masking already done in type_type *)
 		field_access ctx mode f (FStatic (ctx.curclass,f)) (field_type ctx ctx.curclass [] f p) e p
+	with Not_found -> try
+		(* module-level statics *)
+		(match ctx.m.curmod.m_statics with
+		| None -> raise Not_found
+		| Some c ->
+			let f = PMap.find i c.cl_statics in
+			let e = type_module_type ctx (TClassDecl c) None p in
+			field_access ctx mode f (FStatic (c,f)) (field_type ctx c [] f p) e p
+		)
 	with Not_found -> try
 		let wrap e = if mode = MSet then
 				AKNo i

+ 29 - 12
src/typing/typerDotPath.ml

@@ -39,22 +39,39 @@ let mk_dot_path_part s p : dot_path_part =
 let s_dot_path parts =
 	String.concat "." (List.map (fun (s,_,_) -> s) parts)
 
+(** resolve given path against module statics or raise Not_found *)
+let resolve_module_static ctx m path p =
+	match path, m.m_statics with
+	| [], _ | _, None ->
+		raise Not_found
+	| (name,_,p) :: path_rest, Some c ->
+		let f = PMap.find name c.cl_statics in (* raises Not_found *)
+		check_field_access ctx c f true p;
+		let ft = Fields.field_type ctx c [] f p in
+		let e = type_module_type ctx (TClassDecl c) None p in
+		(fun mode -> field_access ctx mode f (FStatic (c,f)) ft e p), path_rest
+
 let resolve_module_type ctx m name p =
 	let t = Typeload.find_type_in_module m name in (* raises Not_found *)
 	mk_module_type_access ctx t p
 
 let resolve_in_module ctx m path p =
-	let mname = snd m.m_path in
-	match path with
-	| (sname,PUppercase,sp) :: path_rest ->
-		begin
-		try
-			resolve_module_type ctx m sname sp, path_rest
-		with Not_found ->
+	try
+		(* first, try to find module-level static access *)
+		resolve_module_static ctx m path p
+	with Not_found ->
+		(* if there was no module-statics, resolve  *)
+		let mname = snd m.m_path in
+		match path with
+		| (sname,PUppercase,sp) :: path_rest ->
+			begin
+			try
+				resolve_module_type ctx m sname sp, path_rest
+			with Not_found ->
+				resolve_module_type ctx m mname p, path
+			end
+		| _ ->
 			resolve_module_type ctx m mname p, path
-		end
-	| _ ->
-		resolve_module_type ctx m mname p, path
 
 (** resolve given qualified module pack+name (and possibly next path part) or raise Not_found *)
 let resolve_qualified ctx pack name next_path p =
@@ -75,11 +92,11 @@ let resolve_unqualified ctx name next_path p =
 
 		begin
 			(*
-				if there's further uppercase field access, it might be a this-package module access rather than static field access,
+				if there's further field access, it might be a this-package module access rather than static field access,
 				so we try resolving a field first and fall back to find_in_unqualified_modules
 			*)
 			match next_path with
-			| (field,PUppercase,pfield) :: next_path ->
+			| (field,_,pfield) :: next_path ->
 				let e = type_module_type ctx t None p in
 				let f = type_field (TypeFieldConfig.create true) ctx e field pfield in
 				ignore(f MCall); (* raises Not_found *) (* not necessarily a call, but prevent #2602 among others *)

+ 5 - 0
std/haxe/macro/Expr.hx

@@ -953,6 +953,11 @@ enum TypeDefKind {
 		Represents an abstract kind.
 	**/
 	TDAbstract(tthis:Null<ComplexType>, ?from:Array<ComplexType>, ?to:Array<ComplexType>);
+
+	/**
+		Represents a module-level static field.
+	**/
+	TDStatic(kind:FieldType, ?access:Array<Access>); // ignore TypeDefinition.fields
 }
 
 /**

+ 8 - 0
std/haxe/macro/Printer.hx

@@ -377,6 +377,14 @@ class Printer {
 						}
 					].join("\n")
 					+ "\n}";
+				case TDStatic(kind, access):
+					tabs = old;
+					(access != null && access.length > 0 ? access.map(printAccess).join(" ") + " " : "")
+					+ switch (kind) {
+						case FVar(type, eo): ((access != null && access.has(AFinal)) ? '' : 'var ') + '${t.name}' + opt(type, printComplexType, " : ") + opt(eo, printExpr, " = ") + ";";
+						case FProp(get, set, type, eo): 'var ${t.name}($get, $set)' + opt(type, printComplexType, " : ") + opt(eo, printExpr, " = ") + ";";
+						case FFun(func): 'function ${t.name}' + printFunction(func) + switch func.expr { case {expr: EBlock(_)}: ""; case _: ";"; };
+					}
 			} tabs = old;
 
 		return str;

+ 2 - 4
std/haxe/macro/Type.hx

@@ -303,11 +303,9 @@ enum ClassKind {
 	KTypeParameter(constraints:Array<Type>);
 
 	/**
-		A structurally extended class.
-
-		@deprecated
+		A class containing module statics.
 	**/
-	KExtension(cl:Ref<ClassType>, params:Array<Type>);
+	KModuleStatics(module:String);
 
 	/**
 		A special kind of class to encode expressions into type parameters.

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

@@ -1,2 +1,2 @@
-Foo.hx:2: characters 1-18 : Type name A is already defined in this module
+Foo.hx:2: characters 1-18 : Name A is already defined in this module
 Foo.hx:1: characters 1-16 : Previous declaration here

+ 4 - 2
tests/misc/resolution/projects/Issue9367/Main.hx

@@ -2,8 +2,10 @@ import utest.Assert.equals;
 
 class Main extends utest.Test {
 	function test() {
-		equals("subtype1subtype2", pack.UsageNoImport.f());
-		equals("field1subtype2", pack.UsageImport.f());
+		equals("subtype1subtype2static3", pack.UsageNoImport.f());
+		equals("pack.Mod3.lowerCase", pack.UsageNoImport.f2());
+		equals("field1subtype2field3", pack.UsageImport.f());
+		equals("pack.Mod3.lowerCase", pack.UsageImport.f2());
 	}
 
 	static function main() {

+ 11 - 0
tests/misc/resolution/projects/Issue9367/pack/Mod3.hx

@@ -0,0 +1,11 @@
+package pack;
+
+class Mod3 {
+	public static final Mod3Sub = {field: "field3"}
+}
+
+final Mod3Sub = {
+	field: "static3",
+}
+
+function lowerCase() return "pack.Mod3.lowerCase";

+ 6 - 1
tests/misc/resolution/projects/Issue9367/pack/UsageImport.hx

@@ -2,9 +2,14 @@ package pack;
 
 import pack.Mod1;
 import pack.Mod2;
+import pack.Mod3;
 
 class UsageImport {
 	public static function f() {
-		return Mod1.Mod1Sub.field + Mod2.Mod2Sub.field;
+		return Mod1.Mod1Sub.field + Mod2.Mod2Sub.field + Mod3.Mod3Sub.field;
+	}
+
+	public static function f2() {
+		return Mod3.lowerCase();
 	}
 }

+ 5 - 1
tests/misc/resolution/projects/Issue9367/pack/UsageNoImport.hx

@@ -5,6 +5,10 @@ package pack;
 
 class UsageNoImport {
 	public static function f() {
-		return Mod1.Mod1Sub.field + Mod2.Mod2Sub.field;
+		return Mod1.Mod1Sub.field + Mod2.Mod2Sub.field + Mod3.Mod3Sub.field;
+	}
+
+	public static function f2() {
+		return Mod3.lowerCase();
 	}
 }

+ 1 - 0
tests/misc/resolution/projects/modulestatics/.gitignore

@@ -0,0 +1 @@
+/test.js

+ 2 - 0
tests/misc/resolution/projects/modulestatics/Duplicate.hx

@@ -0,0 +1,2 @@
+function C() {}
+class C {}

+ 58 - 0
tests/misc/resolution/projects/modulestatics/Macro.hx

@@ -0,0 +1,58 @@
+import haxe.macro.Expr;
+
+final calls = [];
+final builds = [];
+
+macro function getCalls() {
+	return macro $v{calls};
+}
+
+macro function getBuilds() {
+	return macro $v{builds};
+}
+
+function c(name) {
+	calls.push(name);
+}
+
+function b(name) {
+	builds.push(name);
+}
+
+function lowerCase() {
+	c("lowerCase");
+}
+
+function UpperCase() {
+	c("UpperCase");
+}
+
+function build() {
+	b("build");
+	return [];
+}
+
+function Build() {
+	b("Build");
+	return [];
+}
+
+class Macro {
+	static function lowerCase() {
+		c("Macro.lowerCase");
+	}
+
+	static function UpperCase() {
+		c("Macro.UpperCase");
+	}
+
+	static function build() {
+		b("Macro.build");
+		return [];
+	}
+
+	static function Build() {
+		b("Macro.Build");
+		return [];
+	}
+}

+ 105 - 0
tests/misc/resolution/projects/modulestatics/Main.hx

@@ -0,0 +1,105 @@
+import utest.Assert;
+
+import RootMod1;
+import RootMod3.lowerCase3;
+import RootMod3.UpperCase3;
+import RootMod4.lowerCase as lowerCase4;
+import RootMod4.UpperCase as UpperCase4;
+import pack.Mod1;
+import ModWithStaticAndClassStatic2;
+
+class Main extends utest.Test {
+	function testImportedModule() {
+		Assert.equals("RootMod1.lowerCase", lowerCase());
+		Assert.equals("RootMod1.UpperCase", UpperCase());
+		Assert.equals("pack.Mod1.lowerCasePack", lowerCasePack());
+		Assert.equals("pack.Mod1.UpperCasePack", UpperCasePack());
+	}
+
+	function testUnimportedRootModule() {
+		Assert.equals("RootMod2.lowerCase", RootMod2.lowerCase());
+		Assert.equals("RootMod2.UpperCase", RootMod2.UpperCase());
+	}
+
+	function testUnimportedRootModuleWithStd() {
+		Assert.equals("RootMod2.lowerCase", std.RootMod2.lowerCase());
+		Assert.equals("RootMod2.UpperCase", std.RootMod2.UpperCase());
+	}
+
+	function testUnimportedPackModule() {
+		Assert.equals("pack.Mod2.lowerCasePack", pack.Mod2.lowerCasePack());
+		Assert.equals("pack.Mod2.UpperCasePack", pack.Mod2.UpperCasePack());
+	}
+
+	function testUnimportedPackModuleWithStd() {
+		Assert.equals("pack.Mod2.lowerCasePack", std.pack.Mod2.lowerCasePack());
+		Assert.equals("pack.Mod2.UpperCasePack", std.pack.Mod2.UpperCasePack());
+	}
+
+	function testImportedFunction() {
+		Assert.equals("RootMod3.lowerCase", lowerCase3());
+		Assert.equals("RootMod3.UpperCase", UpperCase3());
+	}
+
+	function testImportedFunctionAliased() {
+		Assert.equals("RootMod4.lowerCase", lowerCase4());
+		Assert.equals("RootMod4.UpperCase", UpperCase4());
+	}
+
+	function testPrivate() {
+		Assert.equals("ModWithPrivate.f", ModWithPrivate.f());
+	}
+
+	function testUnimportedModuleStaticBeforeMainClassStatic() {
+		Assert.equals("ModWithStaticAndClassStatic.lowerCaseMod", ModWithStaticAndClassStatic.lowerCaseMod());
+		Assert.equals("ModWithStaticAndClassStatic.UpperCaseMod", ModWithStaticAndClassStatic.UpperCaseMod());
+	}
+
+	function testImportedClassStaticBeforeModuleStatic() {
+		Assert.equals("ModWithStaticAndClassStatic2.ModWithStaticAndClassStatic2.lowerCaseMod", ModWithStaticAndClassStatic2.lowerCaseMod());
+		Assert.equals("ModWithStaticAndClassStatic2.ModWithStaticAndClassStatic2.UpperCaseMod", ModWithStaticAndClassStatic2.UpperCaseMod());
+	}
+
+	function testModuleWithStaticsResolvesToMainType() {
+		Assert.equals("ModWithStaticAndClassStatic", Type.getClassName(ModWithStaticAndClassStatic));
+		Assert.equals("ModWithStaticAndClassStatic2", Type.getClassName(ModWithStaticAndClassStatic2));
+		Assert.equals("pack.Mod2", Type.getClassName(pack.Mod2));
+	}
+
+	function testMacro() {
+		Assert.same([
+			"lowerCase",
+			"UpperCase",
+			"Macro.lowerCase",
+			"Macro.UpperCase",
+		], Macro.getCalls());
+
+		// force build
+		(null : C1);
+		(null : C2);
+		(null : C3);
+		(null : C4);
+		var builds = Macro.getBuilds();
+		builds.sort(Reflect.compare);
+		trace(builds);
+		Assert.same([
+			"Build",
+			"Macro.Build",
+			"Macro.build",
+			"build",
+		], builds);
+	}
+
+	static function main() {
+		utest.UTest.run([
+			new Main(),
+			new Wildcard(),
+			new pack.inner.Test(),
+		]);
+	}
+}
+
+@:build(Macro.build()) private class C1 {}
+@:build(Macro.Build()) private class C2 {}
+@:build(Macro.Macro.build()) private class C3 {}
+@:build(Macro.Macro.Build()) private class C4 {}

+ 7 - 0
tests/misc/resolution/projects/modulestatics/ModWithPrivate.hx

@@ -0,0 +1,7 @@
+function f() {
+    return privateFunc();
+}
+
+private function privateFunc() {
+    return "ModWithPrivate.f";
+}

+ 17 - 0
tests/misc/resolution/projects/modulestatics/ModWithStaticAndClassStatic.hx

@@ -0,0 +1,17 @@
+function UpperCaseMod() {
+    return "ModWithStaticAndClassStatic.UpperCaseMod";
+}
+
+function lowerCaseMod() {
+    return "ModWithStaticAndClassStatic.lowerCaseMod";
+}
+
+class ModWithStaticAndClassStatic {
+    public static function UpperCaseMod() {
+        return "ModWithStaticAndClassStatic.ModWithStaticAndClassStatic.UpperCaseMod";
+    }
+    
+    public static function lowerCaseMod() {
+        return "ModWithStaticAndClassStatic.ModWithStaticAndClassStatic.lowerCaseMod";
+    }
+}

+ 17 - 0
tests/misc/resolution/projects/modulestatics/ModWithStaticAndClassStatic2.hx

@@ -0,0 +1,17 @@
+function UpperCaseMod() {
+    return "ModWithStaticAndClassStatic2.UpperCaseMod";
+}
+
+function lowerCaseMod() {
+    return "ModWithStaticAndClassStatic2.lowerCaseMod";
+}
+
+class ModWithStaticAndClassStatic2 {
+    public static function UpperCaseMod() {
+        return "ModWithStaticAndClassStatic2.ModWithStaticAndClassStatic2.UpperCaseMod";
+    }
+    
+    public static function lowerCaseMod() {
+        return "ModWithStaticAndClassStatic2.ModWithStaticAndClassStatic2.lowerCaseMod";
+    }
+}

+ 5 - 0
tests/misc/resolution/projects/modulestatics/ModuleStaticWithNoMainClass.hx

@@ -0,0 +1,5 @@
+class ModuleStaticWithNoMainClass {
+    static function main() {
+        trace(RootMod1);
+    }
+}

+ 5 - 0
tests/misc/resolution/projects/modulestatics/ModuleStaticWithNoMainClassInPack.hx

@@ -0,0 +1,5 @@
+class ModuleStaticWithNoMainClassInPack {
+    static function main() {
+        trace(pack.Mod1);
+    }
+}

+ 5 - 0
tests/misc/resolution/projects/modulestatics/PrivateAccess.hx

@@ -0,0 +1,5 @@
+class PrivateAccess {
+	static function main() {
+		ModWithPrivate.privateFunc();
+	}
+}

+ 7 - 0
tests/misc/resolution/projects/modulestatics/PrivateImport.hx

@@ -0,0 +1,7 @@
+import ModWithPrivate.privateFunc;
+
+class PrivateImport {
+	static function main() {
+		privateFunc();
+	}
+}

+ 7 - 0
tests/misc/resolution/projects/modulestatics/RootMod1.hx

@@ -0,0 +1,7 @@
+function lowerCase() {
+    return "RootMod1.lowerCase";
+}
+
+function UpperCase() {
+    return "RootMod1.UpperCase";
+}

+ 7 - 0
tests/misc/resolution/projects/modulestatics/RootMod2.hx

@@ -0,0 +1,7 @@
+function lowerCase() {
+    return "RootMod2.lowerCase";
+}
+
+function UpperCase() {
+    return "RootMod2.UpperCase";
+}

+ 7 - 0
tests/misc/resolution/projects/modulestatics/RootMod3.hx

@@ -0,0 +1,7 @@
+function lowerCase3() {
+    return "RootMod3.lowerCase";
+}
+
+function UpperCase3() {
+    return "RootMod3.UpperCase";
+}

+ 7 - 0
tests/misc/resolution/projects/modulestatics/RootMod4.hx

@@ -0,0 +1,7 @@
+function lowerCase() {
+    return "RootMod4.lowerCase";
+}
+
+function UpperCase() {
+    return "RootMod4.UpperCase";
+}

+ 13 - 0
tests/misc/resolution/projects/modulestatics/Wildcard.hx

@@ -0,0 +1,13 @@
+import pack.inner.*;
+import pack.shadow.*;
+
+import utest.Assert;
+
+class Wildcard extends utest.Test {
+    function test() {
+        Assert.equals("pack.inner.InnerMod.lowerCase", InnerMod.lowerCase());
+        Assert.equals("pack.inner.InnerMod.UpperCase", InnerMod.UpperCase());
+        Assert.equals("pack.shadow.Test.lowerCase", Test.lowerCase());
+        Assert.equals("pack.shadow.Test.UpperCase", Test.UpperCase());
+    }
+}

+ 2 - 0
tests/misc/resolution/projects/modulestatics/compile-duplicate-fail.hxml

@@ -0,0 +1,2 @@
+-main Duplicate
+-js duplicateFail.js

+ 2 - 0
tests/misc/resolution/projects/modulestatics/compile-duplicate-fail.hxml.stderr

@@ -0,0 +1,2 @@
+Duplicate.hx:2: characters 1-11 : Name C is already defined in this module
+Duplicate.hx:1: characters 1-16 : Previous declaration here

+ 2 - 0
tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClass-fail.hxml

@@ -0,0 +1,2 @@
+-main ModuleStaticWithNoMainClass
+-js moduleStaticWithNoMainClass.js

+ 1 - 0
tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClass-fail.hxml.stderr

@@ -0,0 +1 @@
+ModuleStaticWithNoMainClass.hx:3: characters 15-23 : Module RootMod1 does not define type RootMod1

+ 2 - 0
tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClassInPack-fail.hxml

@@ -0,0 +1,2 @@
+-main ModuleStaticWithNoMainClassInPack
+-js moduleStaticWithNoMainClass.js

+ 1 - 0
tests/misc/resolution/projects/modulestatics/compile-moduleStaticWithNoMainClassInPack-fail.hxml.stderr

@@ -0,0 +1 @@
+ModuleStaticWithNoMainClassInPack.hx:3: characters 15-24 : Module pack.Mod1 does not define type Mod1

+ 2 - 0
tests/misc/resolution/projects/modulestatics/compile-private-fail.hxml

@@ -0,0 +1,2 @@
+-main PrivateImport
+-js privateFail.js

+ 1 - 0
tests/misc/resolution/projects/modulestatics/compile-private-fail.hxml.stderr

@@ -0,0 +1 @@
+PrivateImport.hx:1: characters 1-35 : Importing private declarations from a module is not allowed

+ 2 - 0
tests/misc/resolution/projects/modulestatics/compile-private2-fail.hxml

@@ -0,0 +1,2 @@
+-main PrivateAccess
+-js privateFail2.js

+ 1 - 0
tests/misc/resolution/projects/modulestatics/compile-private2-fail.hxml.stderr

@@ -0,0 +1 @@
+PrivateAccess.hx:3: characters 3-29 : Cannot access private field privateFunc

+ 8 - 0
tests/misc/resolution/projects/modulestatics/compile.hxml

@@ -0,0 +1,8 @@
+-main Main
+--macro Macro.lowerCase()
+--macro Macro.UpperCase()
+--macro Macro.Macro.lowerCase()
+--macro Macro.Macro.UpperCase()
+-lib utest
+-js test.js
+-cmd node test.js

+ 11 - 0
tests/misc/resolution/projects/modulestatics/pack/InnerMod.hx

@@ -0,0 +1,11 @@
+package pack.inner;
+
+// this module should NOT shadow InnerMod when called from within pack.inner
+
+function lowerCase() {
+    return "pack.InnerMod.lowerCase";
+}
+
+function UpperCase() {
+    return "pack.InnerMod.UpperCase";
+}

+ 9 - 0
tests/misc/resolution/projects/modulestatics/pack/Mod1.hx

@@ -0,0 +1,9 @@
+package pack;
+
+function lowerCasePack() {
+    return "pack.Mod1.lowerCasePack";
+}
+
+function UpperCasePack() {
+    return "pack.Mod1.UpperCasePack";
+}

+ 11 - 0
tests/misc/resolution/projects/modulestatics/pack/Mod2.hx

@@ -0,0 +1,11 @@
+package pack;
+
+function lowerCasePack() {
+    return "pack.Mod2.lowerCasePack";
+}
+
+function UpperCasePack() {
+    return "pack.Mod2.UpperCasePack";
+}
+
+class Mod2 {}

+ 9 - 0
tests/misc/resolution/projects/modulestatics/pack/inner/InnerMod.hx

@@ -0,0 +1,9 @@
+package pack.inner;
+
+function lowerCase() {
+    return "pack.inner.InnerMod.lowerCase";
+}
+
+function UpperCase() {
+    return "pack.inner.InnerMod.UpperCase";
+}

+ 31 - 0
tests/misc/resolution/projects/modulestatics/pack/inner/Test.hx

@@ -0,0 +1,31 @@
+package pack.inner;
+
+import utest.Assert;
+
+class Test extends utest.Test {
+    function testUnqualifiedThisPack() {
+        Assert.equals("pack.inner.InnerMod.lowerCase", pack.inner.InnerMod.lowerCase());
+        Assert.equals("pack.inner.InnerMod.UpperCase", pack.inner.InnerMod.UpperCase());
+    }
+    
+    function testUnqualifiedUpperPack() {
+        Assert.equals("pack.Mod1.lowerCasePack", Mod1.lowerCasePack());
+        Assert.equals("pack.Mod1.UpperCasePack", Mod1.UpperCasePack());
+    }
+    
+    function testUnqualifiedRootPack() {
+        Assert.equals("RootMod1.lowerCase", RootMod1.lowerCase());
+        Assert.equals("RootMod1.UpperCase", RootMod1.UpperCase());
+    }
+    
+    function testUnqualifiedRootPackStd() {
+        Assert.equals("RootMod1.lowerCase", std.RootMod1.lowerCase());
+        Assert.equals("RootMod1.UpperCase", std.RootMod1.UpperCase());
+    }
+    
+    function testUnqualifiedRootPackStdShadowed() {
+        var RootMod = 1;
+        Assert.equals("RootMod1.lowerCase", std.RootMod1.lowerCase());
+        Assert.equals("RootMod1.UpperCase", std.RootMod1.UpperCase());
+    }
+}

+ 9 - 0
tests/misc/resolution/projects/modulestatics/pack/shadow/Test.hx

@@ -0,0 +1,9 @@
+package pack.shadow;
+
+function lowerCase() {
+    return "pack.shadow.Test.lowerCase";
+}
+
+function UpperCase() {
+    return "pack.shadow.Test.UpperCase";
+}

+ 1 - 0
tests/misc/resolution/projects/spec/ModWithPrivate.hx

@@ -0,0 +1 @@
+private class A {}

+ 7 - 0
tests/misc/resolution/projects/spec/PrivateImport.hx

@@ -0,0 +1,7 @@
+import ModWithPrivate.A;
+
+class PrivateImport {
+	static function main() {
+		trace(A);
+	}
+}

+ 2 - 0
tests/misc/resolution/projects/spec/compile-private-fail.hxml

@@ -0,0 +1,2 @@
+-main PrivateImport
+-js privateFail.js

+ 2 - 0
tests/misc/resolution/projects/spec/compile-private-fail.hxml.stderr

@@ -0,0 +1,2 @@
+PrivateImport.hx:1: characters 8-24 : Importing private declarations from a module is not allowed
+PrivateImport.hx:5: characters 9-10 : Unknown identifier : A

+ 132 - 133
tests/unit/src/unit/TestMain.hx

@@ -5,150 +5,149 @@ import utest.Runner;
 import unit.Test.*;
 import haxe.ds.List;
 
+final asyncWaits = new Array<haxe.PosInfos>();
+final asyncCache = new Array<Void -> Void>();
+
 @:access(unit.Test)
-@:expose("unit.TestMain")
+#if js
+@:expose("unit.TestMain.main")
 @:keep
-class TestMain {
-
-	static var asyncWaits = new Array<haxe.PosInfos>();
-	static var asyncCache = new Array<Void -> Void>();
-
-	static function main() {
-		#if js
-		if (js.Browser.supported) {
-			var oTrace = haxe.Log.trace;
-			var traceElement = js.Browser.document.getElementById("haxe:trace");
-			haxe.Log.trace = function(v, ?infos) {
-				oTrace(v, infos);
-				traceElement.innerHTML += infos.fileName + ":" + infos.lineNumber + ": " + StringTools.htmlEscape(v) + "<br/>";
-			}
+#end
+function main() {
+	#if js
+	if (js.Browser.supported) {
+		var oTrace = haxe.Log.trace;
+		var traceElement = js.Browser.document.getElementById("haxe:trace");
+		haxe.Log.trace = function(v, ?infos) {
+			oTrace(v, infos);
+			traceElement.innerHTML += infos.fileName + ":" + infos.lineNumber + ": " + StringTools.htmlEscape(v) + "<br/>";
 		}
-		#end
+	}
+	#end
 
-		var verbose = #if ( cpp || neko || php ) Sys.args().indexOf("-v") >= 0 #else false #end;
+	var verbose = #if ( cpp || neko || php ) Sys.args().indexOf("-v") >= 0 #else false #end;
 
-		#if cs //"Turkey Test" - Issue #996
-		cs.system.threading.Thread.CurrentThread.CurrentCulture = new cs.system.globalization.CultureInfo('tr-TR');
-		cs.Lib.applyCultureChanges();
+	#if cs //"Turkey Test" - Issue #996
+	cs.system.threading.Thread.CurrentThread.CurrentCulture = new cs.system.globalization.CultureInfo('tr-TR');
+	cs.Lib.applyCultureChanges();
+	#end
+	#if neko
+	if( neko.Web.isModNeko )
+		neko.Web.setHeader("Content-Type","text/plain");
+	#elseif php
+	if( php.Web.isModNeko )
+		php.Web.setHeader("Content-Type","text/plain");
+	#end
+	#if !macro
+	trace("Generated at: " + HelperMacros.getCompilationDate());
+	#end
+	trace("START");
+	#if flash
+	var tf : flash.text.TextField = untyped flash.Boot.getTrace();
+	tf.selectable = true;
+	tf.mouseEnabled = true;
+	#end
+	var classes = [
+		new TestOps(),
+		new TestBasetypes(),
+		new TestExceptions(),
+		new TestBytes(),
+		new TestIO(),
+		new TestLocals(),
+		new TestEReg(),
+		new TestXML(),
+		new TestMisc(),
+		new TestJson(),
+		new TestResource(),
+		new TestInt64(),
+		new TestReflect(),
+		new TestSerialize(),
+		new TestSerializerCrossTarget(),
+		new TestMeta(),
+		new TestType(),
+		new TestOrder(),
+		new TestGADT(),
+		new TestGeneric(),
+		new TestArrowFunctions(),
+		new TestCasts(),
+		new TestSyntaxModule(),
+		new TestNull(),
+		new TestNumericCasts(),
+		new TestHashMap(),
+		#if (!no_http && (!azure || !(php && Windows)))
+		new TestHttp(),
+		#end
+		#if !no_pattern_matching
+		new TestMatch(),
+		#end
+		#if cs
+		new TestCSharp(),
+		#end
+		#if java
+		new TestJava(),
+		#end
+		#if lua
+		new TestLua(),
 		#end
-		#if neko
-		if( neko.Web.isModNeko )
-			neko.Web.setHeader("Content-Type","text/plain");
-		#elseif php
-		if( php.Web.isModNeko )
-			php.Web.setHeader("Content-Type","text/plain");
+		#if python
+		new TestPython(),
 		#end
-		#if !macro
-		trace("Generated at: " + HelperMacros.getCompilationDate());
+		#if hl
+		new TestHL(),
 		#end
-		trace("START");
-		#if flash
-		var tf : flash.text.TextField = untyped flash.Boot.getTrace();
-		tf.selectable = true;
-		tf.mouseEnabled = true;
+		#if php
+		new TestPhp(),
 		#end
-		var classes = [
-			new TestOps(),
-			new TestBasetypes(),
-			new TestExceptions(),
-			new TestBytes(),
-			new TestIO(),
-			new TestLocals(),
-			new TestEReg(),
-			new TestXML(),
-			new TestMisc(),
-			new TestJson(),
-			new TestResource(),
-			new TestInt64(),
-			new TestReflect(),
-			new TestSerialize(),
-			new TestSerializerCrossTarget(),
-			new TestMeta(),
-			new TestType(),
-			new TestOrder(),
-			new TestGADT(),
-			new TestGeneric(),
-			new TestArrowFunctions(),
-			new TestCasts(),
-			new TestSyntaxModule(),
-			new TestNull(),
-			new TestNumericCasts(),
-			new TestHashMap(),
-			#if (!no_http && (!azure || !(php && Windows)))
-			new TestHttp(),
-			#end
-			#if !no_pattern_matching
-			new TestMatch(),
-			#end
-			#if cs
-			new TestCSharp(),
-			#end
-			#if java
-			new TestJava(),
-			#end
-			#if lua
-			new TestLua(),
-			#end
-			#if python
-			new TestPython(),
-			#end
-			#if hl
-			new TestHL(),
-			#end
-			#if php
-			new TestPhp(),
-			#end
-			#if (java || cs)
-			new TestOverloads(),
-			#end
-			new TestInterface(),
-			new TestNaN(),
-			#if ((dce == "full") && !interp)
-			new TestDCE(),
-			#end
-			new TestMapComprehension(),
-			new TestMacro(),
-			new TestKeyValueIterator(),
-			new TestFieldVariance()
-			//new TestUnspecified(),
-			//new TestRemoting(),
-		];
+		#if (java || cs)
+		new TestOverloads(),
+		#end
+		new TestInterface(),
+		new TestNaN(),
+		#if ((dce == "full") && !interp)
+		new TestDCE(),
+		#end
+		new TestMapComprehension(),
+		new TestMacro(),
+		new TestKeyValueIterator(),
+		new TestFieldVariance()
+		//new TestUnspecified(),
+		//new TestRemoting(),
+	];
 
-		for (specClass in unit.UnitBuilder.generateSpec("src/unitstd")) {
-			classes.push(specClass);
-		}
-		TestIssues.addIssueClasses("src/unit/issues", "unit.issues");
-		TestIssues.addIssueClasses("src/unit/hxcpp_issues", "unit.hxcpp_issues");
+	for (specClass in unit.UnitBuilder.generateSpec("src/unitstd")) {
+		classes.push(specClass);
+	}
+	TestIssues.addIssueClasses("src/unit/issues", "unit.issues");
+	TestIssues.addIssueClasses("src/unit/hxcpp_issues", "unit.hxcpp_issues");
 
-		var runner = new Runner();
-		for (c in classes) {
-			runner.addCase(c);
-		}
-		var report = Report.create(runner);
-		report.displayHeader = AlwaysShowHeader;
-		report.displaySuccessResults = NeverShowSuccessResults;
-		var success = true;
-		runner.onProgress.add(function(e) {
-			for(a in e.result.assertations) {
-				switch a {
-					case Success(pos):
-					case Warning(msg):
-					case Ignore(reason):
-					case _: success = false;
-				}
+	var runner = new Runner();
+	for (c in classes) {
+		runner.addCase(c);
+	}
+	var report = Report.create(runner);
+	report.displayHeader = AlwaysShowHeader;
+	report.displaySuccessResults = NeverShowSuccessResults;
+	var success = true;
+	runner.onProgress.add(function(e) {
+		for(a in e.result.assertations) {
+			switch a {
+				case Success(pos):
+				case Warning(msg):
+				case Ignore(reason):
+				case _: success = false;
 			}
-			#if js
-			if (js.Browser.supported && e.totals == e.done) {
-				untyped js.Browser.window.success = success;
-			};
-			#end
-		});
-		#if sys
-		if (verbose)
-			runner.onTestStart.add(function(test) {
-				Sys.println(' $test...'); // TODO: need utest success state for this
-			});
+		}
+		#if js
+		if (js.Browser.supported && e.totals == e.done) {
+			untyped js.Browser.window.success = success;
+		};
 		#end
-		runner.run();
-	}
+	});
+	#if sys
+	if (verbose)
+		runner.onTestStart.add(function(test) {
+			Sys.println(' $test...'); // TODO: need utest success state for this
+		});
+	#end
+	runner.run();
 }

+ 109 - 0
tests/unit/src/unit/TestModuleStatics.hx

@@ -0,0 +1,109 @@
+package unit;
+
+class TestModuleStatics extends Test {
+	function testVars() {
+		eq("finalInit", finalInit);
+		eq("finalHintInit", finalHintInit);
+		eq("inlineFinalInit", inlineFinalInit);
+		eq("inlineFinalHintInit", inlineFinalHintInit);
+		eq("privateFinalInit", privateFinalInit);
+		eq("privateFinalHintInit", privateFinalHintInit);
+		eq("privateInlineFinalInit", privateInlineFinalInit);
+		eq("privateInlineFinalHintInit", privateInlineFinalHintInit);
+		eq("inlinePrivateFinalInit", inlinePrivateFinalInit);
+		eq("inlinePrivateFinalHintInit", inlinePrivateFinalHintInit);
+		eq("varInit", varInit);
+		eq("varInitHint", varInitHint);
+		eq(null, varHint);
+		eq("inlineVarInit", inlineVarInit);
+		eq("inlineVarInitHint", inlineVarInitHint);
+		eq("privateVarInit", privateVarInit);
+		eq("privateVarInitHint", privateVarInitHint);
+		eq(null, privateVarHint);
+		eq("privateInlineVarInit", privateInlineVarInit);
+		eq("privateInlineVarInitHint", privateInlineVarInitHint);
+		eq("inlinePrivateVarInit", inlinePrivateVarInit);
+		eq("inlinePrivateVarInitHint", inlinePrivateVarInitHint);
+
+
+		varInit = "1";
+		eq("1", varInit);
+		varInitHint = "2";
+		eq("2", varInitHint);
+		varHint = "3";
+		eq("3", varHint);
+		privateVarInit = "4";
+		eq("4", privateVarInit);
+		privateVarInitHint = "5";
+		eq("5", privateVarInitHint);
+		privateVarHint = "6";
+		eq("6", privateVarHint);
+	}
+
+	function testFuncs() {
+		eq("func", func());
+		eq("privateFunc", privateFunc());
+		eq("privateInlineFunc", privateInlineFunc());
+		eq("inlinePrivateFunc", inlinePrivateFunc());
+		eq("dynamicFunc", dynamicFunc());
+		eq("privateDynamicFunc", privateDynamicFunc());
+		eq("dynamicPrivateFunc", dynamicPrivateFunc());
+
+		dynamicFunc = () -> "1";
+		eq("1", dynamicFunc());
+		privateDynamicFunc = () -> "2";
+		eq("2", privateDynamicFunc());
+		dynamicPrivateFunc = () -> "3";
+		eq("3", dynamicPrivateFunc());
+	}
+
+	function testProps() {
+		eq("prop-get", prop);
+		prop = "hello";
+		eq("hello-set-get", prop);
+	}
+
+	function testMacroDefined() {
+		eq(42, mstatics.Funcs.funcA());
+		eq(43, mstatics.Funcs.funcB());
+		eq(44, mstatics.FuncC.FuncC());
+	}
+}
+
+// macro-define the functions
+typedef T = TestModuleStaticsMacro<Void>;
+
+final finalInit = "finalInit";
+final finalHintInit:String = "finalHintInit";
+inline final inlineFinalInit = "inlineFinalInit";
+inline final inlineFinalHintInit:String = "inlineFinalHintInit";
+private final privateFinalInit = "privateFinalInit";
+private final privateFinalHintInit:String = "privateFinalHintInit";
+private inline final privateInlineFinalInit = "privateInlineFinalInit";
+private inline final privateInlineFinalHintInit:String = "privateInlineFinalHintInit";
+inline private final inlinePrivateFinalInit = "inlinePrivateFinalInit";
+inline private final inlinePrivateFinalHintInit:String = "inlinePrivateFinalHintInit";
+var varInit = "varInit";
+var varInitHint:String = "varInitHint";
+var varHint:String;
+inline var inlineVarInit = "inlineVarInit";
+inline var inlineVarInitHint:String = "inlineVarInitHint";
+private var privateVarInit = "privateVarInit";
+private var privateVarInitHint:String = "privateVarInitHint";
+private var privateVarHint:String;
+private inline var privateInlineVarInit = "privateInlineVarInit";
+private inline var privateInlineVarInitHint:String = "privateInlineVarInitHint";
+inline private var inlinePrivateVarInit = "inlinePrivateVarInit";
+inline private var inlinePrivateVarInitHint:String = "inlinePrivateVarInitHint";
+
+function func() return "func";
+private function privateFunc() return "privateFunc";
+private inline function privateInlineFunc() return "privateInlineFunc";
+inline private function inlinePrivateFunc() return "inlinePrivateFunc";
+dynamic function dynamicFunc() return "dynamicFunc";
+private dynamic function privateDynamicFunc() return "privateDynamicFunc";
+dynamic private function dynamicPrivateFunc() return "dynamicPrivateFunc";
+
+@:isVar var prop(get,set):String = "prop";
+function get_prop() return prop + "-get";
+function set_prop(value) return prop = value + "-set";

+ 41 - 0
tests/unit/src/unit/TestModuleStaticsMacro.hx

@@ -0,0 +1,41 @@
+package unit;
+
+#if !macro
+@:genericBuild(unit.TestModuleStaticsMacro.build()) class TestModuleStaticsMacro<T> {}
+#else
+import haxe.macro.Context;
+
+class TestModuleStaticsMacro {
+	static function build() {
+		var pos = Context.currentPos();
+
+		// a bit awkward, but oh well
+		Context.defineModule("mstatics.Funcs", [
+			{
+				pos: pos,
+				name: "funcA",
+				kind: TDStatic(FFun({ret: macro : Int, args: [], expr: macro return 42})),
+				pack: ["mstatics"],
+				fields: []
+			},
+			{
+				pos: pos,
+				name: "funcB",
+				kind: TDStatic(FFun({ret: macro : Int, args: [], expr: macro return 43})),
+				pack: ["mstatics"],
+				fields: []
+			}
+		]);
+
+		Context.defineType({
+			pos: pos,
+			pack: ["mstatics"],
+			name: "FuncC",
+			kind: TDStatic(FFun({ret: macro : Int, args: [], expr: macro return 44})),
+			fields: []
+		});
+
+		return macro : Void;
+	}
+}
+#end