Fix a handful of things
* Move checking the validity of a derived class with no constructor from evlauation to binding (verification). Furthermore, make it a verification error instead of an assert, and make the checking complete (i.e., don't just check for the existence of a base class, also check for the existence of a ctor, recursively). * Refactor the constructor accessor out of the evaluator and make it a method, Ctor, on the Function abstraction. * Add a recursive Module population case to property initialization. * Only treat the top-most frame in a module's initializer as belonging to the module scope. This avoids interpreting block scopes within that initializer as belonging to the module scope. Ultimately, it's up to the language compiler to decide where to place scopes, but this gives it more precise control over module scoping. * Assert against unsupported named/star/keyword args in CocoPy.
This commit is contained in:
parent
346bcca77c
commit
2c6ad1e331
6 changed files with 54 additions and 20 deletions
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/golang/glog"
|
||||
|
||||
"github.com/pulumi/coconut/pkg/compiler/ast"
|
||||
"github.com/pulumi/coconut/pkg/compiler/errors"
|
||||
"github.com/pulumi/coconut/pkg/compiler/symbols"
|
||||
"github.com/pulumi/coconut/pkg/util/contract"
|
||||
)
|
||||
|
@ -37,6 +38,17 @@ func (b *binder) bindClassDefinition(class *symbols.Class) {
|
|||
}
|
||||
|
||||
b.bindClassMembers(class)
|
||||
|
||||
if class.Ctor() == nil {
|
||||
// If there is no ctor defined, ensure that all base classes have no ctor also.
|
||||
ext := class.Extends
|
||||
for ext != nil {
|
||||
if ext.Ctor() != nil {
|
||||
b.Diag().Errorf(errors.ErrorDerivedClassHasNoCtor.At(class.Node), class, ext)
|
||||
}
|
||||
ext = ext.Base()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *binder) bindClassMembers(class *symbols.Class) {
|
||||
|
|
|
@ -40,4 +40,5 @@ var (
|
|||
"Unexpected object target for this static or module load operation")
|
||||
ErrorInvalidCast = newError(529, "Illegal cast from '%v' to '%v'; this can never succeed")
|
||||
ErrorModuleAliasTargetNotFound = newError(530, "Module alias target '%v' was not found (from '%v')")
|
||||
ErrorDerivedClassHasNoCtor = newError(531, "Class '%v' has no constructor, but its base class '%v' does")
|
||||
)
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
package symbols
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/pulumi/coconut/pkg/compiler/ast"
|
||||
"github.com/pulumi/coconut/pkg/diag"
|
||||
"github.com/pulumi/coconut/pkg/tokens"
|
||||
|
@ -39,11 +41,22 @@ func (node *Class) Base() Type { return node.Extends }
|
|||
func (node *Class) TypeName() tokens.TypeName { return node.Nm }
|
||||
func (node *Class) TypeToken() tokens.Type { return node.Tok }
|
||||
func (node *Class) TypeMembers() ClassMemberMap { return node.Members }
|
||||
func (node *Class) Sealed() bool { return node.Node.Sealed != nil && *node.Node.Sealed }
|
||||
func (node *Class) Abstract() bool { return node.Node.Abstract != nil && *node.Node.Abstract }
|
||||
func (node *Class) Record() bool { return node.Node.Record != nil && *node.Node.Record }
|
||||
func (node *Class) Interface() bool { return node.Node.Interface != nil && *node.Node.Interface }
|
||||
func (node *Class) String() string { return string(node.Token()) }
|
||||
func (node *Class) Ctor() Function {
|
||||
if ctor, has := node.TypeMembers()[tokens.ClassConstructorFunction]; has {
|
||||
ctormeth, ismeth := ctor.(*ClassMethod)
|
||||
contract.Assertf(ismeth,
|
||||
"Expected ctor %v to be a class method; got %v", ctor, reflect.TypeOf(ctor))
|
||||
contract.Assertf(ctormeth.Sig.Return == nil,
|
||||
"Expected ctor %v to have a nil return; got %v", ctor, ctormeth.Sig.Return)
|
||||
return ctormeth
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (node *Class) Sealed() bool { return node.Node.Sealed != nil && *node.Node.Sealed }
|
||||
func (node *Class) Abstract() bool { return node.Node.Abstract != nil && *node.Node.Abstract }
|
||||
func (node *Class) Record() bool { return node.Node.Record != nil && *node.Node.Record }
|
||||
func (node *Class) Interface() bool { return node.Node.Interface != nil && *node.Node.Interface }
|
||||
func (node *Class) String() string { return string(node.Token()) }
|
||||
|
||||
// HasInit returns true if this module has an initialzer associated with it.
|
||||
func (node *Class) HasInit() bool { return node.GetInit() != nil }
|
||||
|
|
|
@ -14,6 +14,7 @@ type Type interface {
|
|||
TypeName() tokens.TypeName // this type's name identifier.
|
||||
TypeToken() tokens.Type // this type's (qualified) token.
|
||||
TypeMembers() ClassMemberMap // this type's members.
|
||||
Ctor() Function // this type's constructor (or nil if none).
|
||||
Record() bool // true if this is a record type.
|
||||
Interface() bool // true if this is an interface type.
|
||||
}
|
||||
|
@ -37,6 +38,7 @@ func (node *PrimitiveType) Base() Type { return nil }
|
|||
func (node *PrimitiveType) TypeName() tokens.TypeName { return node.Nm }
|
||||
func (node *PrimitiveType) TypeToken() tokens.Type { return tokens.Type(node.Nm) }
|
||||
func (node *PrimitiveType) TypeMembers() ClassMemberMap { return noClassMembers }
|
||||
func (node *PrimitiveType) Ctor() Function { return nil }
|
||||
func (node *PrimitiveType) Record() bool { return false }
|
||||
func (node *PrimitiveType) Interface() bool { return false }
|
||||
func (node *PrimitiveType) String() string { return string(node.Token()) }
|
||||
|
@ -63,6 +65,7 @@ func (node *PointerType) Base() Type { return nil }
|
|||
func (node *PointerType) TypeName() tokens.TypeName { return node.Nm }
|
||||
func (node *PointerType) TypeToken() tokens.Type { return node.Tok }
|
||||
func (node *PointerType) TypeMembers() ClassMemberMap { return noClassMembers }
|
||||
func (node *PointerType) Ctor() Function { return nil }
|
||||
func (node *PointerType) Record() bool { return false }
|
||||
func (node *PointerType) Interface() bool { return false }
|
||||
func (node *PointerType) String() string { return string(node.Token()) }
|
||||
|
@ -101,6 +104,7 @@ func (node *ArrayType) Base() Type { return nil }
|
|||
func (node *ArrayType) TypeName() tokens.TypeName { return node.Nm }
|
||||
func (node *ArrayType) TypeToken() tokens.Type { return node.Tok }
|
||||
func (node *ArrayType) TypeMembers() ClassMemberMap { return noClassMembers }
|
||||
func (node *ArrayType) Ctor() Function { return nil }
|
||||
func (node *ArrayType) Record() bool { return false }
|
||||
func (node *ArrayType) Interface() bool { return false }
|
||||
func (node *ArrayType) String() string { return string(node.Token()) }
|
||||
|
@ -140,6 +144,7 @@ func (node *MapType) Base() Type { return nil }
|
|||
func (node *MapType) TypeName() tokens.TypeName { return node.Nm }
|
||||
func (node *MapType) TypeToken() tokens.Type { return node.Tok }
|
||||
func (node *MapType) TypeMembers() ClassMemberMap { return noClassMembers }
|
||||
func (node *MapType) Ctor() Function { return nil }
|
||||
func (node *MapType) Record() bool { return false }
|
||||
func (node *MapType) Interface() bool { return false }
|
||||
func (node *MapType) String() string { return string(node.Token()) }
|
||||
|
@ -179,6 +184,7 @@ func (node *FunctionType) Base() Type { return nil }
|
|||
func (node *FunctionType) TypeName() tokens.TypeName { return node.Nm }
|
||||
func (node *FunctionType) TypeToken() tokens.Type { return node.Tok }
|
||||
func (node *FunctionType) TypeMembers() ClassMemberMap { return noClassMembers }
|
||||
func (node *FunctionType) Ctor() Function { return nil }
|
||||
func (node *FunctionType) Record() bool { return false }
|
||||
func (node *FunctionType) Interface() bool { return false }
|
||||
func (node *FunctionType) String() string { return string(node.Token()) }
|
||||
|
@ -234,6 +240,7 @@ func (node *ModuleType) TypeToken() tokens.Type {
|
|||
return tokens.Type(string(node.Module.Token()) + ".modtype")
|
||||
}
|
||||
func (node *ModuleType) TypeMembers() ClassMemberMap { return noClassMembers }
|
||||
func (node *ModuleType) Ctor() Function { return nil }
|
||||
func (node *ModuleType) Record() bool { return false }
|
||||
func (node *ModuleType) Interface() bool { return false }
|
||||
func (node *ModuleType) String() string { return string(node.Token()) }
|
||||
|
@ -270,6 +277,7 @@ func (node *PrototypeType) TypeToken() tokens.Type {
|
|||
return tokens.Type(string(node.Type.Token()) + ".prototype")
|
||||
}
|
||||
func (node *PrototypeType) TypeMembers() ClassMemberMap { return noClassMembers }
|
||||
func (node *PrototypeType) Ctor() Function { return nil }
|
||||
func (node *PrototypeType) Record() bool { return false }
|
||||
func (node *PrototypeType) Interface() bool { return false }
|
||||
func (node *PrototypeType) String() string { return string(node.Token()) }
|
||||
|
|
|
@ -274,6 +274,10 @@ func (e *evaluator) initProperty(this *rt.Object, properties rt.PropertyMap,
|
|||
}
|
||||
ptr := properties.InitAddr(key, obj, false)
|
||||
return ptr, m.Readonly()
|
||||
case *symbols.Module:
|
||||
// A module resolves to its module object.
|
||||
modobj := e.getModuleObject(m)
|
||||
return properties.InitAddr(key, modobj, false), false
|
||||
case *symbols.Class:
|
||||
// A class resolves to its prototype object.
|
||||
proto := e.getPrototypeObject(m)
|
||||
|
@ -808,7 +812,7 @@ func (e *evaluator) evalTryCatchFinally(node *ast.TryCatchFinally) *rt.Unwind {
|
|||
exty := ex.Type()
|
||||
if types.CanConvert(thrown.Type(), exty) {
|
||||
// This type matched, so this handler will catch the exception. Set the exception variable,
|
||||
// evaluate the block, and swap the rt.Unwind information (thereby "handling" the in-flight exception).
|
||||
// evaluate the block, and swap the rt.Unwind information ("handling" the in-flight exception).
|
||||
e.pushScope(nil)
|
||||
e.locals.SetValue(ex, thrown)
|
||||
uw = e.evalBlock(catch.Block)
|
||||
|
@ -1528,10 +1532,11 @@ func (e *evaluator) getDynamicNameAddr(key tokens.Name, lval bool) *rt.Pointer {
|
|||
|
||||
// Finally, if neither of those existed, and this is the target of a load, allocate a slot.
|
||||
if pv == nil && lval {
|
||||
if e.fnc.SpecialModInit() {
|
||||
if e.fnc.SpecialModInit() && e.locals.Frame {
|
||||
pv = globals.Properties().GetAddr(pkey, true)
|
||||
} else {
|
||||
loc := symbols.NewSpecialVariableSym(key, types.Dynamic)
|
||||
e.ctx.Scope.MustRegister(loc)
|
||||
pv = e.locals.GetValueAddr(loc, true)
|
||||
}
|
||||
}
|
||||
|
@ -1552,13 +1557,7 @@ func (e *evaluator) evalNew(node diag.Diagable, t symbols.Type, args *[]ast.Expr
|
|||
obj := e.newObject(node, t)
|
||||
|
||||
// See if there is a constructor method. If there isn't, we just return the fresh object.
|
||||
if ctor, has := t.TypeMembers()[tokens.ClassConstructorFunction]; has {
|
||||
ctormeth, isfunc := ctor.(*symbols.ClassMethod)
|
||||
contract.Assertf(isfunc,
|
||||
"Expected ctor %v to be a class method; got %v", ctor, reflect.TypeOf(ctor))
|
||||
contract.Assertf(ctormeth.Sig.Return == nil,
|
||||
"Expected ctor %v to have a nil return; got %v", ctor, ctormeth.Sig.Return)
|
||||
|
||||
if ctor := t.Ctor(); ctor != nil {
|
||||
// Evaluate the arguments in order.
|
||||
var argobjs []*rt.Object
|
||||
if args != nil {
|
||||
|
@ -1572,15 +1571,12 @@ func (e *evaluator) evalNew(node diag.Diagable, t symbols.Type, args *[]ast.Expr
|
|||
}
|
||||
|
||||
// Now dispatch the function call using the fresh object as the constructor's `this` argument.
|
||||
if _, uw := e.evalCall(node, ctormeth, obj, argobjs...); uw != nil {
|
||||
if _, uw := e.evalCall(node, ctor, obj, argobjs...); uw != nil {
|
||||
return nil, uw
|
||||
}
|
||||
} else {
|
||||
contract.Assertf(args == nil || len(*args) == 0,
|
||||
"No constructor found for %v, yet the new expression had %v args", t, len(*args))
|
||||
class, isclass := t.(*symbols.Class)
|
||||
contract.Assertf(!isclass || class.Extends == nil,
|
||||
"No constructor found for %v, yet there is a base class; chaining must be done manually", t)
|
||||
}
|
||||
|
||||
// Finally, ensure that all readonly properties are frozen now.
|
||||
|
|
|
@ -474,10 +474,14 @@ class Transformer:
|
|||
|
||||
def transform_Call(self, node):
|
||||
# TODO: support named arguments, starargs, etc.
|
||||
assert node.keywords is None or len(node.keywords) == 0, "Named args not yet supported"
|
||||
assert node.starargs is None or len(node.starargs) == 0, "Star args not yet supported"
|
||||
assert node.kwargs is None or len(node.kwargs) == 0, "KW args not yet supported"
|
||||
func = self.transform_expr(node.func)
|
||||
args = list()
|
||||
for arg in args:
|
||||
args.append(self.transform_expr(arg))
|
||||
if node.args:
|
||||
for arg in node.args:
|
||||
args.append(self.transform_expr(arg))
|
||||
return ast.InvokeFunctionExpression(func, args, loc=self.loc_from(node))
|
||||
|
||||
def transform_Compare(self, node):
|
||||
|
|
Loading…
Reference in a new issue