lang: ast, interfaces: Improve speculation safety checks

We want to speculate in more cases, so make sure that speculation is
safe!
This commit is contained in:
James Shubin
2025-03-06 16:53:57 -05:00
parent aea894a706
commit f87c550be1
2 changed files with 19 additions and 0 deletions

View File

@@ -10484,6 +10484,13 @@ func (obj *ExprVar) SetType(typ *types.Type) error {
func (obj *ExprVar) Type() (*types.Type, error) { func (obj *ExprVar) Type() (*types.Type, error) {
// TODO: should this look more like Type() in ExprCall or vice-versa? // TODO: should this look more like Type() in ExprCall or vice-versa?
if obj.scope == nil { // avoid a possible nil panic if we speculate here
if obj.typ == nil {
return nil, errwrap.Wrapf(interfaces.ErrTypeCurrentlyUnknown, obj.String())
}
return obj.typ, nil
}
// Return the type if it is already known statically... It is useful for // Return the type if it is already known statically... It is useful for
// type unification to have some extra info early. // type unification to have some extra info early.
expr, exists := obj.scope.Variables[obj.Name] expr, exists := obj.scope.Variables[obj.Name]
@@ -10602,6 +10609,10 @@ func (obj *ExprVar) SetValue(value types.Value) error {
// it can lookup in the previous set scope which expression this points to, and // it can lookup in the previous set scope which expression this points to, and
// then it can call Value on that expression. // then it can call Value on that expression.
func (obj *ExprVar) Value() (types.Value, error) { func (obj *ExprVar) Value() (types.Value, error) {
if obj.scope == nil { // avoid a possible nil panic if we speculate here
return nil, errwrap.Wrapf(interfaces.ErrValueCurrentlyUnknown, obj.String())
}
expr, exists := obj.scope.Variables[obj.Name] expr, exists := obj.scope.Variables[obj.Name]
if !exists { if !exists {
return nil, fmt.Errorf("var `%s` does not exist in scope", obj.Name) return nil, fmt.Errorf("var `%s` does not exist in scope", obj.Name)

View File

@@ -36,8 +36,16 @@ import (
const ( const (
// ErrTypeCurrentlyUnknown is returned from the Type() call on Expr if // ErrTypeCurrentlyUnknown is returned from the Type() call on Expr if
// unification didn't run successfully and the type isn't obvious yet. // unification didn't run successfully and the type isn't obvious yet.
// Note that it is perfectly legal to return any error, but this one can
// be used instead of inventing your own.
ErrTypeCurrentlyUnknown = util.Error("type is currently unknown") ErrTypeCurrentlyUnknown = util.Error("type is currently unknown")
// ErrValueCurrentlyUnknown is returned from the Value() call on Expr if
// we're speculating and we don't know a value statically. Note that it
// is perfectly legal to return any error, but this one can be used
// instead of inventing your own.
ErrValueCurrentlyUnknown = util.Error("value is currently unknown")
// ErrExpectedFileMissing is returned when a file that is used by an // ErrExpectedFileMissing is returned when a file that is used by an
// import is missing. This might signal the downloader, or it might // import is missing. This might signal the downloader, or it might
// signal a permanent error. // signal a permanent error.