Introduce an <error> type
This change renames the old Error type to Exception -- more consistent with our AST, etc. nodes anyway -- and introduces a new Error type ("<error>") to use when something during typechecking or binding fails. The old way led to errors like: error: MU504: tags.ts:32:18: Symbol 'Tag:push' not found error: MU522: tags.ts:32:8: Cannot invoke a non-function; 'any' is not a function This is because of cascading errors during type-checking; the symbol not found error means we cannot produce the right type for the function invoke that consumes it. But the 'any' part is weird. Instead, this new change produces: error: MU504: tags.ts:32:18: Symbol 'Tag:push' not found error: MU522: tags.ts:32:8: Cannot invoke a non-function; '<error>' is not a function It's slightly better. And furthermore, it gives us a leg to stand on someday should we decide to get smarter about detecting cascades and avoiding issuing the secondary error messages (we can just check for the Error type).
This commit is contained in:
parent
42bdcb7bca
commit
3eb72b62d5
6 changed files with 31 additions and 28 deletions
|
@ -201,13 +201,13 @@ func (ctx *Context) LookupTypeToken(node ast.Node, tok tokens.Type, require bool
|
|||
}
|
||||
}
|
||||
|
||||
// The type was not found; issue an error, and return Any so we can proceed with typechecking.
|
||||
// The type was not found; issue an error, and return an error type so we can proceed with typechecking.
|
||||
if ty == nil {
|
||||
if require {
|
||||
contract.Assert(reason != "")
|
||||
ctx.Diag.Errorf(errors.ErrorTypeNotFound.At(node), tok, reason)
|
||||
}
|
||||
ty = types.Any
|
||||
ty = types.Error
|
||||
}
|
||||
return ty
|
||||
}
|
||||
|
@ -221,9 +221,9 @@ func (ctx *Context) LookupFunctionType(node ast.Function) *symbols.FunctionType
|
|||
// If there was an explicit type, look it up.
|
||||
ptysym := ctx.LookupType(param.Type)
|
||||
|
||||
// If either the parameter's type was unknown, or the lookup failed (leaving an error), use the any type.
|
||||
// If either the parameter's type was unknown, or the lookup failed, sub in an error type.
|
||||
if ptysym == nil {
|
||||
ptysym = types.Any
|
||||
ptysym = types.Error
|
||||
}
|
||||
|
||||
params = append(params, ptysym)
|
||||
|
@ -287,7 +287,7 @@ func (ctx *Context) lookupBasicType(node ast.Node, tok tokens.Type, require bool
|
|||
if require {
|
||||
ctx.Diag.Errorf(errors.ErrorTypeNotFound.At(node), tok, "unrecognized primitive type name")
|
||||
}
|
||||
return types.Any
|
||||
return types.Error
|
||||
}
|
||||
|
||||
func (ctx *Context) checkModuleVisibility(node ast.Node, module *symbols.Module, member symbols.ModuleMember) {
|
||||
|
|
|
@ -305,7 +305,7 @@ func (a *astBinder) checkLoadLocationExpression(node *ast.LoadLocationExpression
|
|||
// Produce a type of the right kind from the target location.
|
||||
var ty symbols.Type
|
||||
if sym == nil {
|
||||
ty = types.Any
|
||||
ty = types.Error // no symbol found, use an error type.
|
||||
} else {
|
||||
for ty == nil {
|
||||
switch s := sym.(type) {
|
||||
|
@ -369,7 +369,7 @@ func (a *astBinder) checkInvokeFunctionExpression(node *ast.InvokeFunctionExpres
|
|||
a.b.ctx.RegisterType(node, funty.Return)
|
||||
} else {
|
||||
a.b.Diag().Errorf(errors.ErrorCannotInvokeNonFunction.At(node), ty)
|
||||
a.b.ctx.RegisterType(node, types.Any)
|
||||
a.b.ctx.RegisterType(node, types.Error)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,22 +9,25 @@ import (
|
|||
|
||||
// All of the primitive types.
|
||||
var (
|
||||
Any = symbols.NewPrimitiveType("any")
|
||||
Bool = symbols.NewPrimitiveType("bool")
|
||||
Number = symbols.NewPrimitiveType("number")
|
||||
String = symbols.NewPrimitiveType("string")
|
||||
Null = symbols.NewPrimitiveType("null")
|
||||
Error = symbols.NewPrimitiveType("error")
|
||||
Any = symbols.NewPrimitiveType("any")
|
||||
Bool = symbols.NewPrimitiveType("bool")
|
||||
Number = symbols.NewPrimitiveType("number")
|
||||
String = symbols.NewPrimitiveType("string")
|
||||
Null = symbols.NewPrimitiveType("null")
|
||||
Exception = symbols.NewPrimitiveType("exception")
|
||||
|
||||
// Error is not meant to use directly; it is used internally in the compiler when something bad happens.
|
||||
Error = symbols.NewPrimitiveType("<error>")
|
||||
)
|
||||
|
||||
// Primitives contains a map of all primitive types, keyed by their token/name.
|
||||
var Primitives = map[tokens.TypeName]symbols.Type{
|
||||
Any.Nm: Any,
|
||||
Bool.Nm: Bool,
|
||||
Number.Nm: Number,
|
||||
String.Nm: String,
|
||||
Null.Nm: Null,
|
||||
Error.Nm: Error,
|
||||
Any.Nm: Any,
|
||||
Bool.Nm: Bool,
|
||||
Number.Nm: Number,
|
||||
String.Nm: String,
|
||||
Null.Nm: Null,
|
||||
Exception.Nm: Exception,
|
||||
}
|
||||
|
||||
// Common weakly typed types.
|
||||
|
|
|
@ -74,9 +74,9 @@ func (a *Allocator) NewPointer(t symbols.Type, ptr *rt.Pointer) *rt.Object {
|
|||
return obj
|
||||
}
|
||||
|
||||
// NewError creates a new exception with the given message.
|
||||
func (a *Allocator) NewError(message string, args ...interface{}) *rt.Object {
|
||||
obj := rt.NewErrorObject(message, args...)
|
||||
// NewException creates a new exception with the given message.
|
||||
func (a *Allocator) NewException(message string, args ...interface{}) *rt.Object {
|
||||
obj := rt.NewExceptionObject(message, args...)
|
||||
a.onNewObject(obj)
|
||||
return obj
|
||||
}
|
||||
|
|
|
@ -471,7 +471,7 @@ func (e *evaluator) evalTryCatchFinally(node *ast.TryCatchFinally) *Unwind {
|
|||
for _, catch := range *node.CatchBlocks {
|
||||
ex := e.ctx.RequireVariable(catch.Exception).(*symbols.LocalVariable)
|
||||
exty := ex.Type()
|
||||
contract.Assert(types.CanConvert(exty, types.Error))
|
||||
contract.Assert(types.CanConvert(exty, types.Exception))
|
||||
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 Unwind information (thereby "handling" the in-flight exception).
|
||||
|
@ -703,7 +703,7 @@ func (e *evaluator) evalArrayLiteral(node *ast.ArrayLiteral) (*rt.Object, *Unwin
|
|||
sz := int(sze.NumberValue())
|
||||
if sz < 0 {
|
||||
// If the size is less than zero, raise a new error.
|
||||
return nil, NewThrowUnwind(e.alloc.NewError("Invalid array size (must be >= 0)"))
|
||||
return nil, NewThrowUnwind(e.alloc.NewException("Invalid array size (must be >= 0)"))
|
||||
}
|
||||
arr = make([]rt.Value, sz)
|
||||
}
|
||||
|
@ -718,7 +718,7 @@ func (e *evaluator) evalArrayLiteral(node *ast.ArrayLiteral) (*rt.Object, *Unwin
|
|||
} else if len(*node.Elements) > *sz {
|
||||
// The element count exceeds the size; raise an error.
|
||||
return nil, NewThrowUnwind(
|
||||
e.alloc.NewError("Invalid number of array elements; expected <=%v, got %v",
|
||||
e.alloc.NewException("Invalid number of array elements; expected <=%v, got %v",
|
||||
*sz, len(*node.Elements)))
|
||||
}
|
||||
|
||||
|
|
|
@ -191,10 +191,10 @@ func NewPointerObject(t symbols.Type, ptr *Pointer) *Object {
|
|||
return NewPrimitiveObject(ptrt, ptr)
|
||||
}
|
||||
|
||||
// NewErrorObject creates a new exception with the given message.
|
||||
func NewErrorObject(message string, args ...interface{}) *Object {
|
||||
// NewExceptionObject creates a new exception with the given message.
|
||||
func NewExceptionObject(message string, args ...interface{}) *Object {
|
||||
// TODO: capture a stack trace.
|
||||
return NewPrimitiveObject(types.Error, fmt.Sprintf(message, args...))
|
||||
return NewPrimitiveObject(types.Exception, fmt.Sprintf(message, args...))
|
||||
}
|
||||
|
||||
// NewConstantObject returns a new object with the right type and value, based on some constant data.
|
||||
|
|
Loading…
Reference in a new issue