Skip to content

Commit

Permalink
compiler/checker: return same as gc error using non-bool with && and ||
Browse files Browse the repository at this point in the history
This change implements the commit
golang/go@23573d0 in Scriggo.

For #385
  • Loading branch information
gazerro committed Jun 29, 2021
1 parent f23e424 commit 491f18c
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 31 deletions.
74 changes: 43 additions & 31 deletions compiler/checker_expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1029,42 +1029,54 @@ func (tc *typechecker) binaryOp(expr1 ast.Expression, op ast.OperatorType, expr2
}
return nil, fmt.Errorf("cannot compare type %s to type %s", t2, t1)
}
} else if t1.Untyped() != t2.Untyped() {
// Make both typed.
if t1.Untyped() {
c, err := tc.convert(t1, expr1, t2.Type)
if err != nil {
if err == errNotRepresentable {
err = fmt.Errorf("cannot convert %v (type %s) to type %s", t1.Constant, t1, t2)
}
return nil, err
} else {
// Return a better error message if one of the operands is not boolean.
// See https://github.com/golang/go/commit/23573d0ea225d4b93ccd2b946b1de121c3a6cee5
if op == ast.OperatorAnd || op == ast.OperatorOr {
if t1.Nil() || t1.Type.Kind() != reflect.Bool {
return nil, fmt.Errorf("operator %s not defined on %s", op, t1)
}
if t1.Nil() {
if op != ast.OperatorEqual && op != ast.OperatorNotEqual &&
op != ast.OperatorContains && op != ast.OperatorNotContains {
return nil, fmt.Errorf("operator %s not defined on %s", op, t2.Type.Kind())
}
return untypedBoolTypeInfo, nil
if t2.Nil() || t2.Type.Kind() != reflect.Bool {
return nil, fmt.Errorf("operator %s not defined on %s", op, t2)
}
t1.setValue(t2.Type)
t1 = &typeInfo{Type: t2.Type, Constant: c}
} else {
c, err := tc.convert(t2, expr2, t1.Type)
if err != nil {
if err == errNotRepresentable {
err = fmt.Errorf("cannot convert %v (type %s) to type %s", t2.Constant, t2, t1)
}
if t1.Untyped() != t2.Untyped() {
// Make both typed.
if t1.Untyped() {
c, err := tc.convert(t1, expr1, t2.Type)
if err != nil {
if err == errNotRepresentable {
err = fmt.Errorf("cannot convert %v (type %s) to type %s", t1.Constant, t1, t2)
}
return nil, err
}
return nil, err
}
if t2.Nil() {
if op != ast.OperatorEqual && op != ast.OperatorNotEqual &&
op != ast.OperatorContains && op != ast.OperatorNotContains {
return nil, fmt.Errorf("operator %s not defined on %s", op, t1.Type.Kind())
if t1.Nil() {
if op != ast.OperatorEqual && op != ast.OperatorNotEqual &&
op != ast.OperatorContains && op != ast.OperatorNotContains {
return nil, fmt.Errorf("operator %s not defined on %s", op, t2.Type.Kind())
}
return untypedBoolTypeInfo, nil
}
t1.setValue(t2.Type)
t1 = &typeInfo{Type: t2.Type, Constant: c}
} else {
c, err := tc.convert(t2, expr2, t1.Type)
if err != nil {
if err == errNotRepresentable {
err = fmt.Errorf("cannot convert %v (type %s) to type %s", t2.Constant, t2, t1)
}
return nil, err
}
if t2.Nil() {
if op != ast.OperatorEqual && op != ast.OperatorNotEqual &&
op != ast.OperatorContains && op != ast.OperatorNotContains {
return nil, fmt.Errorf("operator %s not defined on %s", op, t1.Type.Kind())
}
return untypedBoolTypeInfo, nil
}
return untypedBoolTypeInfo, nil
t2.setValue(t1.Type)
t2 = &typeInfo{Type: t1.Type, Constant: c}
}
t2.setValue(t1.Type)
t2 = &typeInfo{Type: t1.Type, Constant: c}
}
}

Expand Down
5 changes: 5 additions & 0 deletions compiler/checker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,11 @@ var checkerExprErrors = []struct {
{`x.F`, tierr(1, 2, `x.F undefined (type int has no field or method F)`), map[string]*typeInfo{"x": tiInt()}},
{`x.f`, tierr(1, 2, `x.f undefined (type compiler.definedStruct has no field or method f)`), map[string]*typeInfo{"x": definedStructTypeInfo}},
{`nil.X`, tierr(1, 1, `use of untyped nil`), nil},

// Non-bool operands used with || and &&.
{`true || "foo"`, tierr(1, 6, `invalid operation: true || "foo" (operator || not defined on untyped string)`), nil},
{`len([]int{}) && false`, tierr(1, 14, `invalid operation: len([]int{}) && false (operator && not defined on int)`), nil},
{`nil || 2 == 2.0`, tierr(1, 5, `invalid operation: nil || 2 == 2.0 (operator || not defined on nil)`), nil},
}

func TestCheckerExpressionErrors(t *testing.T) {
Expand Down

0 comments on commit 491f18c

Please sign in to comment.