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:
joeduffy 2017-02-09 12:58:34 -08:00
parent 42bdcb7bca
commit 3eb72b62d5
6 changed files with 31 additions and 28 deletions

View file

@ -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) {

View file

@ -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)
}
}

View file

@ -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.

View file

@ -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
}

View file

@ -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)))
}

View file

@ -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.