fix: remove default constructors and require explicit construct new() for all user classes

The compiler no longer auto-generates a default `new()` constructor for classes. All classes must now explicitly define `construct new() {}` to be instantiable. This change removes the `createDefaultConstructor` function from the compiler, eliminates the `return_this` primitive used by Object's default initializer, and adds explicit constructors to all test and example classes. Core metaclasses (Bool, Class, Null, Num, Object, Range, Sequence) now correctly reject `new()` calls with runtime errors.
This commit is contained in:
Bob Nystrom
2015-09-01 15:16:04 +00:00
parent 4665ec1913
commit ef93496fd4
86 changed files with 248 additions and 133 deletions
@@ -1,4 +1,6 @@
class foo {
construct new() {}
static callFoo {
IO.print(foo)
}
@@ -1,4 +1,6 @@
class Foo {
construct new() {}
static sayName {
IO.print(Foo)
}
@@ -1,6 +1,8 @@
var F = null
class Foo {
construct new() {}
method(param) {
F = Fn.new {
IO.print(param)
@@ -4,6 +4,8 @@ var foo = null
{
var local = "local"
class Foo {
construct new() {}
method {
IO.print(local)
}
@@ -2,6 +2,8 @@
{
var local = "local"
class Foo {
construct new() {}
method {
IO.print(local)
}
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
static bar { true }
static baz { 1 }
}
-8
View File
@@ -1,8 +0,0 @@
class Foo {
toString { "Foo" }
}
// Classes get an argument-less "new()" by default.
var foo = Foo.new()
IO.print(foo is Foo) // expect: true
IO.print(foo.toString) // expect: Foo
@@ -1,9 +0,0 @@
class Foo {
construct new() {
IO.print("Foo.new()")
}
}
class Bar is Foo {}
Bar.new() // expect: Foo.new()
+2 -4
View File
@@ -1,6 +1,4 @@
class Foo {
construct real() {}
}
class Foo {}
// Classes do not get an argument-less "new()" if they define a constructor.
// Classes do not get a constructor by default.
var foo = Foo.new() // expect runtime error: Foo metaclass does not implement 'new()'.
-11
View File
@@ -1,11 +0,0 @@
// Tests that Object implements new(). The only way to call that is through a
// super() call in a subclass, so this does that.
class Foo {
construct new() {
super() // Should not cause a no method error.
IO.print("ok")
}
}
Foo.new() // expect: ok
+1
View File
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
write { IO.print(_field) }
}
+2
View File
@@ -1,4 +1,6 @@
class Foo {
construct new() {}
set(a, b, c, d, e) {
_a = a
_b = b
+4
View File
@@ -1,9 +1,13 @@
class Outer {
construct new() {}
method {
_field = "outer"
IO.print(_field) // expect: outer
class Inner {
construct new() {}
method {
_field = "inner"
IO.print(_field) // expect: inner
+1
View File
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
write { IO.print(_field) } // Compile a use of the field...
init { _field = "value" } // ...before an assignment to it.
}
@@ -1,4 +1,6 @@
class Foo {
construct new() {}
getter {
IO.print("getter")
}
@@ -13,6 +15,8 @@ class Foo {
}
class Bar is Foo {
construct new() {}
test {
getter // expect: getter
setter = "value" // expect: setter
@@ -1,4 +1,6 @@
class Foo {
construct new() {}
getter {
IO.print("getter")
}
@@ -1,4 +1,6 @@
class Foo {
construct new() {}
bar { "getter" }
test {
@@ -1,4 +1,6 @@
class Foo {
construct new() {}
bar=(value) {
IO.print("setter")
return value
@@ -1,4 +1,6 @@
class Outer {
construct new() {}
getter {
IO.print("outer getter")
}
@@ -17,6 +19,8 @@ class Outer {
method("arg") // expect: outer method
class Inner {
construct new() {}
getter {
IO.print("inner getter")
}
@@ -1,4 +1,6 @@
class Foo {
construct new() {}
foo(a, b) {
_field1 = a
_field2 = b
@@ -11,6 +13,8 @@ class Foo {
}
class Bar is Foo {
construct new() {}
bar(a, b) {
_field1 = a
_field2 = b
@@ -6,6 +6,7 @@ class Foo {
}
class Bar is Foo {
construct new() {}
methodOnBar { IO.print("bar") }
method(a, b) { IO.print("bar") }
method(a, b, c, d) { IO.print("bar") }
+9 -3
View File
@@ -1,6 +1,12 @@
class A {}
class B is A {}
class C is B {}
class A {
construct new() {}
}
class B is A {
construct new() {}
}
class C is B {
construct new() {}
}
var a = A.new()
var b = B.new()
var c = C.new()
+1
View File
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
method { "getter" }
method() { "no args" }
method(a) { a }
+1
View File
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
bar {}
}
+1
View File
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
thisHasAMethodNameThatIsExactly64CharactersLongWhichIsTheMaximum {
return "result"
}
+1
View File
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
method000 { 1 }
method001 { 1 }
method002 { 1 }
+1
View File
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
method(a, b) { "method " + a + " " + b }
[a, b] { "subscript " + a + " " + b }
}
+3 -1
View File
@@ -1,3 +1,5 @@
class Foo {}
class Foo {
construct new() {}
}
Foo.new().someUnknownMethod // expect runtime error: Foo does not implement 'someUnknownMethod'.
@@ -1,3 +1,5 @@
class Foo {}
class Foo {
construct new() {}
}
Foo.new().someUnknownMethod(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) // expect runtime error: Foo does not implement 'someUnknownMethod(_,_,_,_,_,_,_,_,_,_,_)'.
@@ -1,3 +1,5 @@
class Foo {}
class Foo {
construct new() {}
}
Foo.new().someUnknownMethod(1, 2) // expect runtime error: Foo does not implement 'someUnknownMethod(_,_)'.
@@ -1,3 +1,5 @@
class Foo {}
class Foo {
construct new() {}
}
Foo.new().someUnknownMethod(1) // expect runtime error: Foo does not implement 'someUnknownMethod(_)'.
+2
View File
@@ -1,4 +1,6 @@
class Foo {
construct new() {}
+(other) { "infix + " + other }
-(other) { "infix - " + other }
*(other) { "infix * " + other }
+1
View File
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
bar { "on instance" }
static bar { "on metaclass" }
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
[a] { "1-subscript " + a }
[a, b] { "2-subscript " + a + " " + b }
[a, b, c] { "3-subscript " + a + " " + b + " " + c }
@@ -1,8 +1,10 @@
class Foo {
construct new() {}
static bar { Bar.new() }
}
class Bar {
construct new() {}
static foo { Foo.new() }
}
@@ -1,6 +1,8 @@
var Global = "global"
class Foo {
construct new() {}
method {
IO.print(Global)
}
@@ -1,4 +1,6 @@
class Foo {
construct new() {}
method {
IO.print(Global)
}
+2
View File
@@ -1,4 +1,6 @@
class Foo {
construct new() {}
method {
return "ok"
IO.print("bad")
+2
View File
@@ -1,4 +1,6 @@
class Foo {
construct new() {}
bar=(value) {
IO.print(value)
}
+1
View File
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
bar=(value) { "result" }
}
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
bar=(value) { IO.print("set") }
bar { IO.print("get") }
}
@@ -1,4 +1,6 @@
class Foo {
construct new() {}
set(a, b, c, d, e) {
__a = a
__b = b
@@ -1,9 +1,13 @@
class Outer {
construct new() {}
static staticMethod {
__field = "outer"
IO.print(__field) // expect: outer
class Inner {
construct new() {}
static staticMethod {
__field = "inner"
IO.print(__field) // expect: inner
@@ -19,6 +23,8 @@ class Outer {
IO.print(__field) // expect: outer
class Inner {
construct new() {}
instanceMethod {
__field = "inner"
IO.print(__field) // expect: inner
@@ -5,6 +5,8 @@ class Base {
}
class Derived is Base {
construct new() {}
foo(a) {
IO.print("Derived.bar(a)")
super
@@ -5,6 +5,8 @@ class Base {
}
class Derived is Base {
construct new() {}
bar {
IO.print("Derived.bar")
super.foo
@@ -5,6 +5,8 @@ class Base {
}
class Derived is Base {
construct new() {}
foo {
IO.print("Derived.foo")
super.foo
+1
View File
@@ -3,6 +3,7 @@ class Base {
}
class Derived is Base {
construct new() {}
getClosure { Fn.new { super.toString } }
toString { "Derived" }
}
+2
View File
@@ -5,6 +5,8 @@ class Base {
}
class Derived is Base {
construct new() {}
foo {
IO.print("Derived.foo")
super
@@ -7,6 +7,8 @@ class A {
class B is A {}
class C is B {
construct new() {}
foo {
IO.print("C.foo")
super.foo
@@ -1,6 +1,7 @@
class Base {}
class Derived is Base {
construct new() {}
foo { super.doesNotExist(1) } // expect runtime error: Base does not implement 'doesNotExist(_)'.
}
@@ -6,6 +6,8 @@ class A {
toString { "A.toString" }
}
class B is A {}
class B is A {
construct new() {}
}
IO.print(B.new().callSuperToString) // expect: instance of B
@@ -4,6 +4,8 @@ class A {
toString { "A.toString" }
}
class B is A {}
class B is A {
construct new() {}
}
IO.print(B.new().callSuperToString) // expect: instance of B
+1
View File
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
getClosure { Fn.new { toString } }
toString { "Foo" }
}
+4
View File
@@ -1,4 +1,6 @@
class Outer {
construct new() {}
method {
IO.print(this) // expect: Outer
@@ -6,6 +8,8 @@ class Outer {
IO.print(this) // expect: Outer
class Inner {
construct new() {}
method {
IO.print(this) // expect: Inner
}
+1
View File
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
getClosure { Fn.new { Fn.new { Fn.new { toString } } } }
toString { "Foo" }
}
+1
View File
@@ -1,4 +1,5 @@
class Foo {
construct new() {}
bar { this }
baz { "baz" }
}
@@ -1,4 +1,6 @@
class Foo {
construct new() {}
bar {
var a = "a"
IO.print(a) // expect: a
@@ -1,6 +1,8 @@
var foo = "variable"
class Foo {
construct new() {}
foo { "method" }
method {