diff --git a/src/typing/typeloadFields.ml b/src/typing/typeloadFields.ml index ea333eb8882..b3e0e42cbc9 100644 --- a/src/typing/typeloadFields.ml +++ b/src/typing/typeloadFields.ml @@ -733,7 +733,35 @@ module TypeBinding = struct if not fctx.is_static && not cctx.is_lib then begin match get_declared cf.cf_name c.cl_super with | None -> () | Some (csup,_) -> - display_error ctx.com ("Redefinition of variable " ^ cf.cf_name ^ " in subclass is not allowed. Previously declared at " ^ (s_type_path csup.cl_path) ) cf.cf_name_pos + let error_redefinition () = + display_error ctx.com ("Redefinition of variable " ^ cf.cf_name ^ " in subclass is not allowed. Previously declared at " ^ (s_type_path csup.cl_path)) cf.cf_name_pos + in + (try + let cf_parent = PMap.find cf.cf_name csup.cl_fields in + if ( + has_class_field_flag cf_parent CfPublic + && not (has_class_field_flag cf CfPublic) + ) then + display_error ctx.com ("Variable " ^ cf.cf_name ^ " has less visibility (public/private) than superclass one") cf.cf_pos + else if is_physical_var_field cf_parent then + error_redefinition () + else begin + let is_narrowing parent_acc child_acc = match parent_acc, child_acc with + | AccCall, (AccPrivateCall | AccNever) -> true + | AccPrivateCall, AccNever -> true + | _ -> false + in + match cf.cf_kind, cf_parent.cf_kind with + | Var child_v, Var parent_v -> + if is_narrowing parent_v.v_read child_v.v_read then + display_error ctx.com ("Cannot narrow read access of " ^ cf.cf_name ^ " in subclass") cf.cf_name_pos + else if is_narrowing parent_v.v_write child_v.v_write then + display_error ctx.com ("Cannot narrow write access of " ^ cf.cf_name ^ " in subclass") cf.cf_name_pos + | _ -> + error_redefinition () + end + with Not_found -> + error_redefinition ()) end let bind_var_expression ctx_f cctx fctx cf e = diff --git a/tests/misc/eval/projects/Issue12521/Main.hx b/tests/misc/eval/projects/Issue12521/Main.hx new file mode 100644 index 00000000000..674c059f303 --- /dev/null +++ b/tests/misc/eval/projects/Issue12521/Main.hx @@ -0,0 +1,54 @@ +class PhysicalParent { + public var x:Int; + public function new() {} +} + +class PhysicalPropertyParent { + @:isVar public var x(get, set):Int; + public function new() {} + function get_x() return x; + function set_x(v) return x = v; +} + +class RedefinePhysical extends PhysicalParent { + public var x:Int; +} + +class RedefinePhysicalProperty extends PhysicalPropertyParent { + @:isVar public var x(get, set):Int; +} + +class RedefinePhysicalPropAsProperty extends PhysicalPropertyParent { + public var x(get, set):Int; +} + +class RedefinePhysicalAsProperty extends PhysicalParent { + public var x(get, set):Int; + function get_x() return 0; + function set_x(v) return v; +} + +class NonPhysicalParent { + public var x(get, set):Int; + public function new() {} + function get_x() return 0; + function set_x(v) return v; +} + +class NarrowRead extends NonPhysicalParent { + public var x(never, set):Int; +} + +class NarrowWrite extends NonPhysicalParent { + public var x(get, never):Int; +} + +class NarrowWrite2 extends NonPhysicalParent { + public var x(get, private set):Int; +} + +class PrivateVar extends NonPhysicalParent { + var x(get, set):Int; +} + +function main() {} diff --git a/tests/misc/eval/projects/Issue12521/compile-fail.hxml b/tests/misc/eval/projects/Issue12521/compile-fail.hxml new file mode 100644 index 00000000000..7d61297e2cb --- /dev/null +++ b/tests/misc/eval/projects/Issue12521/compile-fail.hxml @@ -0,0 +1,2 @@ +-main Main +--interp diff --git a/tests/misc/eval/projects/Issue12521/compile-fail.hxml.stderr b/tests/misc/eval/projects/Issue12521/compile-fail.hxml.stderr new file mode 100644 index 00000000000..898d144cef6 --- /dev/null +++ b/tests/misc/eval/projects/Issue12521/compile-fail.hxml.stderr @@ -0,0 +1,8 @@ +Main.hx:51: characters 2-22 : Variable x has less visibility (public/private) than superclass one +Main.hx:47: characters 13-14 : Cannot narrow write access of x in subclass +Main.hx:43: characters 13-14 : Cannot narrow write access of x in subclass +Main.hx:39: characters 13-14 : Cannot narrow read access of x in subclass +Main.hx:26: characters 13-14 : Redefinition of variable x in subclass is not allowed. Previously declared at PhysicalParent +Main.hx:22: characters 13-14 : Redefinition of variable x in subclass is not allowed. Previously declared at PhysicalPropertyParent +Main.hx:18: characters 21-22 : Redefinition of variable x in subclass is not allowed. Previously declared at PhysicalPropertyParent +Main.hx:14: characters 13-14 : Redefinition of variable x in subclass is not allowed. Previously declared at PhysicalParent diff --git a/tests/unit/src/unit/TestMain.hx b/tests/unit/src/unit/TestMain.hx index 8cc773e8283..6d31721e1dd 100644 --- a/tests/unit/src/unit/TestMain.hx +++ b/tests/unit/src/unit/TestMain.hx @@ -106,6 +106,7 @@ function main() { #if (!flash && !hl && !cppia) new TestCoroutines(), #end + new TestRedefinition(), // new TestUnspecified(), ]; diff --git a/tests/unit/src/unit/TestRedefinition.hx b/tests/unit/src/unit/TestRedefinition.hx new file mode 100644 index 00000000000..63aa7b58324 --- /dev/null +++ b/tests/unit/src/unit/TestRedefinition.hx @@ -0,0 +1,88 @@ +package unit; + +abstract class Dispatcher { + public var scheduler(get, never):Float; + + abstract function get_scheduler():Float; +} + +class ConcreteDispatcher extends Dispatcher { + @:isVar public var scheduler(get, null):Int; + + public function new(v:Int) { + this.scheduler = v; + } + + function get_scheduler() { + return scheduler; + } +} + +class ParentWithNonPhysical { + public function new() {} + + public var name(get, private set):String; + + function get_name():String + return "parent"; + + function set_name(v:String):String + return "parent"; +} + +class ChildWidened extends ParentWithNonPhysical { + @:isVar public var name(get, set):String; + + public function new(v:String) { + super(); + this.name = v; + } + + override function get_name():String + return name; + + override function set_name(v:String):String + return name = v; +} + +class ChildWidenedNonPhysical extends ParentWithNonPhysical { + public var name(get, set):String; + + public function new() { + super(); + } + + override function get_name():String + return "child"; + + override function set_name(v:String):String + return v; +} + +class TestRedefinition extends Test { + public function testDispatcher() { + final dispatcher = new ConcreteDispatcher(123); + eq(dispatcher.scheduler, 123); + final generalDispatcher:Dispatcher = dispatcher; + eq(generalDispatcher.scheduler, 123.0); + } + + public function testWidening() { + final child = new ChildWidened("child"); + eq(child.name, "child"); + child.name = "new child"; + eq(child.name, "new child"); + + final parent:ParentWithNonPhysical = child; + eq(parent.name, "new child"); + } + + public function testWideningNonPhysical() { + final child = new ChildWidenedNonPhysical(); + eq(child.name, "child"); + child.name = "new child"; + + final parent:ParentWithNonPhysical = child; + eq(parent.name, "child"); + } +}