Quellcode durchsuchen

Final (#6596)

* add final as a keyword

Allow using it as field modifier for functions and in place of `var` for fields

* respect `final` for fields and functions

* detect uninitialized finals

* fix test

* require constructor if there's an uninitialized final var

* allow `final` in anonymous structures as shortcut to `(default,never)`
Simon Krajewski vor 8 Jahren
Ursprung
Commit
8a076bf68d

+ 3 - 2
src/generators/gencpp.ml

@@ -5318,7 +5318,8 @@ let access_str a = match a with
    | AccResolve -> "AccResolve"
    | AccCall -> "AccCall"
    | AccInline -> "AccInline"
-   | AccRequire(_,_) -> "AccRequire" ;;
+   | AccRequire(_,_) -> "AccRequire"
+   | AccCtor -> "AccCtor";;
 
 
 let script_type t optional = if optional then begin
@@ -7992,7 +7993,7 @@ let generate_script_class common_ctx script class_def =
          script#writeOpLine IaInline;
       | Var v,_ ->
          let mode_code mode = match mode with
-         | AccNormal -> IaAccessNormal
+         | AccNormal | AccCtor -> IaAccessNormal
          | AccNo -> IaAccessNot
          | AccNever -> IaAccessNot
          | AccResolve -> IaAccessResolve

+ 4 - 2
src/generators/genxml.ml

@@ -132,7 +132,7 @@ and gen_type_decl n t pl =
 and gen_field att f =
 	let add_get_set acc name att =
 		match acc with
-		| AccNormal | AccResolve | AccRequire _ -> att
+		| AccNormal | AccResolve | AccRequire _ | AccCtor -> att
 		| AccNo | AccNever -> (name, "null") :: att
 		| AccCall -> (name,"accessor") :: att
 		| AccInline -> (name,"inline") :: att
@@ -171,7 +171,9 @@ and gen_field att f =
 		with Not_found ->
 			cf.cf_name
 	in
-	node (field_name f) (if f.cf_public then ("public","1") :: att else att) (gen_type ~values:(Some values) f.cf_type :: gen_meta f.cf_meta @ gen_doc_opt f.cf_doc @ overloads)
+	let att = if f.cf_public then ("public","1") :: att else att in
+	let att = if (Meta.has Meta.Final) f.cf_meta then ("final","1") :: att else att in
+	node (field_name f) att (gen_type ~values:(Some values) f.cf_type :: gen_meta f.cf_meta @ gen_doc_opt f.cf_doc @ overloads)
 
 let gen_constr e =
 	let doc = gen_doc_opt e.ef_doc in

+ 4 - 0
src/macro/macroApi.ml

@@ -387,6 +387,7 @@ and encode_access a =
 		| ADynamic -> 4
 		| AInline -> 5
 		| AMacro -> 6
+		| AFinal -> 7
 	in
 	encode_enum IAccess tag []
 
@@ -685,6 +686,7 @@ and decode_access v =
 	| 4, [] -> ADynamic
 	| 5, [] -> AInline
 	| 6, [] -> AMacro
+	| 7, [] -> AFinal
 	| _ -> raise Invalid_expr
 
 and decode_meta_entry v =
@@ -943,6 +945,7 @@ and encode_var_access a =
 		| AccCall -> 4, []
 		| AccInline	-> 5, []
 		| AccRequire (s,msg) -> 6, [encode_string s; null encode_string msg]
+		| AccCtor -> 7, []
 	) in
 	encode_enum IVarAccess tag pl
 
@@ -1266,6 +1269,7 @@ let decode_var_access v =
 	| 4, [] -> AccCall
 	| 5, [] -> AccInline
 	| 6, [s1;s2] -> AccRequire(decode_string s1, opt decode_string s2)
+	| 7, [] -> AccCtor
 	| _ -> raise Invalid_expr
 
 let decode_method_kind v =

+ 4 - 0
src/syntax/ast.ml

@@ -62,6 +62,7 @@ type keyword =
 	| False
 	| Abstract
 	| Macro
+	| Final
 
 type binop =
 	| OpAdd
@@ -221,6 +222,7 @@ and access =
 	| ADynamic
 	| AInline
 	| AMacro
+	| AFinal
 
 and class_field_kind =
 	| FVar of type_hint option * expr option
@@ -362,6 +364,7 @@ let s_access = function
 	| ADynamic -> "dynamic"
 	| AInline -> "inline"
 	| AMacro -> "macro"
+	| AFinal -> "final"
 
 let s_keyword = function
 	| Function -> "function"
@@ -406,6 +409,7 @@ let s_keyword = function
 	| False -> "false"
 	| Abstract -> "abstract"
 	| Macro -> "macro"
+	| Final -> "final"
 
 let rec s_binop = function
 	| OpAdd -> "+"

+ 1 - 1
src/syntax/lexer.ml

@@ -84,7 +84,7 @@ let keywords =
 		Switch;Case;Default;Public;Private;Try;Untyped;
 		Catch;New;This;Throw;Extern;Enum;In;Interface;
 		Cast;Override;Dynamic;Typedef;Package;
-		Inline;Using;Null;True;False;Abstract;Macro];
+		Inline;Using;Null;True;False;Abstract;Macro;Final];
 	h
 
 let init file do_add =

+ 49 - 36
src/syntax/parser.mly

@@ -338,6 +338,7 @@ let reify in_macro =
 			| ADynamic -> "ADynamic"
 			| AInline -> "AInline"
 			| AMacro -> "AMacro"
+			| AFinal -> "AFinal"
 			) in
 			mk_enum "Access" n [] p
 		in
@@ -848,7 +849,7 @@ and parse_class_field_resume tdecl s =
 			| Kwd New :: Kwd Function :: _ ->
 				junk_tokens (k - 2);
 				parse_class_field_resume tdecl s
-			| Kwd Macro :: _ | Kwd Public :: _ | Kwd Static :: _ | Kwd Var :: _ | Kwd Override :: _ | Kwd Dynamic :: _ | Kwd Inline :: _ ->
+			| Kwd Macro :: _ | Kwd Public :: _ | Kwd Static :: _ | Kwd Var :: _ | Kwd Final :: _ | Kwd Override :: _ | Kwd Dynamic :: _ | Kwd Inline :: _ ->
 				junk_tokens (k - 1);
 				parse_class_field_resume tdecl s
 			| BrClose :: _ when tdecl ->
@@ -940,11 +941,11 @@ and parse_complex_type_inner = parser
 	| [< '(POpen,p1); t = parse_complex_type; '(PClose,p2) >] -> CTParent t,punion p1 p2
 	| [< '(BrOpen,p1); s >] ->
 		(match s with parser
-		| [< l,p2 = parse_type_anonymous false >] -> CTAnonymous l,punion p1 p2
+		| [< l,p2 = parse_type_anonymous false false >] -> CTAnonymous l,punion p1 p2
 		| [< t = parse_structural_extension; s>] ->
 			let tl = t :: plist parse_structural_extension s in
 			(match s with parser
-			| [< l,p2 = parse_type_anonymous false >] -> CTExtend (tl,l),punion p1 p2
+			| [< l,p2 = parse_type_anonymous false false >] -> CTExtend (tl,l),punion p1 p2
 			| [< l,p2 = parse_class_fields true p1 >] -> CTExtend (tl,l),punion p1 p2)
 		| [< l,p2 = parse_class_fields true p1 >] -> CTAnonymous l,punion p1 p2
 		| [< >] -> serror())
@@ -1018,8 +1019,9 @@ and parse_complex_type_next (t : type_hint) = parser
 			CTFunction ([t] , (t2,p2)),punion (pos t) p2)
 	| [< >] -> t
 
-and parse_type_anonymous opt = parser
-	| [< '(Question,_) when not opt; s >] -> parse_type_anonymous true s
+and parse_type_anonymous opt final = parser
+	| [< '(Question,_) when not opt; s >] -> parse_type_anonymous true final s
+	| [< '(Kwd Final,_) when not opt && not final; s >] -> parse_type_anonymous opt true s
 	| [< name, p1 = ident; t = parse_type_hint_with_pos; s >] ->
 		let p2 = pos (last_token s) in
 		let next acc =
@@ -1028,7 +1030,7 @@ and parse_type_anonymous opt = parser
 				cff_meta = if opt then [Meta.Optional,[],null_pos] else [];
 				cff_access = [];
 				cff_doc = None;
-				cff_kind = FVar (Some t,None);
+				cff_kind = if final then FProp(("default",null_pos),("never",null_pos),Some t,None) else FVar (Some t,None);
 				cff_pos = punion p1 p2;
 			} :: acc
 		in
@@ -1037,7 +1039,7 @@ and parse_type_anonymous opt = parser
 		| [< '(Comma,p2) >] ->
 			(match s with parser
 			| [< '(BrClose,p2) >] -> next [],p2
-			| [< l,p2 = parse_type_anonymous false >] -> next l,punion p1 p2
+			| [< l,p2 = parse_type_anonymous false false >] -> next l,punion p1 p2
 			| [< >] -> serror());
 		| [< >] -> serror()
 
@@ -1069,43 +1071,54 @@ and parse_enum_param = parser
 	| [< '(Question,_); name, _ = ident; t = parse_type_hint_with_pos >] -> (name,true,t)
 	| [< name, _ = ident; t = parse_type_hint_with_pos >] -> (name,false,t)
 
+and parse_function_field doc meta al = parser
+	| [< '(Kwd Function,p1); name = parse_fun_name; pl = parse_constraint_params; '(POpen,_); args = psep Comma parse_fun_param; '(PClose,_); t = popt parse_type_hint_with_pos; s >] ->
+		let e, p2 = (match s with parser
+			| [< e = toplevel_expr; s >] ->
+				(try ignore(semicolon s) with Error (Missing_semicolon,p) -> !display_error Missing_semicolon p);
+				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
+		name, punion p1 p2, FFun f, al
+
+and parse_var_field_assignment = parser
+	| [< '(Binop OpAssign,_); e = toplevel_expr; p2 = semicolon >] -> Some e , p2
+	| [< p2 = semicolon >] -> None , p2
+	| [< >] -> serror()
+
 and parse_class_field s =
 	let doc = get_doc s in
 	match s with parser
 	| [< meta = parse_meta; al = parse_cf_rights true []; s >] ->
-		let name, pos, k = (match s with parser
+		let name, pos, k, al = (match s with parser
 		| [< '(Kwd Var,p1); name = dollar_ident; s >] ->
-			(match s with parser
+			begin match s with parser
 			| [< '(POpen,_); i1 = property_ident; '(Comma,_); i2 = property_ident; '(PClose,_) >] ->
 				let t = popt parse_type_hint_with_pos s in
-				let e , p2 = (match s with parser
-				| [< '(Binop OpAssign,_); e = toplevel_expr; p2 = semicolon >] -> Some e , p2
-				| [< p2 = semicolon >] -> None , p2
-				| [< >] -> serror()
-				) in
-				name, punion p1 p2, FProp (i1,i2,t, e)
+				let e,p2 = parse_var_field_assignment s in
+				name, punion p1 p2, FProp (i1,i2,t, e), al
 			| [< t = popt parse_type_hint_with_pos; s >] ->
-				let e , p2 = (match s with parser
-				| [< '(Binop OpAssign,_); e = toplevel_expr; p2 = semicolon >] -> Some e , p2
-				| [< p2 = semicolon >] -> None , p2
-				| [< >] -> serror()
-				) in
-				name, punion p1 p2, FVar (t,e))
-		| [< '(Kwd Function,p1); name = parse_fun_name; pl = parse_constraint_params; '(POpen,_); al = psep Comma parse_fun_param; '(PClose,_); t = popt parse_type_hint_with_pos; s >] ->
-			let e, p2 = (match s with parser
-				| [< e = toplevel_expr; s >] ->
-					(try ignore(semicolon s) with Error (Missing_semicolon,p) -> !display_error Missing_semicolon p);
-					Some e, pos e
-				| [< p = semicolon >] -> None, p
-				| [< >] -> serror()
-			) in
-			let f = {
-				f_params = pl;
-				f_args = al;
-				f_type = t;
-				f_expr = e;
-			} in
-			name, punion p1 p2, FFun f
+				let e,p2 = parse_var_field_assignment s in
+				name, punion p1 p2, FVar (t,e), al
+			end
+		| [< '(Kwd Final,p1) >] ->
+			begin match s with parser
+			| [< name = dollar_ident; t = popt parse_type_hint_with_pos; e,p2 = parse_var_field_assignment >] ->
+				name,punion p1 p2,FVar(t,e),(AFinal :: al)
+			| [< al = parse_cf_rights (not (List.mem AStatic al)) (AFinal :: al); f = parse_function_field doc meta al >] ->
+				f
+			| [< >] ->
+				serror()
+			end
+		| [< f = parse_function_field doc meta al >] ->
+			f
 		| [< >] ->
 			if al = [] then raise Stream.Failure else serror()
 		) in

+ 8 - 6
src/typing/type.ml

@@ -33,11 +33,12 @@ and var_kind = {
 
 and var_access =
 	| AccNormal
-	| AccNo				(* can't be accessed outside of the class itself and its subclasses *)
-	| AccNever			(* can't be accessed, even in subclasses *)
-	| AccResolve		(* call resolve("field") when accessed *)
-	| AccCall			(* perform a method call when accessed *)
-	| AccInline			(* similar to Normal but inline when accessed *)
+	| AccNo             (* can't be accessed outside of the class itself and its subclasses *)
+	| AccNever          (* can't be accessed, even in subclasses *)
+	| AccCtor           (* can only be accessed from the constructor *)
+	| AccResolve        (* call resolve("field") when accessed *)
+	| AccCall           (* perform a method call when accessed *)
+	| AccInline         (* similar to Normal but inline when accessed *)
 	| AccRequire of string * string option (* set when @:require(cond) fails *)
 
 and method_kind =
@@ -974,6 +975,7 @@ let s_access is_read = function
 	| AccCall -> if is_read then "get" else "set"
 	| AccInline	-> "inline"
 	| AccRequire (n,_) -> "require " ^ n
+	| AccCtor -> "ctor"
 
 let s_kind = function
 	| Var { v_read = AccNormal; v_write = AccNormal } -> "var"
@@ -1627,7 +1629,7 @@ let unify_access a1 a2 =
 	| _ -> false
 
 let direct_access = function
-	| AccNo | AccNever | AccNormal | AccInline | AccRequire _ -> true
+	| AccNo | AccNever | AccNormal | AccInline | AccRequire _ | AccCtor -> true
 	| AccResolve | AccCall -> false
 
 let unify_kind k1 k2 =

+ 51 - 9
src/typing/typeload.ml

@@ -620,7 +620,7 @@ and load_complex_type ctx allow_display p (t,pn) =
 				| APublic -> ()
 				| APrivate -> pub := false;
 				| ADynamic when (match f.cff_kind with FFun _ -> true | _ -> false) -> dyn := true
-				| AStatic | AOverride | AInline | ADynamic | AMacro -> error ("Invalid access " ^ Ast.s_access a) p
+				| AStatic | AOverride | AInline | ADynamic | AMacro | AFinal -> error ("Invalid access " ^ Ast.s_access a) p
 			) f.cff_access;
 			let t , access = (match f.cff_kind with
 				| FVar (Some (CTPath({tpackage=[];tname="Void"}),_), _)  | FProp (_,_,Some (CTPath({tpackage=[];tname="Void"}),_),_) ->
@@ -905,7 +905,7 @@ let check_overriding ctx c =
 					() (* allow to redefine a method as inlined *)
 				| _ ->
 					display_error ctx ("Field " ^ i ^ " has different property access than in superclass") p);
-				if has_meta Meta.Final f2.cf_meta then display_error ctx ("Cannot override @:final method " ^ i) p;
+				if has_meta Meta.Final f2.cf_meta then display_error ctx ("Cannot override final method " ^ i) p;
 				try
 					let t = apply_params csup.cl_params params t in
 					valid_redefinition ctx f f.cf_type f2 t
@@ -1638,7 +1638,28 @@ let type_function ctx args ret fmode f do_display p =
 	in
 	let e = if fmode <> FunConstructor then
 		e
-	else match has_super_constr() with
+	else begin
+		let final_vars = Hashtbl.create 0 in
+		List.iter (fun cf -> match cf.cf_kind with
+			| Var _ when Meta.has Meta.Final cf.cf_meta && cf.cf_expr = None ->
+				Hashtbl.add final_vars cf.cf_name cf
+			| _ ->
+				()
+		) ctx.curclass.cl_ordered_fields;
+		if Hashtbl.length final_vars > 0 then begin
+			let rec find_inits e = match e.eexpr with
+				| TBinop(OpAssign,{eexpr = TField({eexpr = TConst TThis},fa)},e2) ->
+					Hashtbl.remove final_vars (field_name fa);
+					find_inits e2;
+				| _ ->
+					Type.iter find_inits e
+			in
+			find_inits e;
+			Hashtbl.iter (fun _ cf ->
+				display_error ctx ("final field " ^ cf.cf_name ^ " must be initialized immediately or in the constructor") cf.cf_pos;
+			) final_vars
+		end;
+		match has_super_constr() with
 		| Some (was_forced,t_super) ->
 			(try
 				loop e;
@@ -1654,7 +1675,7 @@ let type_function ctx args ret fmode f do_display p =
 				Exit -> e);
 		| None ->
 			e
-	in
+	end in
 	locals();
 	let e = match ctx.curfun, ctx.vthis with
 		| (FunMember|FunConstructor), Some v ->
@@ -1900,6 +1921,7 @@ module ClassInitializer = struct
 		context_init : unit -> unit;
 		mutable delayed_expr : (typer * tlazy ref option) list;
 		mutable force_constructor : bool;
+		mutable uninitialized_final : pos option;
 	}
 
 	type field_kind =
@@ -1909,6 +1931,7 @@ module ClassInitializer = struct
 
 	type field_init_ctx = {
 		is_inline : bool;
+		is_final : bool;
 		is_static : bool;
 		is_override : bool;
 		is_extern : bool;
@@ -1999,6 +2022,7 @@ module ClassInitializer = struct
 			abstract = abstract;
 			context_init = context_init;
 			force_constructor = false;
+			uninitialized_final = None;
 			delayed_expr = [];
 		} in
 		ctx,cctx
@@ -2028,6 +2052,7 @@ module ClassInitializer = struct
 			is_override = is_override;
 			is_macro = is_macro;
 			is_extern = is_extern;
+			is_final = List.mem AFinal cff.cff_access;
 			is_display_field = ctx.is_display_file && Display.is_display_position cff.cff_pos;
 			is_field_debug = cctx.is_class_debug;
 			is_abstract_member = cctx.abstract <> None && Meta.has Meta.Impl cff.cff_meta;
@@ -2280,7 +2305,10 @@ module ClassInitializer = struct
 		if not fctx.is_static && cctx.abstract <> None then error (fst f.cff_name ^ ": Cannot declare member variable in abstract") p;
 		if fctx.is_inline && not fctx.is_static then error (fst f.cff_name ^ ": Inline variable must be static") p;
 		if fctx.is_inline && eo = None then error (fst f.cff_name ^ ": Inline variable must be initialized") p;
-
+		if fctx.is_final && eo = None then begin
+			if fctx.is_static then error (fst f.cff_name ^ ": Static final variable must be initialized") p
+			else cctx.uninitialized_final <- Some f.cff_pos;
+		end;
 		let t = (match t with
 			| None when not fctx.is_static && eo = None ->
 				error ("Type required for member variable " ^ fst f.cff_name) p;
@@ -2297,14 +2325,21 @@ module ClassInitializer = struct
 				if fctx.is_static then ctx.type_params <- old;
 				t
 		) in
+		let kind = if fctx.is_inline then
+			{ v_read = AccInline ; v_write = AccNever }
+		else if fctx.is_final then
+			{ v_read = AccNormal ; v_write = if fctx.is_static then AccNever else AccCtor }
+		else
+			{ v_read = AccNormal ; v_write = AccNormal }
+		in
 		let cf = {
 			cf_name = fst f.cff_name;
 			cf_doc = f.cff_doc;
-			cf_meta = f.cff_meta;
+			cf_meta = (if fctx.is_final && not (Meta.has Meta.Final f.cff_meta) then (Meta.Final,[],null_pos) :: f.cff_meta else f.cff_meta);
 			cf_type = t;
 			cf_pos = f.cff_pos;
 			cf_name_pos = pos f.cff_name;
-			cf_kind = Var (if fctx.is_inline then { v_read = AccInline ; v_write = AccNever } else { v_read = AccNormal; v_write = AccNormal });
+			cf_kind = Var kind;
 			cf_expr = None;
 			cf_expr_unoptimized = None;
 			cf_public = is_public (ctx,cctx) f.cff_access None;
@@ -2523,7 +2558,7 @@ module ClassInitializer = struct
 		let cf = {
 			cf_name = fst f.cff_name;
 			cf_doc = f.cff_doc;
-			cf_meta = f.cff_meta;
+			cf_meta = (if fctx.is_final && not (Meta.has Meta.Final f.cff_meta) then (Meta.Final,[],null_pos) :: f.cff_meta else f.cff_meta);
 			cf_type = t;
 			cf_pos = f.cff_pos;
 			cf_name_pos = pos f.cff_name;
@@ -2733,7 +2768,7 @@ module ClassInitializer = struct
 		if name.[0] = '$' then display_error ctx "Field names starting with a dollar are not allowed" p;
 		List.iter (fun acc ->
 			match (acc, f.cff_kind) with
-			| APublic, _ | APrivate, _ | AStatic, _ -> ()
+			| APublic, _ | APrivate, _ | AStatic, _ | AFinal, _ -> ()
 			| ADynamic, FFun _ | AOverride, FFun _ | AMacro, FFun _ | AInline, FFun _ | AInline, FVar _ -> ()
 			| _, FVar _ -> error ("Invalid accessor '" ^ Ast.s_access acc ^ "' for variable " ^ name) p
 			| _, FProp _ -> error ("Invalid accessor '" ^ Ast.s_access acc ^ "' for property " ^ name) p
@@ -2865,6 +2900,13 @@ module ClassInitializer = struct
 		(*
 			make sure a default contructor with same access as super one will be added to the class structure at some point.
 		*)
+		begin match cctx.uninitialized_final with
+			| Some pf when c.cl_constructor = None ->
+				display_error ctx "This class has uninitialized final vars, which requires a constructor" p;
+				error "Example of an uninitialized final var" pf
+			| _ ->
+				()
+		end;
 		(* add_constructor does not deal with overloads correctly *)
 		if not ctx.com.config.pf_overload then add_constructor ctx c cctx.force_constructor p;
 		if Meta.has Meta.StructInit c.cl_meta then check_struct_init_constructor ctx c p;

+ 2 - 0
src/typing/typer.ml

@@ -1088,6 +1088,8 @@ let field_access ctx mode f fmode t e p =
 			if ctx.untyped then normal() else AKNo f.cf_name
 		| AccInline ->
 			AKInline (e,f,fmode,t)
+		| AccCtor ->
+			if ctx.curfun = FunConstructor then normal() else AKNo f.cf_name
 		| AccRequire (r,msg) ->
 			match msg with
 			| None -> error_require r p

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

@@ -772,6 +772,12 @@ enum Access {
 		normal functions which are executed as soon as they are typed.
 	**/
 	AMacro;
+
+	/**
+		Final access modifier. For functions, they can not be overridden. For
+		variables, it means they can be assigned to only once.
+	**/
+	AFinal;
 }
 
 /**

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

@@ -135,6 +135,7 @@ class Printer {
 		case AInline: "inline";
 		case ADynamic: "dynamic";
 		case AMacro: "macro";
+		case AFinal: "final";
 	}
 
 	public function printField(field:Field) return

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

@@ -628,6 +628,11 @@ enum VarAccess {
 		Failed access due to a `@:require` metadata.
 	**/
 	AccRequire( r : String, ?msg : String );
+
+	/**
+		Access is only allowed from the constructor.
+	**/
+	AccCtor;
 }
 
 /**

+ 6 - 2
std/haxe/macro/TypeTools.hx

@@ -43,7 +43,7 @@ class TypeTools {
 	static function toField(cf : ClassField) : Field return {
 		function varAccessToString(va : VarAccess, getOrSet : String) : String return {
 			switch (va) {
-				case AccNormal: "default";
+				case AccNormal | AccCtor: "default";
 				case AccNo: "null";
 				case AccNever: "never";
 				case AccResolve: throw "Invalid TAnonymous";
@@ -52,10 +52,14 @@ class TypeTools {
 				case AccRequire(_, _): "default";
 			}
 		}
+		var access = cf.isPublic ? [ APublic ] : [ APrivate ];
+		if (cf.meta.has(":final")) {
+			access.push(AFinal);
+		}
 		if (cf.params.length == 0) {
 			name: cf.name,
 			doc: cf.doc,
-			access: cf.isPublic ? [ APublic ] : [ APrivate ],
+			access: access,
 			kind: switch([ cf.kind, cf.type ]) {
 				case [ FVar(read, write), ret ]:
 					FProp(

+ 5 - 0
std/haxe/rtti/CType.hx

@@ -109,6 +109,11 @@ typedef ClassField = {
 	**/
 	var isPublic : Bool;
 
+	/**
+		Whether or not the field is final.
+	**/
+	var isFinal : Bool;
+
 	/**
 		Whether or not the field overrides another field.
 	**/

+ 1 - 0
std/haxe/rtti/XmlParser.hx

@@ -414,6 +414,7 @@ class XmlParser {
 			name : x.name,
 			type : t,
 			isPublic : x.x.exists("public") || defPublic,
+			isFinal : x.x.exists("final"),
 			isOverride : x.x.exists("override"),
 			line : if( x.has.line ) Std.parseInt(x.att.line) else null,
 			doc : doc,

+ 5 - 5
std/haxe/zip/InflateImpl.hx

@@ -98,7 +98,7 @@ class InflateImpl {
 	var nbits : Int;
 	var bits : Int;
 	var state : State;
-	var final : Bool;
+	var isFinal : Bool;
 	var huffman : Huffman;
 	var huffdist : Null<Huffman>;
 	var htools : HuffTools;
@@ -114,7 +114,7 @@ class InflateImpl {
 	static var FIXED_HUFFMAN = null;
 
 	public function new( i, ?header = true, ?crc = true ) {
-		final = false;
+		isFinal = false;
 		htools = new HuffTools();
 		huffman = buildFixedHuffman();
 		huffdist = null;
@@ -279,7 +279,7 @@ class InflateImpl {
 			// nothing
 			return false;
 		case Block:
-			final = getBit();
+			isFinal = getBit();
 			switch( getBits(2) ) {
 			case 0: // no compression
 				len = input.readUInt16();
@@ -319,7 +319,7 @@ class InflateImpl {
 			var bytes = input.read(rlen);
 			len -= rlen;
 			addBytes(bytes,0,rlen);
-			if( len == 0 ) state = final ? Crc : Block;
+			if( len == 0 ) state = isFinal ? Crc : Block;
 			return needed > 0;
 		case DistOne:
 			var rlen = (len < needed) ? len : needed;
@@ -342,7 +342,7 @@ class InflateImpl {
 				addByte(n);
 				return needed > 0;
 			} else if( n == 256 ) {
-				state = final ? Crc : Block;
+				state = isFinal ? Crc : Block;
 				return true;
 			} else {
 				n -= 257;

+ 3 - 0
tests/misc/projects/Issue6584/Main1.hx

@@ -0,0 +1,3 @@
+class Main {
+	static final v;
+}

+ 7 - 0
tests/misc/projects/Issue6584/Main2.hx

@@ -0,0 +1,7 @@
+class Main {
+	static final v = 1;
+
+	static function f() {
+		v = 2;
+	}
+}

+ 11 - 0
tests/misc/projects/Issue6584/Main3.hx

@@ -0,0 +1,11 @@
+class Main {
+	final v:Int;
+
+	function new() {
+		v = 1;
+	}
+
+	function f() {
+		v = 2;
+	}
+}

+ 10 - 0
tests/misc/projects/Issue6584/Main4.hx

@@ -0,0 +1,10 @@
+class Main {
+	final y:Int;
+	static public function main() {
+		new Main();
+	}
+
+	function new() {
+
+	}
+}

+ 6 - 0
tests/misc/projects/Issue6584/Main5.hx

@@ -0,0 +1,6 @@
+class Main {
+	final v:Int;
+	static public function main() {
+
+	}
+}

+ 2 - 0
tests/misc/projects/Issue6584/compile1-fail.hxml

@@ -0,0 +1,2 @@
+Main1
+--interp

+ 1 - 0
tests/misc/projects/Issue6584/compile1-fail.hxml.stderr

@@ -0,0 +1 @@
+Main1.hx:2: characters 9-17 : v: Static final variable must be initialized

+ 2 - 0
tests/misc/projects/Issue6584/compile2-fail.hxml

@@ -0,0 +1,2 @@
+Main2
+--interp

+ 1 - 0
tests/misc/projects/Issue6584/compile2-fail.hxml.stderr

@@ -0,0 +1 @@
+Main2.hx:5: characters 3-8 : Cannot access field or identifier v for writing

+ 2 - 0
tests/misc/projects/Issue6584/compile3-fail.hxml

@@ -0,0 +1,2 @@
+Main3
+--interp

+ 1 - 0
tests/misc/projects/Issue6584/compile3-fail.hxml.stderr

@@ -0,0 +1 @@
+Main3.hx:9: characters 3-8 : Cannot access field or identifier v for writing

+ 2 - 0
tests/misc/projects/Issue6584/compile4-fail.hxml

@@ -0,0 +1,2 @@
+Main4
+--interp

+ 1 - 0
tests/misc/projects/Issue6584/compile4-fail.hxml.stderr

@@ -0,0 +1 @@
+Main4.hx:2: characters 2-14 : final field y must be initialized immediately or in the constructor

+ 2 - 0
tests/misc/projects/Issue6584/compile5-fail.hxml

@@ -0,0 +1,2 @@
+Main5
+--interp

+ 2 - 0
tests/misc/projects/Issue6584/compile5-fail.hxml.stderr

@@ -0,0 +1,2 @@
+Main5.hx:1: lines 1-6 : This class has uninitialized final vars, which requires a constructor
+Main5.hx:2: characters 2-14 : Example of an uninitialized final var