Browse Source

Support `abstract` modifier on classes and methods (#9716)

* wip

* start on abstract classes

* test the bad stuff

* don't show abstract classes in `new` completion

* try to get the terror twins to compile

* support `abstract` syntax at declaration-level

* support `abstract` syntax at field modifier level

* move java tests to java land

* add more errors

* support `abstract` class modifier in `TDClass`

* don't forget about `Access`

* more errors

* move `@:structInit` check to typeloading

* disallow expression-inline on abstract method

* disallow abstract + inline

* generate abstract methods and classes for Java and C#

* change test to break everything

* [php] generate "abstract" keywords

* try HL workaround

* make random change to maybe trigger CI

* try to get hxcpp working

* maybe this is better

* try random change to see if it fixes flash

* error on `super.abstractMethod()`

* don't generate `_dyn` if we don't need it

Co-authored-by: Aleksandr Kuzmenko <[email protected]>
Simon Krajewski 5 years ago
parent
commit
9434a59b63
77 changed files with 595 additions and 81 deletions
  1. 3 0
      src/core/ast.ml
  2. 1 0
      src/core/display/completionItem.ml
  3. 2 0
      src/core/error.ml
  4. 1 0
      src/core/tPrinting.ml
  5. 2 0
      src/core/tType.ml
  6. 44 13
      src/generators/gencpp.ml
  7. 7 3
      src/generators/gencs.ml
  8. 19 1
      src/generators/genhl.ml
  9. 6 2
      src/generators/genjava.ml
  10. 2 0
      src/generators/genjvm.ml
  11. 7 4
      src/generators/genphp7.ml
  12. 1 1
      src/generators/genswf9.ml
  13. 5 1
      src/macro/macroApi.ml
  14. 63 46
      src/syntax/grammar.mly
  15. 4 2
      src/syntax/reification.ml
  16. 4 0
      src/typing/calls.ml
  17. 3 0
      src/typing/fields.ml
  18. 1 1
      src/typing/typeload.ml
  19. 40 0
      src/typing/typeloadCheck.ml
  20. 26 3
      src/typing/typeloadFields.ml
  21. 5 0
      src/typing/typeloadModule.ml
  22. 10 1
      src/typing/typer.ml
  23. 6 1
      std/haxe/macro/Expr.hx
  24. 3 1
      std/haxe/macro/Printer.hx
  25. 1 1
      tests/misc/compile.hxml
  26. 22 0
      tests/misc/java/projects/Issue9619/missing-overload-implementation-from-parent-2/Main.hx
  27. 3 0
      tests/misc/java/projects/Issue9619/missing-overload-implementation-from-parent-2/compile-fail.hxml
  28. 3 0
      tests/misc/java/projects/Issue9619/missing-overload-implementation-from-parent-2/compile-fail.hxml.stderr
  29. 22 0
      tests/misc/java/projects/Issue9619/missing-overload-implementation-from-parent/Main.hx
  30. 3 0
      tests/misc/java/projects/Issue9619/missing-overload-implementation-from-parent/compile-fail.hxml
  31. 3 0
      tests/misc/java/projects/Issue9619/missing-overload-implementation-from-parent/compile-fail.hxml.stderr
  32. 19 0
      tests/misc/java/projects/Issue9619/missing-overload-implementation/Main.hx
  33. 3 0
      tests/misc/java/projects/Issue9619/missing-overload-implementation/compile-fail.hxml
  34. 3 0
      tests/misc/java/projects/Issue9619/missing-overload-implementation/compile-fail.hxml.stderr
  35. 16 0
      tests/misc/java/projects/Issue9619/missing-overload-implementations/Main.hx
  36. 3 0
      tests/misc/java/projects/Issue9619/missing-overload-implementations/compile-fail.hxml
  37. 4 0
      tests/misc/java/projects/Issue9619/missing-overload-implementations/compile-fail.hxml.stderr
  38. 9 0
      tests/misc/projects/Issue9619/abstract-dynamic/Main.hx
  39. 2 0
      tests/misc/projects/Issue9619/abstract-dynamic/compile-fail.hxml
  40. 1 0
      tests/misc/projects/Issue9619/abstract-dynamic/compile-fail.hxml.stderr
  41. 10 0
      tests/misc/projects/Issue9619/abstract-expression-inline/Main.hx
  42. 2 0
      tests/misc/projects/Issue9619/abstract-expression-inline/compile-fail.hxml
  43. 1 0
      tests/misc/projects/Issue9619/abstract-expression-inline/compile-fail.hxml.stderr
  44. 9 0
      tests/misc/projects/Issue9619/abstract-final-class/Main.hx
  45. 2 0
      tests/misc/projects/Issue9619/abstract-final-class/compile-fail.hxml
  46. 1 0
      tests/misc/projects/Issue9619/abstract-final-class/compile-fail.hxml.stderr
  47. 9 0
      tests/misc/projects/Issue9619/abstract-final/Main.hx
  48. 2 0
      tests/misc/projects/Issue9619/abstract-final/compile-fail.hxml
  49. 1 0
      tests/misc/projects/Issue9619/abstract-final/compile-fail.hxml.stderr
  50. 10 0
      tests/misc/projects/Issue9619/abstract-inline/Main.hx
  51. 2 0
      tests/misc/projects/Issue9619/abstract-inline/compile-fail.hxml
  52. 1 0
      tests/misc/projects/Issue9619/abstract-inline/compile-fail.hxml.stderr
  53. 7 0
      tests/misc/projects/Issue9619/abstract-interface/Main.hx
  54. 2 0
      tests/misc/projects/Issue9619/abstract-interface/compile-fail.hxml
  55. 1 0
      tests/misc/projects/Issue9619/abstract-interface/compile-fail.hxml.stderr
  56. 9 0
      tests/misc/projects/Issue9619/abstract-static/Main.hx
  57. 2 0
      tests/misc/projects/Issue9619/abstract-static/compile-fail.hxml
  58. 1 0
      tests/misc/projects/Issue9619/abstract-static/compile-fail.hxml.stderr
  59. 9 0
      tests/misc/projects/Issue9619/abstract-structInit/Main.hx
  60. 2 0
      tests/misc/projects/Issue9619/abstract-structInit/compile-fail.hxml
  61. 2 0
      tests/misc/projects/Issue9619/abstract-structInit/compile-fail.hxml.stderr
  62. 9 0
      tests/misc/projects/Issue9619/abstract-with-expression/Main.hx
  63. 2 0
      tests/misc/projects/Issue9619/abstract-with-expression/compile-fail.hxml
  64. 1 0
      tests/misc/projects/Issue9619/abstract-with-expression/compile-fail.hxml.stderr
  65. 12 0
      tests/misc/projects/Issue9619/constructing-abstract/Main.hx
  66. 2 0
      tests/misc/projects/Issue9619/constructing-abstract/compile-fail.hxml
  67. 1 0
      tests/misc/projects/Issue9619/constructing-abstract/compile-fail.hxml.stderr
  68. 7 0
      tests/misc/projects/Issue9619/missing-abstract-on-class/Main.hx
  69. 2 0
      tests/misc/projects/Issue9619/missing-abstract-on-class/compile-fail.hxml
  70. 2 0
      tests/misc/projects/Issue9619/missing-abstract-on-class/compile-fail.hxml.stderr
  71. 10 0
      tests/misc/projects/Issue9619/missing-implementation/Main.hx
  72. 2 0
      tests/misc/projects/Issue9619/missing-implementation/compile-fail.hxml
  73. 3 0
      tests/misc/projects/Issue9619/missing-implementation/compile-fail.hxml.stderr
  74. 13 0
      tests/misc/projects/Issue9619/super-abstract/Main.hx
  75. 2 0
      tests/misc/projects/Issue9619/super-abstract/compile-fail.hxml
  76. 1 0
      tests/misc/projects/Issue9619/super-abstract/compile-fail.hxml.stderr
  77. 59 0
      tests/unit/src/unit/issues/Issue9619.hx

+ 3 - 0
src/core/ast.ml

@@ -254,6 +254,7 @@ and access =
 	| AMacro
 	| AFinal
 	| AExtern
+	| AAbstract
 
 and placed_access = access * pos
 
@@ -290,6 +291,7 @@ type class_flag =
 	| HExtends of placed_type_path
 	| HImplements of placed_type_path
 	| HFinal
+	| HAbstract
 
 type abstract_flag =
 	| AbPrivate
@@ -431,6 +433,7 @@ let s_access = function
 	| AMacro -> "macro"
 	| AFinal -> "final"
 	| AExtern -> "extern"
+	| AAbstract -> "abstract"
 
 let s_placed_access (a,_) = s_access a
 

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

@@ -187,6 +187,7 @@ module CompletionModuleType = struct
 		in
 		let ctor c =
 			try
+				if has_class_flag c CAbstract then raise Not_found;
 				let _,cf = get_constructor (fun cf -> cf.cf_type) c in
 				if (has_class_flag c CExtern) || (has_class_field_flag cf CfPublic) then Yes else YesButPrivate
 			with Not_found ->

+ 2 - 0
src/core/error.ml

@@ -20,6 +20,7 @@ and error_msg =
 	| Stack of error_msg * error_msg
 	| Call_error of call_error
 	| No_constructor of module_type
+	| Abstract_class of module_type
 
 and type_not_found_reason =
 	| Private_type
@@ -277,6 +278,7 @@ let rec error_msg = function
 	| Stack (m1,m2) -> error_msg m1 ^ "\n" ^ error_msg m2
 	| Call_error err -> s_call_error err
 	| No_constructor mt -> (s_type_path (t_infos mt).mt_path ^ " does not have a constructor")
+	| Abstract_class mt -> (s_type_path (t_infos mt).mt_path) ^ " is abstract and cannot be constructed"
 
 and s_call_error = function
 	| Not_enough_arguments tl ->

+ 1 - 0
src/core/tPrinting.ml

@@ -650,6 +650,7 @@ module Printer = struct
 		| HExtends tp -> "HExtends " ^ (s_type_path (fst tp))
 		| HImplements tp -> "HImplements " ^ (s_type_path (fst tp))
 		| HFinal -> "HFinal"
+		| HAbstract -> "HAbstract"
 
 	let s_placed f (x,p) =
 		s_pair (f x) (s_pos p)

+ 2 - 0
src/core/tType.ml

@@ -382,6 +382,7 @@ type flag_tclass =
 	| CExtern
 	| CFinal
 	| CInterface
+	| CAbstract
 
 type flag_tclass_field =
 	| CfPublic
@@ -390,6 +391,7 @@ type flag_tclass_field =
 	| CfFinal
 	| CfModifiesThis (* This is set for methods which reassign `this`. E.g. `this = value` *)
 	| CfOverride
+	| CfAbstract
 
 type flag_tvar =
 	| VCaptured

+ 44 - 13
src/generators/gencpp.ml

@@ -4536,6 +4536,16 @@ let gen_field ctx class_def class_name ptr_name dot_name is_static is_interface
       if is_static && is_physical_field field then begin
          gen_type ctx field.cf_type;
          output ( " " ^ class_name ^ "::" ^ remap_name ^ ";\n\n");
+      end else if has_class_field_flag field CfAbstract then begin
+         let tl,tr = match follow field.cf_type with
+            | TFun(tl,tr) -> tl,tr
+            | _ -> die "" __LOC__
+         in
+         let nargs = string_of_int (List.length tl) in
+         let return_type = (cpp_type_of ctx tr ) in
+         let is_void = return_type = TCppVoid in
+         let ret = if is_void  then "(void)" else "return " in
+         output ("HX_DEFINE_DYNAMIC_FUNC" ^ nargs ^ "(" ^ class_name ^ "," ^ remap_name ^ "," ^ ret ^ ")\n\n");
       end
    )
    ;;
@@ -4620,13 +4630,13 @@ let gen_member_def ctx class_def is_static is_interface field =
    end else begin
       let decl = get_meta_string field.cf_meta Meta.Decl in
       let has_decl = decl <> "" in
+      let nonVirtual = has_meta_key field.cf_meta Meta.NonVirtual in
+      let doDynamic =  (nonVirtual || not (is_override field ) ) && (reflective class_def field ) in
       if (has_decl) then
          output ( "      typedef " ^ decl ^ ";\n" );
       output (if is_static then "\t\tstatic " else "\t\t");
       (match  field.cf_expr with
       | Some { eexpr = TFunction function_def } ->
-         let nonVirtual = has_meta_key field.cf_meta Meta.NonVirtual in
-         let doDynamic =  (nonVirtual || not (is_override field ) ) && (reflective class_def field ) in
          if ( is_dynamic_haxe_method field ) then begin
             if ( doDynamic ) then begin
                output ("::Dynamic " ^ remap_name ^ ";\n");
@@ -4657,6 +4667,23 @@ let gen_member_def ctx class_def is_static is_interface field =
             end;
          end;
          output "\n";
+      | _ when has_class_field_flag field CfAbstract ->
+         let ctx_arg_list ctx arg_list prefix =
+            String.concat "," (List.map (fun (n,o,t) -> (ctx_arg ctx n None t prefix) ) arg_list)
+         in
+         let tl,tr = match follow field.cf_type with
+            | TFun(tl,tr) -> tl,tr
+            | _ -> die "" __LOC__
+         in
+         let return_type = (ctx_type_string ctx tr) in
+         let remap_name = native_field_name_remap is_static field in
+         output "virtual ";
+         output (if return_type="Void" then "void" else return_type );
+         output (" " ^ remap_name ^ "(" );
+         output (ctx_arg_list ctx tl "" );
+         output ") = 0;\n";
+         if doDynamic then
+            output ("		::Dynamic " ^ remap_name ^ "_dyn();\n" );
       | _ when has_decl ->
          output ( remap_name ^ "_decl " ^ remap_name ^ ";\n" );
          (* Variable access *)
@@ -5332,6 +5359,7 @@ let is_abstract_impl class_def = match class_def.cl_kind with
 let variable_field field =
    (match field.cf_expr with
    | Some { eexpr = TFunction function_def } -> is_dynamic_haxe_method field
+   | None when has_class_field_flag field CfAbstract -> false
    | _ -> true)
 ;;
 
@@ -5814,14 +5842,15 @@ let generate_class_files baseCtx super_deps constructor_deps class_def inScripta
       );
 
       (* Destructor goes in the cpp file so we can "see" the full definition of the member vars *)
-      output_cpp ("Dynamic " ^ class_name ^ "::__CreateEmpty() { return new " ^ class_name ^ "; }\n\n");
-      output_cpp ("void *" ^ class_name ^ "::_hx_vtable = 0;\n\n");
-
-      output_cpp ("Dynamic " ^ class_name ^ "::__Create(::hx::DynamicArray inArgs)\n");
-      output_cpp ("{\n\t" ^ ptr_name ^ " _hx_result = new " ^ class_name ^ "();\n");
-      output_cpp ("\t_hx_result->__construct(" ^ (array_arg_list constructor_var_list) ^ ");\n");
-      output_cpp ("\treturn _hx_result;\n}\n\n");
-
+      if not (has_class_flag class_def CAbstract) then begin
+         output_cpp ("Dynamic " ^ class_name ^ "::__CreateEmpty() { return new " ^ class_name ^ "; }\n\n");
+         output_cpp ("void *" ^ class_name ^ "::_hx_vtable = 0;\n\n");
+
+         output_cpp ("Dynamic " ^ class_name ^ "::__Create(::hx::DynamicArray inArgs)\n");
+         output_cpp ("{\n\t" ^ ptr_name ^ " _hx_result = new " ^ class_name ^ "();\n");
+         output_cpp ("\t_hx_result->__construct(" ^ (array_arg_list constructor_var_list) ^ ");\n");
+         output_cpp ("\treturn _hx_result;\n}\n\n");
+      end;
       let rec addParent cls others = match cls.cl_super with
       | Some (super,_) -> ( try (
          let parentId = Hashtbl.find ctx.ctx_type_ids (class_text super.cl_path)  in
@@ -5965,7 +5994,7 @@ let generate_class_files baseCtx super_deps constructor_deps class_def inScripta
       output_cpp ("}\n");
    end;
 
-   if (not (has_class_flag class_def CInterface)) && not nativeGen && not inlineContructor then
+   if (not (has_class_flag class_def CInterface)) && not nativeGen && not inlineContructor && not (has_class_flag class_def CAbstract) then
       outputConstructor ctx output_cpp false;
 
 
@@ -6409,7 +6438,7 @@ let generate_class_files baseCtx super_deps constructor_deps class_def inScripta
    let class_name_text = join_class_path class_path "." in
 
    (* Initialise static in boot function ... *)
-   if (not (has_class_flag class_def CInterface) && not nativeGen) then begin
+   if (not (has_class_flag class_def CInterface) && not nativeGen) && not (has_class_flag class_def CAbstract) then begin
       (* Remap the specialised "extern" classes back to the generic names *)
       output_cpp ("::hx::Class " ^ class_name ^ "::__mClass;\n\n");
       if (scriptable) then begin
@@ -6604,7 +6633,9 @@ let generate_class_files baseCtx super_deps constructor_deps class_def inScripta
       output_h ("\t\t\t{ return ::hx::Object::operator new(inSize,inContainer,inName); }\n" );
       output_h ("\t\tinline void *operator new(size_t inSize, int extra)\n" );
       output_h ("\t\t\t{ return ::hx::Object::operator new(inSize+extra," ^ isContainer ^ "," ^ gcName ^ "); }\n" );
-      if inlineContructor then begin
+      if has_class_flag class_def CAbstract then
+         output_h "\n"
+      else if inlineContructor then begin
          output_h "\n";
          outputConstructor ctx (fun str -> output_h ("\t\t" ^ str) ) true
       end else begin

+ 7 - 3
src/generators/gencs.ml

@@ -2261,7 +2261,7 @@ let generate con =
 						| overloads -> overloads
 					in
 					List.iter (fun cf ->
-						if (has_class_flag cl CInterface) || cf.cf_expr <> None then
+						if (has_class_flag cl CInterface) || (has_class_flag cl CAbstract) || cf.cf_expr <> None then
 							gen_class_field w ~is_overload:true is_static cl (has_class_field_flag cf CfFinal) cf
 					) overloads;
 					let is_virtual = not is_final && match mkind with | MethInline -> false | _ when not is_new -> true | _ -> false in
@@ -2277,11 +2277,13 @@ let generate con =
 					in
 					let is_override = if Meta.has (Meta.Custom "?prop_impl") cf.cf_meta then false else is_override in
 
-					let is_virtual = is_virtual && not (has_class_flag cl CFinal) && not (is_interface) in
+					let is_abstract = has_class_field_flag cf CfAbstract in
+					let is_virtual = is_virtual && not (has_class_flag cl CFinal) && not (is_interface) && not is_abstract in
 					let visibility = if is_interface then "" else "public" in
 
 					let visibility, modifiers = get_fun_modifiers cf.cf_meta visibility [] in
 					let modifiers = modifiers @ modf in
+					let modifiers = if is_abstract then "abstract" :: modifiers else modifiers in
 					let visibility, is_virtual = if is_explicit_iface then "",false else if visibility = "private" then "private",false else visibility, is_virtual in
 					let v_n = if is_static then "static" else if is_override && not is_interface then "override" else if is_virtual then "virtual" else "" in
 					let cf_type = if is_override && not is_overload && not (Meta.has Meta.Overload cf.cf_meta) then match field_access gen (TInst(cl, List.map snd cl.cl_params)) cf.cf_name with | FClassField(_,_,_,_,_,actual_t,_) -> actual_t | _ -> die "" __LOC__ else cf.cf_type in
@@ -2299,7 +2301,7 @@ let generate con =
 					| _ ->
 							print w "%s(%s)%s" (params) (String.concat ", " (List.map (fun (name, _, t) -> sprintf "%s %s" (argt_s t) (change_id name)) args)) (params_ext)
 					);
-					if is_interface then
+					if is_interface || is_abstract then
 						write w ";"
 					else begin
 						write w " ";
@@ -2626,6 +2628,8 @@ let generate con =
 			let is_final = clt = "struct" || (has_class_flag cl CFinal) in
 
 			let modifiers = [access] @ modifiers in
+			let is_abstract = has_class_flag cl CAbstract in
+			let modifiers = if is_abstract then "abstract" :: modifiers else modifiers in
 			print w "%s %s %s" (String.concat " " modifiers) clt (change_clname (snd cl.cl_path));
 			(* type parameters *)
 			let params, params_ext = get_string_params cl cl.cl_params in

+ 19 - 1
src/generators/genhl.ml

@@ -3359,7 +3359,25 @@ let rec generate_member ctx c f =
 				| _ -> ()
 			) c.cl_ordered_fields;
 		) in
-		ignore(make_fun ?gen_content ctx (s_type_path c.cl_path,f.cf_name) (alloc_fid ctx c f) (match f.cf_expr with Some { eexpr = TFunction f } -> f | _ -> abort "Missing function body" f.cf_pos) (Some c) None);
+		let ff = match f.cf_expr with
+			| Some { eexpr = TFunction f } -> f
+			| None when has_class_field_flag f CfAbstract ->
+				let tl,tr = match follow f.cf_type with
+					| TFun(tl,tr) -> tl,tr
+					| _ -> die "" __LOC__
+				in
+				let args = List.map (fun (n,_,t) ->
+					let v = Type.alloc_var VGenerated n t null_pos in
+					(v,None)
+				) tl in
+				{
+					tf_args = args;
+					tf_type = tr;
+					tf_expr = mk (TThrow (mk (TConst TNull) t_dynamic null_pos)) t_dynamic null_pos;
+				}
+			| _ -> abort "Missing function body" f.cf_pos
+		in
+		ignore(make_fun ?gen_content ctx (s_type_path c.cl_path,f.cf_name) (alloc_fid ctx c f) ff (Some c) None);
 		if f.cf_name = "toString" && not (has_class_field_flag f CfOverride) && not (PMap.mem "__string" c.cl_fields) && is_to_string f.cf_type then begin
 			let p = f.cf_pos in
 			(* function __string() return this.toString().bytes *)

+ 6 - 2
src/generators/genjava.ml

@@ -1949,7 +1949,7 @@ let generate con =
 			| Var _ | Method MethDynamic -> ()
 			| Method mkind ->
 				List.iter (fun cf ->
-					if (has_class_flag cl CInterface) || cf.cf_expr <> None then
+					if (has_class_flag cl CInterface) || (has_class_flag cl CAbstract) || cf.cf_expr <> None then
 						gen_class_field w ~is_overload:true is_static cl (has_class_field_flag cf CfFinal) cf
 				) cf.cf_overloads;
 				let is_virtual = is_new || (not is_final && match mkind with | MethInline -> false | _ when not is_new -> true | _ -> false) in
@@ -1987,6 +1987,8 @@ let generate con =
 				let visibility = if is_interface then "" else "public" in
 
 				let visibility, modifiers = get_fun_modifiers cf.cf_meta visibility [] in
+				let is_abstract = has_class_field_flag cf CfAbstract in
+				let modifiers = if is_abstract then "abstract" :: modifiers else modifiers in
 				let visibility, is_virtual = if is_explicit_iface then "",false else visibility, is_virtual in
 				let v_n = if is_static then "static" else if is_override && not is_interface then "" else if not is_virtual then "final" else "" in
 				let cf_type = if is_override && not is_overload && not (Meta.has Meta.Overload cf.cf_meta) then match field_access gen (TInst(cl, List.map snd cl.cl_params)) cf.cf_name with | FClassField(_,_,_,_,_,actual_t,_) -> actual_t | _ -> die "" __LOC__ else cf.cf_type in
@@ -2012,7 +2014,7 @@ let generate con =
 					| _ ->
 							print w "(%s)" (String.concat ", " (List.map (fun (name, _, t) -> sprintf "%s %s" (argt_s cf.cf_pos (run_follow gen t)) (change_id name)) args))
 				);
-				if is_interface || List.mem "native" modifiers then
+				if is_interface || List.mem "native" modifiers || is_abstract then
 					write w ";"
 				else begin
 					let rec loop meta =
@@ -2109,7 +2111,9 @@ let generate con =
 
 		let clt, access, modifiers = get_class_modifiers cl.cl_meta (if (has_class_flag cl CInterface) then "interface" else "class") "public" [] in
 		let is_final = has_class_flag cl CFinal in
+		let is_abstract = has_class_flag cl CAbstract in
 		let modifiers = if is_final then "final" :: modifiers else modifiers in
+		let modifiers = if is_abstract then "abstract" :: modifiers else modifiers in
 
 		write_parts w (access :: modifiers @ [clt; (change_clname (snd cl.cl_path))]);
 

+ 2 - 0
src/generators/genjvm.ml

@@ -2174,6 +2174,7 @@ class tclass_to_jvm gctx c = object(self)
 			(* TODO: this should be done via Haxe metadata instead of hardcoding it here *)
 			jc#add_annotation retention_path ["value",(AEnum(retention_policy_sig,"RUNTIME"))];
 		end;
+		if (has_class_flag c CAbstract) then jc#add_access_flag 0x0400; (* abstract *)
 		if Meta.has Meta.JvmSynthetic c.cl_meta then jc#add_access_flag 0x1000 (* synthetic *)
 
 	method private handle_relation_type_params =
@@ -2375,6 +2376,7 @@ class tclass_to_jvm gctx c = object(self)
 		let flags = if has_class_field_flag cf CfFinal then MFinal :: flags else flags in
 		let flags = if Meta.has Meta.JvmSynthetic cf.cf_meta then MSynthetic :: flags else flags in
 		let flags = if Meta.has Meta.NativeJni cf.cf_meta then MNative :: flags else flags in
+		let flags = if (has_class_field_flag cf CfAbstract) then MAbstract :: flags else flags in
 		let name,scmode,flags = match mtype with
 			| MConstructor ->
 				let rec has_super_ctor c = match c.cl_super with

+ 7 - 4
src/generators/genphp7.ml

@@ -3114,10 +3114,10 @@ class virtual type_builder ctx (wrapper:type_wrapper) =
 		(**
 			Writes method to output buffer
 		*)
-		method private write_method name func is_static =
+		method private write_method name func is_static is_abstract =
 			match name with
 				| "__construct" -> self#write_constructor_declaration func
-				| _ -> self#write_method_declaration name func is_static
+				| _ -> self#write_method_declaration name func is_static is_abstract
 		(**
 			Writes constructor declaration (except visibility and `static` keywords) to output buffer
 		*)
@@ -3135,7 +3135,8 @@ class virtual type_builder ctx (wrapper:type_wrapper) =
 		(**
 			Writes method declaration (except visibility keywords) to output buffer
 		*)
-		method private write_method_declaration name func is_static =
+		method private write_method_declaration name func is_static is_abstract =
+			if is_abstract then writer#write "abstract ";
 			if is_static then writer#write "static ";
 			let by_ref = if is_ref func.tf_type then "&" else "" in
 			writer#write ("function " ^ by_ref ^ name ^ " (");
@@ -3453,6 +3454,7 @@ class class_builder ctx (cls:tclass) =
 		method private write_declaration =
 			self#write_doc (DocClass (gen_doc_text_opt cls.cl_doc));
 			if self#is_final then writer#write "final ";
+			if has_class_flag cls CAbstract then writer#write "abstract ";
 			writer#write (if (has_class_flag cls CInterface) then "interface " else "class ");
 			writer#write self#get_name;
 			(
@@ -3727,6 +3729,7 @@ class class_builder ctx (cls:tclass) =
 			self#write_doc (DocMethod (args, return_type, (gen_doc_text_opt field.cf_doc)));
 			writer#write_indentation;
 			if self#is_final_field field then writer#write "final ";
+			if has_class_field_flag field CfAbstract then writer#write "abstract ";
 			writer#write ((get_visibility field.cf_meta) ^ " ");
 			match field.cf_expr with
 				| None ->
@@ -3737,7 +3740,7 @@ class class_builder ctx (cls:tclass) =
 					writer#write " ;\n"
 				| Some { eexpr = TFunction fn } ->
 					let name = if field.cf_name = "new" then "__construct" else (field_name field) in
-					self#write_method name fn is_static;
+					self#write_method name fn is_static (has_class_field_flag field CfAbstract);
 					writer#write "\n"
 				| _ -> fail field.cf_pos __LOC__
 		(**

+ 1 - 1
src/generators/genswf9.ml

@@ -2090,7 +2090,7 @@ let generate_field_kind ctx f c stat =
 				hlm_kind = kind;
 			})
 		);
-	| _ when (has_class_flag c CInterface) && not stat ->
+	| _ when (has_class_flag c CInterface || has_class_field_flag f CfAbstract) && not stat ->
 		(match follow f.cf_type, f.cf_kind with
 		| TFun (args,tret), Method (MethNormal | MethInline) ->
 			let dparams = ref None in

+ 5 - 1
src/macro/macroApi.ml

@@ -314,6 +314,7 @@ and encode_access a =
 		| AMacro -> 6
 		| AFinal -> 7
 		| AExtern -> 8
+		| AAbstract -> 9
 	in
 	encode_enum ~pos:(Some (pos a)) IAccess tag []
 
@@ -664,6 +665,7 @@ and decode_access v =
 	| 6 -> AMacro
 	| 7 -> AFinal
 	| 8 -> AExtern
+	| 9 -> AAbstract
 	| _ -> raise Invalid_expr
 	in
 	a,p
@@ -1458,10 +1460,11 @@ let decode_type_def v =
 		EEnum (mk (if isExtern then [EExtern] else []) (List.map conv fields))
 	| 1, [] ->
 		ETypedef (mk (if isExtern then [EExtern] else []) (CTAnonymous fields,Globals.null_pos))
-	| 2, [ext;impl;interf;final] ->
+	| 2, [ext;impl;interf;final;abstract] ->
 		let flags = if isExtern then [HExtern] else [] in
 		let is_interface = decode_opt_bool interf in
 		let is_final = decode_opt_bool final in
+		let is_abstract = decode_opt_bool abstract in
 		let interfaces = (match opt (fun v -> List.map decode_path (decode_array v)) impl with Some l -> l | _ -> [] ) in
 		let flags = (match opt decode_path ext with None -> flags | Some t -> HExtends t :: flags) in
 		let flags = if is_interface then begin
@@ -1472,6 +1475,7 @@ let decode_type_def v =
 			end
 		in
 		let flags = if is_final then HFinal :: flags else flags in
+		let flags = if is_abstract then HAbstract :: flags else flags in
 		EClass (mk flags fields)
 	| 3, [t] ->
 		ETypedef (mk (if isExtern then [EExtern] else []) (decode_ctype t))

+ 63 - 46
src/syntax/grammar.mly

@@ -157,8 +157,8 @@ and parse_type_decls mode pmax pack acc s =
 		ignore(resume false false s);
 		parse_type_decls mode (last_pos s).pmax pack acc s
 
-and parse_abstract doc meta flags = parser
-	| [< '(Kwd Abstract,p1); name = type_name; tl = parse_constraint_params; st = parse_abstract_subtype; sl = plist parse_abstract_relations; s >] ->
+and parse_abstract doc meta flags p1 = parser
+	| [< name = type_name; tl = parse_constraint_params; st = parse_abstract_subtype; sl = plist parse_abstract_relations; s >] ->
 		let fl,p2 = match s with parser
 			| [< '(BrOpen,_); fl, p2 = parse_class_fields false p1 >] -> fl,p2
 			| [< >] -> syntax_error (Expected ["{";"to";"from"]) s ([],last_pos s)
@@ -174,6 +174,50 @@ and parse_abstract doc meta flags = parser
 			d_data = fl;
 		},punion p1 p2)
 
+and parse_class_content doc meta flags n p1 s =
+	let name = type_name s in
+	let tl = parse_constraint_params s in
+	let rec loop had_display p0 acc =
+		let check_display p1 =
+			if not had_display && !in_display_file && display_position#enclosed_in p1 then
+				syntax_completion (if List.mem HInterface n then SCInterfaceRelation else SCClassRelation) None (display_position#with_pos p1)
+		in
+		match s with parser
+		| [< '(Kwd Extends,p1); t,b = parse_type_path_or_resume p1 >] ->
+			check_display {p1 with pmin = p0.pmax; pmax = p1.pmin};
+			let p0 = pos t in
+			(* If we don't have type parameters, we have to offset by one so to not complete `extends`
+				and `implements` after the identifier. *)
+			let p0 = {p0 with pmax = p0.pmax + (if (fst t).tparams = [] then 1 else 0)} in
+			loop (had_display || b) p0 ((HExtends t) :: acc)
+		| [< '(Kwd Implements,p1); t,b = parse_type_path_or_resume p1 >] ->
+			check_display {p1 with pmin = p0.pmax; pmax = p1.pmin};
+			let p0 = pos t in
+			let p0 = {p0 with pmax = p0.pmax + (if (fst t).tparams = [] then 1 else 0)} in
+			loop (had_display || b) p0 ((HImplements t) :: acc)
+		| [< '(BrOpen,p1) >] ->
+			check_display {p1 with pmin = p0.pmax; pmax = p1.pmin};
+			List.rev acc
+		| [< >] ->
+			begin match Stream.peek s with
+			| Some((Const(Ident name),p)) when display_position#enclosed_in p ->
+				syntax_completion (if List.mem HInterface n then SCInterfaceRelation else SCClassRelation) (Some name) p
+			| _ ->
+				check_display {p1 with pmin = p0.pmax; pmax = (next_pos s).pmax};
+				syntax_error (Expected ["extends";"implements";"{"]) s (List.rev acc)
+			end
+	in
+	let hl = loop false (last_pos s) [] in
+	let fl, p2 = parse_class_fields false p1 s in
+	(EClass {
+		d_name = name;
+		d_doc = doc_from_string_opt doc;
+		d_meta = meta;
+		d_params = tl;
+		d_flags = ExtList.List.filter_map decl_flag_to_class_flag flags @ n @ hl;
+		d_data = fl;
+	}, punion p1 p2)
+
 and parse_type_decl mode s =
 	match s with parser
 	| [< '(Kwd Import,p1) >] -> parse_import s p1
@@ -223,7 +267,7 @@ and parse_type_decl mode s =
 			}, punion p1 p2)
 		| [< '(Kwd Enum,p1) >] ->
 			begin match s with parser
-			| [< a,p = parse_abstract doc ((Meta.Enum,[],null_pos) :: meta) c >] ->
+			| [< '(Kwd Abstract,p1); a,p = parse_abstract doc ((Meta.Enum,[],null_pos) :: meta) c p1 >] ->
 				(EAbstract a,p)
 			| [< name = type_name; tl = parse_constraint_params; '(BrOpen,_); l = plist parse_enum; '(BrClose,p2) >] ->
 				(EEnum {
@@ -235,47 +279,8 @@ and parse_type_decl mode s =
 					d_data = l
 				}, punion p1 p2)
 			end
-		| [< n , p1 = parse_class_flags; name = type_name; tl = parse_constraint_params >] ->
-			let rec loop had_display p0 acc =
-				let check_display p1 =
-					if not had_display && !in_display_file && display_position#enclosed_in p1 then
-						syntax_completion (if List.mem HInterface n then SCInterfaceRelation else SCClassRelation) None (display_position#with_pos p1)
-				in
-				match s with parser
-				| [< '(Kwd Extends,p1); t,b = parse_type_path_or_resume p1 >] ->
-					check_display {p1 with pmin = p0.pmax; pmax = p1.pmin};
-					let p0 = pos t in
-					(* If we don't have type parameters, we have to offset by one so to not complete `extends`
-					   and `implements` after the identifier. *)
-					let p0 = {p0 with pmax = p0.pmax + (if (fst t).tparams = [] then 1 else 0)} in
-					loop (had_display || b) p0 ((HExtends t) :: acc)
-				| [< '(Kwd Implements,p1); t,b = parse_type_path_or_resume p1 >] ->
-					check_display {p1 with pmin = p0.pmax; pmax = p1.pmin};
-					let p0 = pos t in
-					let p0 = {p0 with pmax = p0.pmax + (if (fst t).tparams = [] then 1 else 0)} in
-					loop (had_display || b) p0 ((HImplements t) :: acc)
-				| [< '(BrOpen,p1) >] ->
-					check_display {p1 with pmin = p0.pmax; pmax = p1.pmin};
-					List.rev acc
-				| [< >] ->
-					begin match Stream.peek s with
-					| Some((Const(Ident name),p)) when display_position#enclosed_in p ->
-						syntax_completion (if List.mem HInterface n then SCInterfaceRelation else SCClassRelation) (Some name) p
-					| _ ->
-						check_display {p1 with pmin = p0.pmax; pmax = (next_pos s).pmax};
-						syntax_error (Expected ["extends";"implements";"{"]) s (List.rev acc)
-					end
-			in
-			let hl = loop false (last_pos s) [] in
-			let fl, p2 = parse_class_fields false p1 s in
-			(EClass {
-				d_name = name;
-				d_doc = doc_from_string_opt doc;
-				d_meta = meta;
-				d_params = tl;
-				d_flags = ExtList.List.filter_map decl_flag_to_class_flag c @ n @ hl;
-				d_data = fl;
-			}, punion p1 p2)
+		| [< n , p1 = parse_class_flags >] ->
+			parse_class_content doc meta c n p1 s
 		| [< '(Kwd Typedef,p1); name = type_name; tl = parse_constraint_params; '(Binop OpAssign,p2); t = parse_complex_type_at p2; s >] ->
 			(match s with parser
 			| [< '(Semicolon,_) >] -> ()
@@ -288,8 +293,19 @@ and parse_type_decl mode s =
 				d_flags = ExtList.List.filter_map decl_flag_to_enum_flag c;
 				d_data = t;
 			}, punion p1 (pos t))
-		| [< a,p = parse_abstract doc meta c >] ->
-			EAbstract a,p
+		| [< '(Kwd Abstract,p1) >] ->
+			begin match s with parser
+			| [< a,p = parse_abstract doc meta c p1 >] ->
+				EAbstract a,p
+			| [< >] ->
+				let c2 = parse_common_flags s in
+				begin match s with parser
+				| [< flags,_ = parse_class_flags >] ->
+					parse_class_content doc meta (c @ c2) (HAbstract :: flags) p1 s
+				| [< >] ->
+					serror()
+				end
+			end
 		| [< >] ->
 			match List.rev c with
 			| (DFinal,p1) :: crest ->
@@ -963,6 +979,7 @@ and parse_cf_rights = parser
 	| [< '(Kwd Dynamic,p) >] -> ADynamic,p
 	| [< '(Kwd Inline,p) >] -> AInline,p
 	| [< '(Kwd Extern,p) >] -> AExtern,p
+	| [< '(Kwd Abstract,p) >] -> AAbstract,p
 
 and parse_fun_name = parser
 	| [< name,p = dollar_ident >] -> name,p

+ 4 - 2
src/syntax/reification.ml

@@ -182,6 +182,7 @@ let reify in_macro =
 			| AMacro -> "AMacro"
 			| AFinal -> "AFinal"
 			| AExtern -> "AExtern"
+			| AAbstract -> "AAbstract"
 			) in
 			mk_enum "Access" n [] p
 		in
@@ -372,7 +373,7 @@ let reify in_macro =
 	and to_type_def (t,p) =
 		match t with
 		| EClass d ->
-			let ext = ref None and impl = ref [] and interf = ref false and final = ref false in
+			let ext = ref None and impl = ref [] and interf = ref false and final = ref false and abstract = ref false in
 			List.iter (function
 				| HExtern | HPrivate -> ()
 				| HInterface -> interf := true;
@@ -384,6 +385,7 @@ let reify in_macro =
 						end)
 				| HImplements i-> impl := (to_tpath i p) :: !impl
 				| HFinal -> final := true
+				| HAbstract -> abstract := true
 			) d.d_flags;
 			to_obj [
 				"pack", (EArrayDecl [],p);
@@ -392,7 +394,7 @@ let reify in_macro =
 				"meta", to_meta d.d_meta p;
 				"params", (EArrayDecl (List.map (to_tparam_decl p) d.d_params),p);
 				"isExtern", to_bool (List.mem HExtern d.d_flags) p;
-				"kind", mk_enum "TypeDefKind" "TDClass" [(match !ext with None -> (EConst (Ident "null"),p) | Some t -> t);(EArrayDecl (List.rev !impl),p);to_bool !interf p;to_bool !final p] p;
+				"kind", mk_enum "TypeDefKind" "TDClass" [(match !ext with None -> (EConst (Ident "null"),p) | Some t -> t);(EArrayDecl (List.rev !impl),p);to_bool !interf p;to_bool !final p;to_bool !abstract p] p;
 				"fields", (EArrayDecl (List.map (fun f -> to_cfield f p) d.d_data),p)
 			] p
 		| _ -> die "" __LOC__

+ 4 - 0
src/typing/calls.ml

@@ -729,6 +729,10 @@ let rec build_call ?(mode=MGet) ctx acc el (with_type:WithType.t) p =
 							type_generic_function ctx (e1,fa) el with_type p
 						| _ ->
 							let fcc = unify_field_call ctx fa el args r p false in
+							if has_class_field_flag fcc.fc_field CfAbstract then begin match e1.eexpr with
+								| TConst TSuper -> display_error ctx (Printf.sprintf "abstract method %s cannot be accessed directly" fcc.fc_field.cf_name) p;
+								| _ -> ()
+							end;
 							fcc.fc_data e1 e.epos false
 					end
 				| _ ->

+ 3 - 0
src/typing/fields.ml

@@ -71,6 +71,9 @@ let field_type ctx c pl f p =
 		let monos = Monomorph.spawn_constrained_monos (if pl = [] then (fun t -> t) else apply_params c.cl_params pl) f.cf_params in
 		apply_params l monos f.cf_type
 
+let no_abstract_constructor c p =
+	if has_class_flag c CAbstract then raise_error (Abstract_class (TClassDecl c)) p
+
 let get_constructor ctx c params p =
 	match c.cl_kind with
 	| KAbstractImpl a ->

+ 1 - 1
src/typing/typeload.ml

@@ -544,7 +544,7 @@ and load_complex_type' ctx allow_display (t,p) =
 					pub := false;
 				| ADynamic when (match f.cff_kind with FFun _ -> true | _ -> false) -> dyn := true
 				| AFinal -> final := true
-				| AStatic | AOverride | AInline | ADynamic | AMacro | AExtern as a -> error ("Invalid access " ^ Ast.s_access a) p
+				| AStatic | AOverride | AInline | ADynamic | AMacro | AExtern | AAbstract as a -> error ("Invalid access " ^ Ast.s_access a) p
 			) f.cff_access;
 			let t , access = (match f.cff_kind with
 				| FVar(t,e) when !final ->

+ 40 - 0
src/typing/typeloadCheck.ml

@@ -399,6 +399,44 @@ module Inheritance = struct
 		| _ ->
 		List.iter (fun (intf,params) -> check_interface ctx c intf params) c.cl_implements
 
+	let check_abstract_class ctx c csup params =
+		let missing = ref [] in
+		let check_abstract_class_field cf1 t1 =
+			try
+				let cf2 = PMap.find cf1.cf_name c.cl_fields in
+				if not (List.exists (fun cf2 ->
+					Overloads.same_overload_args t1 cf2.cf_type cf1 cf2
+				) (cf2 :: cf2.cf_overloads)) then
+					missing := cf1 :: !missing
+			with Not_found ->
+				missing := cf1 :: !missing
+		in
+		let cfl = TClass.get_all_fields csup params in
+		PMap.iter (fun _ (_,cf) ->
+			let cfl = Overloads.collect_overloads csup cf.cf_name in
+			List.iter (fun (t,cf) ->
+				if (has_class_field_flag cf CfAbstract) then
+					check_abstract_class_field cf t
+			) cfl
+		) cfl;
+		match !missing with
+		| [] ->
+			()
+		| l ->
+			let singular = match l with [_] -> true | _ -> false in
+			display_error ctx (Printf.sprintf "This class extends abstract class %s but doesn't implement the following method%s" (s_type_path csup.cl_path) (if singular then "" else "s")) c.cl_name_pos;
+			display_error ctx (Printf.sprintf "Implement %s or make %s abstract as well" (if singular then "it" else "them") (s_type_path c.cl_path)) c.cl_name_pos;
+			let pctx = print_context() in
+			List.iter (fun cf ->
+				let s = match follow cf.cf_type with
+					| TFun(tl,tr) ->
+						String.concat ", " (List.map (fun (n,o,t) -> Printf.sprintf "%s:%s" n (s_type pctx t)) tl)
+					| t ->
+						s_type pctx t
+				in
+				display_error ctx (Printf.sprintf "... %s(%s)" cf.cf_name s) cf.cf_name_pos
+			) (List.rev !missing)
+
 	let set_heritance ctx c herits p =
 		let is_lib = Meta.has Meta.LibType c.cl_meta in
 		let ctx = { ctx with curclass = c; type_params = c.cl_params; } in
@@ -463,6 +501,8 @@ module Inheritance = struct
 					end
 				end else begin
 					if (has_class_flag csup CInterface) then error "Cannot extend by using an interface" p;
+					if (has_class_flag csup CAbstract) && not (has_class_flag c CAbstract) then
+						delay ctx PForce (fun () -> check_abstract_class ctx c csup params);
 					c.cl_super <- Some (csup,params)
 				end;
 				(fun () ->

+ 26 - 3
src/typing/typeloadFields.ml

@@ -67,6 +67,7 @@ type field_init_ctx = {
 	is_static : bool;
 	override : pos option;
 	is_extern : bool;
+	is_abstract : bool;
 	is_macro : bool;
 	is_abstract_member : bool;
 	is_display_field : bool;
@@ -554,6 +555,7 @@ let create_field_context (ctx,cctx) c cff =
 	let display_modifier = Typeload.check_field_access ctx cff in
 	let is_static = List.mem_assoc AStatic cff.cff_access in
 	let is_extern = ref (List.mem_assoc AExtern cff.cff_access) in
+	let is_abstract = List.mem_assoc AAbstract cff.cff_access in
 	let is_final = ref (List.mem_assoc AFinal cff.cff_access) in
 	List.iter (fun (m,_,p) ->
 		match m with
@@ -569,6 +571,19 @@ let create_field_context (ctx,cctx) c cff =
 			()
 	) cff.cff_meta;
 	let is_inline = List.mem_assoc AInline cff.cff_access in
+	if is_abstract then begin
+		if is_static then
+			display_error ctx "Static methods may not be abstract" (pos cff.cff_name)
+		else if !is_final then
+			display_error ctx "Abstract methods may not be final" (pos cff.cff_name)
+		else if is_inline then
+			display_error ctx "Abstract methods may not be inline" (pos cff.cff_name)
+		else if not (has_class_flag c CAbstract) then begin
+			display_error ctx "This class should be declared abstract because it has at least one abstract field" c.cl_name_pos;
+			display_error ctx "First abstract field was here" (pos cff.cff_name);
+			add_class_flag c CAbstract;
+		end;
+	end;
 	let override = try Some (List.assoc AOverride cff.cff_access) with Not_found -> None in
 	let is_macro = List.mem_assoc AMacro cff.cff_access in
 	let field_kind = match fst cff.cff_name with
@@ -582,13 +597,14 @@ let create_field_context (ctx,cctx) c cff =
 		override = override;
 		is_macro = is_macro;
 		is_extern = !is_extern;
+		is_abstract = is_abstract;
 		is_final = !is_final;
 		is_display_field = ctx.is_display_file && DisplayPosition.display_position#enclosed_in cff.cff_pos;
 		is_field_debug = cctx.is_class_debug || Meta.has (Meta.Custom ":debug.typeload") cff.cff_meta;
 		display_modifier = display_modifier;
 		is_abstract_member = cctx.abstract <> None && Meta.has Meta.Impl cff.cff_meta;
 		field_kind = field_kind;
-		do_bind = (((not ((has_class_flag c CExtern) || !is_extern) || is_inline) && not (has_class_flag c CInterface)) || field_kind = FKInit);
+		do_bind = (((not ((has_class_flag c CExtern) || !is_extern) || is_inline) && not is_abstract && not (has_class_flag c CInterface)) || field_kind = FKInit);
 		do_add = true;
 		expr_presence_matters = false;
 	} in
@@ -1106,6 +1122,7 @@ let create_method (ctx,cctx,fctx) c f fd p =
 	end;
 	let parent = (if not fctx.is_static then get_parent c (fst f.cff_name) else None) in
 	let dynamic = List.mem_assoc ADynamic f.cff_access || (match parent with Some { cf_kind = Method MethDynamic } -> true | _ -> false) in
+	if fctx.is_abstract && dynamic then display_error ctx "Abstract methods may not be dynamic" p;
 	if fctx.is_inline && dynamic then error (fst f.cff_name ^ ": 'inline' is not allowed on 'dynamic' functions") p;
 	let is_override = Option.is_some fctx.override in
 	if (is_override && fctx.is_static) then error (fst f.cff_name ^ ": 'override' is not allowed on 'static' functions") p;
@@ -1141,6 +1158,7 @@ let create_method (ctx,cctx,fctx) c f fd p =
 	} in
 	if fctx.is_final then add_class_field_flag cf CfFinal;
 	if fctx.is_extern then add_class_field_flag cf CfExtern;
+	if fctx.is_abstract then add_class_field_flag cf CfAbstract;
 	cf.cf_meta <- List.map (fun (m,el,p) -> match m,el with
 		| Meta.AstSource,[] -> (m,(match fd.f_expr with None -> [] | Some e -> [e]),p)
 		| _ -> m,el,p
@@ -1224,7 +1242,10 @@ let create_method (ctx,cctx,fctx) c f fd p =
 			) args fd.f_args;
 		);
 		check_field_display ctx fctx c cf;
-		if fd.f_expr <> None && not (fctx.is_inline || fctx.is_macro) then ctx.com.warning "Extern non-inline function may not have an expression" p;
+		if fd.f_expr <> None then begin
+			if fctx.is_abstract then display_error ctx "Abstract methods may not have an expression" p
+			else if not (fctx.is_inline || fctx.is_macro) then ctx.com.warning "Extern non-inline function may not have an expression" p;
+		end;
 	end;
 	cf
 
@@ -1385,7 +1406,7 @@ let init_field (ctx,cctx,fctx) f =
 	List.iter (fun acc ->
 		match (fst acc, f.cff_kind) with
 		| APublic, _ | APrivate, _ | AStatic, _ | AFinal, _ | AExtern, _ -> ()
-		| ADynamic, FFun _ | AOverride, FFun _ | AMacro, FFun _ | AInline, FFun _ | AInline, FVar _ -> ()
+		| ADynamic, FFun _ | AOverride, FFun _ | AMacro, FFun _ | AInline, FFun _ | AInline, FVar _ | AAbstract, FFun _-> ()
 		| _, FVar _ -> display_error ctx ("Invalid accessor '" ^ Ast.s_placed_access acc ^ "' for variable " ^ name) (snd acc)
 		| _, FProp _ -> display_error ctx ("Invalid accessor '" ^ Ast.s_placed_access acc ^ "' for property " ^ name) (snd acc)
 	) f.cff_access;
@@ -1609,6 +1630,8 @@ let init_class ctx c p context_init herits fields =
 	if has_struct_init then
 		if (has_class_flag c CInterface) then
 			display_error ctx "@:structInit is not allowed on interfaces" struct_init_pos
+		else if (has_class_flag c CAbstract) then
+			display_error ctx "@:structInit is not allowed on abstract classes" struct_init_pos
 		else
 			ensure_struct_init_constructor ctx c fields p;
 	begin match cctx.uninitialized_final with

+ 5 - 0
src/typing/typeloadModule.ml

@@ -239,6 +239,7 @@ let module_pass_1 ctx m tdecls loadp =
 			c.cl_private <- priv;
 			c.cl_doc <- d.d_doc;
 			c.cl_meta <- d.d_meta;
+			if List.mem HAbstract d.d_flags then add_class_flag c CAbstract;
 			List.iter (function
 				| HExtern -> add_class_flag c CExtern
 				| HInterface -> add_class_flag c CInterface
@@ -246,6 +247,10 @@ let module_pass_1 ctx m tdecls loadp =
 				| _ -> ()
 			) d.d_flags;
 			if not (has_class_flag c CExtern) then check_type_name name d.d_meta;
+			if has_class_flag c CAbstract then begin
+				if has_class_flag c CInterface then display_error ctx "An interface may not be abstract" c.cl_name_pos;
+				if has_class_flag c CFinal then display_error ctx "An abstract class may not be final" c.cl_name_pos;
+			end;
 			decls := (TClassDecl c, decl) :: !decls;
 			acc
 		| EEnum d ->

+ 10 - 1
src/typing/typer.ml

@@ -1409,6 +1409,7 @@ and type_access ctx e p mode =
 				if mode = MCall then error ("Cannot call constructor like this, use 'new " ^ (s_type_path c.cl_path) ^ "()' instead") p;
 				let monos = Monomorph.spawn_constrained_monos (fun t -> t) (match c.cl_kind with KAbstractImpl a -> a.a_params | _ -> c.cl_params) in
 				let ct, cf = get_constructor ctx c monos p in
+				no_abstract_constructor c p;
 				check_constructor_access ctx c cf p;
 				let args = match follow ct with TFun(args,ret) -> args | _ -> die "" __LOC__ in
 				let vl = List.map (fun (n,_,t) -> alloc_var VGenerated n t c.cl_pos) args in
@@ -1808,6 +1809,7 @@ and type_new ctx path el with_type force_inline p =
 		| TClassDecl ({cl_constructor = Some cf} as c) ->
 			let monos = Monomorph.spawn_constrained_monos (fun t -> t) c.cl_params in
 			let ct, f = get_constructor ctx c monos p in
+			no_abstract_constructor c p;
 			ignore (unify_constructor_call c monos f ct);
 			begin try
 				Generic.build_generic ctx c p monos
@@ -1834,6 +1836,7 @@ and type_new ctx path el with_type force_inline p =
 	let t = follow t in
 	let build_constructor_call c tl =
 		let ct, f = get_constructor ctx c tl p in
+		no_abstract_constructor c p;
 		check_constructor_access ctx c f p;
 		(match f.cf_kind with
 		| Var { v_read = AccRequire (r,msg) } -> (match msg with Some msg -> error msg p | None -> error_require r p)
@@ -2393,15 +2396,21 @@ and type_meta ?(mode=MGet) ctx m e1 with_type p =
 
 and type_call_target ctx e with_type inline p =
 	let e = maybe_type_against_enum ctx (fun () -> type_access ctx (fst e) (snd e) MCall) with_type true p in
+	let check_inline cf =
+		if (has_class_field_flag cf CfAbstract) then display_error ctx "Cannot force inline on abstract method" p
+	in
 	if not inline then
 		e
 	else match e with
 		| AKExpr {eexpr = TField(e1,fa); etype = t} ->
 			begin match extract_field fa with
-			| Some cf -> AKInline(e1,cf,fa,t)
+			| Some cf ->
+				check_inline cf;
+				AKInline(e1,cf,fa,t)
 			| None -> e
 			end;
 		| AKUsing(e,c,cf,ef,_) ->
+			check_inline cf;
 			AKUsing(e,c,cf,ef,true)
 		| AKExpr {eexpr = TLocal _} ->
 			display_error ctx "Cannot force inline on local functions" p;

+ 6 - 1
std/haxe/macro/Expr.hx

@@ -857,6 +857,11 @@ enum Access {
 		Extern access modifier.
 	**/
 	AExtern;
+
+	/**
+		Abstract access modifier.
+	**/
+	AAbstract;
 }
 
 /**
@@ -947,7 +952,7 @@ enum TypeDefKind {
 	/**
 		Represents a class kind.
 	**/
-	TDClass(?superClass:TypePath, ?interfaces:Array<TypePath>, ?isInterface:Bool, ?isFinal:Bool);
+	TDClass(?superClass:TypePath, ?interfaces:Array<TypePath>, ?isInterface:Bool, ?isFinal:Bool, ?isAbstract:Bool);
 
 	/**
 		Represents an alias/typedef kind.

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

@@ -159,6 +159,7 @@ class Printer {
 			case AMacro: "macro";
 			case AFinal: "final";
 			case AExtern: "extern";
+			case AAbstract: "abstract";
 		}
 
 	public function printField(field:Field) {
@@ -344,8 +345,9 @@ class Printer {
 						}
 					].join("\n")
 					+ "\n}";
-				case TDClass(superClass, interfaces, isInterface, isFinal):
+				case TDClass(superClass, interfaces, isInterface, isFinal, isAbstract):
 					(isFinal ? "final " : "")
+						+ (isAbstract ? "abstract " : "")
 						+ (isInterface ? "interface " : "class ")
 						+ t.name
 						+ (t.params != null && t.params.length > 0 ? "<" + t.params.map(printTypeParamDecl).join(", ") + ">" : "")

+ 1 - 1
tests/misc/compile.hxml

@@ -1,4 +1,4 @@
 -p src
-# -D MISC_TEST_FILTER=6790
+#-D MISC_TEST_FILTER=9619
 -main Main
 --interp

+ 22 - 0
tests/misc/java/projects/Issue9619/missing-overload-implementation-from-parent-2/Main.hx

@@ -0,0 +1,22 @@
+abstract
+class Abstract {
+	@:overload
+	abstract
+	function abstractFunction():Void;
+
+	@:overload
+	abstract
+	function abstractFunction(i:Int):Void;
+}
+
+abstract
+class IntermissionAbstract extends Abstract {
+	@:overload
+	override function abstractFunction():Void {}
+}
+
+class Main extends IntermissionAbstract {
+	static function main() {
+
+	}
+}

+ 3 - 0
tests/misc/java/projects/Issue9619/missing-overload-implementation-from-parent-2/compile-fail.hxml

@@ -0,0 +1,3 @@
+--main Main
+--jvm whatever.jar
+--no-output

+ 3 - 0
tests/misc/java/projects/Issue9619/missing-overload-implementation-from-parent-2/compile-fail.hxml.stderr

@@ -0,0 +1,3 @@
+Main.hx:18: characters 7-11 : This class extends abstract class IntermissionAbstract but doesn't implement the following method
+Main.hx:18: characters 7-11 : Implement it or make Main abstract as well
+Main.hx:9: characters 11-27 : ... abstractFunction(i:Int)

+ 22 - 0
tests/misc/java/projects/Issue9619/missing-overload-implementation-from-parent/Main.hx

@@ -0,0 +1,22 @@
+abstract
+class Abstract {
+	@:overload
+	abstract
+	function abstractFunction():Void;
+
+	@:overload
+	abstract
+	function abstractFunction(i:Int):Void;
+}
+
+abstract
+class IntermissionAbstract extends Abstract {}
+
+class Main extends IntermissionAbstract {
+	static function main() {
+
+	}
+
+	@:overload
+	override function abstractFunction():Void {}
+}

+ 3 - 0
tests/misc/java/projects/Issue9619/missing-overload-implementation-from-parent/compile-fail.hxml

@@ -0,0 +1,3 @@
+--main Main
+--jvm whatever.jar
+--no-output

+ 3 - 0
tests/misc/java/projects/Issue9619/missing-overload-implementation-from-parent/compile-fail.hxml.stderr

@@ -0,0 +1,3 @@
+Main.hx:15: characters 7-11 : This class extends abstract class IntermissionAbstract but doesn't implement the following method
+Main.hx:15: characters 7-11 : Implement it or make Main abstract as well
+Main.hx:9: characters 11-27 : ... abstractFunction(i:Int)

+ 19 - 0
tests/misc/java/projects/Issue9619/missing-overload-implementation/Main.hx

@@ -0,0 +1,19 @@
+abstract
+class Abstract {
+	@:overload
+	abstract
+	function abstractFunction():Void;
+
+	@:overload
+	abstract
+	function abstractFunction(i:Int):Void;
+}
+
+class Main extends Abstract {
+	static function main() {
+
+	}
+
+	@:overload
+	override function abstractFunction():Void {}
+}

+ 3 - 0
tests/misc/java/projects/Issue9619/missing-overload-implementation/compile-fail.hxml

@@ -0,0 +1,3 @@
+--main Main
+--jvm whatever.jar
+--no-output

+ 3 - 0
tests/misc/java/projects/Issue9619/missing-overload-implementation/compile-fail.hxml.stderr

@@ -0,0 +1,3 @@
+Main.hx:12: characters 7-11 : This class extends abstract class Abstract but doesn't implement the following method
+Main.hx:12: characters 7-11 : Implement it or make Main abstract as well
+Main.hx:9: characters 11-27 : ... abstractFunction(i:Int)

+ 16 - 0
tests/misc/java/projects/Issue9619/missing-overload-implementations/Main.hx

@@ -0,0 +1,16 @@
+abstract
+class Abstract {
+	@:overload
+	abstract
+	function abstractFunction():Void;
+
+	@:overload
+	abstract
+	function abstractFunction(i:Int):Void;
+}
+
+class Main extends Abstract {
+	static function main() {
+
+	}
+}

+ 3 - 0
tests/misc/java/projects/Issue9619/missing-overload-implementations/compile-fail.hxml

@@ -0,0 +1,3 @@
+--main Main
+--jvm whatever.jar
+--no-output

+ 4 - 0
tests/misc/java/projects/Issue9619/missing-overload-implementations/compile-fail.hxml.stderr

@@ -0,0 +1,4 @@
+Main.hx:12: characters 7-11 : This class extends abstract class Abstract but doesn't implement the following methods
+Main.hx:12: characters 7-11 : Implement them or make Main abstract as well
+Main.hx:5: characters 11-27 : ... abstractFunction()
+Main.hx:9: characters 11-27 : ... abstractFunction(i:Int)

+ 9 - 0
tests/misc/projects/Issue9619/abstract-dynamic/Main.hx

@@ -0,0 +1,9 @@
+abstract class Abstract {
+	abstract dynamic function abstractFunction():Void;
+}
+
+class Main {
+	static function main() {
+
+	}
+}

+ 2 - 0
tests/misc/projects/Issue9619/abstract-dynamic/compile-fail.hxml

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

+ 1 - 0
tests/misc/projects/Issue9619/abstract-dynamic/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:2: characters 2-52 : Abstract methods may not be dynamic

+ 10 - 0
tests/misc/projects/Issue9619/abstract-expression-inline/Main.hx

@@ -0,0 +1,10 @@
+abstract class Abstract {
+	abstract public function abstractFunction():Void;
+}
+
+class Main {
+	static function main() {
+		var w:Abstract = null;
+		inline w.abstractFunction();
+	}
+}

+ 2 - 0
tests/misc/projects/Issue9619/abstract-expression-inline/compile-fail.hxml

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

+ 1 - 0
tests/misc/projects/Issue9619/abstract-expression-inline/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:8: characters 3-30 : Cannot force inline on abstract method

+ 9 - 0
tests/misc/projects/Issue9619/abstract-final-class/Main.hx

@@ -0,0 +1,9 @@
+abstract final class Abstract {
+
+}
+
+class Main {
+	static function main() {
+
+	}
+}

+ 2 - 0
tests/misc/projects/Issue9619/abstract-final-class/compile-fail.hxml

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

+ 1 - 0
tests/misc/projects/Issue9619/abstract-final-class/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:1: characters 22-30 : An abstract class may not be final

+ 9 - 0
tests/misc/projects/Issue9619/abstract-final/Main.hx

@@ -0,0 +1,9 @@
+abstract class Abstract {
+	abstract final function abstractFunction():Void;
+}
+
+class Main {
+	static function main() {
+
+	}
+}

+ 2 - 0
tests/misc/projects/Issue9619/abstract-final/compile-fail.hxml

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

+ 1 - 0
tests/misc/projects/Issue9619/abstract-final/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:2: characters 26-42 : Abstract methods may not be final

+ 10 - 0
tests/misc/projects/Issue9619/abstract-inline/Main.hx

@@ -0,0 +1,10 @@
+abstract class Abstract {
+	abstract inline public function abstractFunction():Void;
+}
+
+class Main {
+	static function main() {
+		var w:Abstract = null;
+		w.abstractFunction();
+	}
+}

+ 2 - 0
tests/misc/projects/Issue9619/abstract-inline/compile-fail.hxml

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

+ 1 - 0
tests/misc/projects/Issue9619/abstract-inline/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:2: characters 34-50 : Abstract methods may not be inline

+ 7 - 0
tests/misc/projects/Issue9619/abstract-interface/Main.hx

@@ -0,0 +1,7 @@
+abstract interface I {}
+
+class Main {
+	static function main() {
+
+	}
+}

+ 2 - 0
tests/misc/projects/Issue9619/abstract-interface/compile-fail.hxml

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

+ 1 - 0
tests/misc/projects/Issue9619/abstract-interface/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:1: characters 20-21 : An interface may not be abstract

+ 9 - 0
tests/misc/projects/Issue9619/abstract-static/Main.hx

@@ -0,0 +1,9 @@
+class Abstract {
+	abstract static function abstractFunction():Void;
+}
+
+class Main extends Abstract {
+	static function main() {
+
+	}
+}

+ 2 - 0
tests/misc/projects/Issue9619/abstract-static/compile-fail.hxml

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

+ 1 - 0
tests/misc/projects/Issue9619/abstract-static/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:2: characters 27-43 : Static methods may not be abstract

+ 9 - 0
tests/misc/projects/Issue9619/abstract-structInit/Main.hx

@@ -0,0 +1,9 @@
+@:structInit abstract class Abstract {
+
+}
+
+class Main extends Abstract {
+	static function main() {
+
+	}
+}

+ 2 - 0
tests/misc/projects/Issue9619/abstract-structInit/compile-fail.hxml

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

+ 2 - 0
tests/misc/projects/Issue9619/abstract-structInit/compile-fail.hxml.stderr

@@ -0,0 +1,2 @@
+Main.hx:1: characters 1-13 : @:structInit is not allowed on abstract classes
+Main.hx:1: lines 1-3 : ... Defined in this class

+ 9 - 0
tests/misc/projects/Issue9619/abstract-with-expression/Main.hx

@@ -0,0 +1,9 @@
+abstract class Abstract {
+	abstract function abstractFunction() {}
+}
+
+class Main {
+	static function main() {
+
+	}
+}

+ 2 - 0
tests/misc/projects/Issue9619/abstract-with-expression/compile-fail.hxml

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

+ 1 - 0
tests/misc/projects/Issue9619/abstract-with-expression/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:2: characters 2-41 : Abstract methods may not have an expression

+ 12 - 0
tests/misc/projects/Issue9619/constructing-abstract/Main.hx

@@ -0,0 +1,12 @@
+abstract
+class Abstract {
+	public function new() {}
+	abstract
+	function abstractFunction():Void;
+}
+
+class Main {
+	static function main() {
+		new Abstract();
+	}
+}

+ 2 - 0
tests/misc/projects/Issue9619/constructing-abstract/compile-fail.hxml

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

+ 1 - 0
tests/misc/projects/Issue9619/constructing-abstract/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:10: characters 3-17 : Abstract is abstract and cannot be constructed

+ 7 - 0
tests/misc/projects/Issue9619/missing-abstract-on-class/Main.hx

@@ -0,0 +1,7 @@
+class Main {
+	static function main() {
+
+	}
+
+	abstract function abstractFunction():Void;
+}

+ 2 - 0
tests/misc/projects/Issue9619/missing-abstract-on-class/compile-fail.hxml

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

+ 2 - 0
tests/misc/projects/Issue9619/missing-abstract-on-class/compile-fail.hxml.stderr

@@ -0,0 +1,2 @@
+Main.hx:1: characters 7-11 : This class should be declared abstract because it has at least one abstract field
+Main.hx:6: characters 20-36 : First abstract field was here

+ 10 - 0
tests/misc/projects/Issue9619/missing-implementation/Main.hx

@@ -0,0 +1,10 @@
+abstract
+class Abstract {
+	abstract function abstractFunction():Void;
+}
+
+class Main extends Abstract {
+	static function main() {
+
+	}
+}

+ 2 - 0
tests/misc/projects/Issue9619/missing-implementation/compile-fail.hxml

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

+ 3 - 0
tests/misc/projects/Issue9619/missing-implementation/compile-fail.hxml.stderr

@@ -0,0 +1,3 @@
+Main.hx:6: characters 7-11 : This class extends abstract class Abstract but doesn't implement the following method
+Main.hx:6: characters 7-11 : Implement it or make Main abstract as well
+Main.hx:3: characters 20-36 : ... abstractFunction()

+ 13 - 0
tests/misc/projects/Issue9619/super-abstract/Main.hx

@@ -0,0 +1,13 @@
+abstract class Abstract {
+	abstract function abstractFunction():Void;
+}
+
+class Main extends Abstract {
+	static function main() {
+
+	}
+
+	override function abstractFunction() {
+		super.abstractFunction();
+	}
+}

+ 2 - 0
tests/misc/projects/Issue9619/super-abstract/compile-fail.hxml

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

+ 1 - 0
tests/misc/projects/Issue9619/super-abstract/compile-fail.hxml.stderr

@@ -0,0 +1 @@
+Main.hx:11: characters 3-27 : abstract method abstractFunction cannot be accessed directly

+ 59 - 0
tests/unit/src/unit/issues/Issue9619.hx

@@ -0,0 +1,59 @@
+package unit.issues;
+
+#if (java || cs)
+
+private abstract class AbstractOverloadParent {
+	public function new():Void {}
+
+	@:overload
+	abstract function abstractFunction():Void;
+
+	@:overload
+	abstract function abstractFunction(i:Int):Void;
+}
+
+private class ConcreteOverloadChild extends AbstractOverloadParent {
+	public function new() {
+		super();
+	}
+
+	@:overload
+	override function abstractFunction():Void {}
+
+	@:overload
+	override function abstractFunction(i:Int):Void {}
+}
+
+#end
+
+abstract private class AbstractParent {
+	public function new():Void {}
+
+	abstract public function abstractFunction():Bool;
+}
+
+private class ConcreteChild extends AbstractParent {
+	public function new() {
+		super();
+	}
+
+	override public function abstractFunction():Bool {
+		return true;
+	}
+}
+
+class Issue9619 extends unit.Test {
+	function test() {
+		#if (java || cs)
+		var cc = new ConcreteOverloadChild();
+		t(HelperMacros.typeError(new AbstractOverloadParent()));
+		#end
+		var cc = new ConcreteChild();
+
+		t(HelperMacros.typeError(new AbstractParent()));
+		t(cc.abstractFunction());
+
+		var ac:AbstractParent = cc;
+		t(ac.abstractFunction());
+	}
+}