@@ -89,11 +89,25 @@ type StackFrame struct {
8989}
9090
9191type RuntimeError struct {
92+ Type string
9293 Message string
9394 CodeFrame string
9495 Frames []StackFrame
9596}
9697
98+ type assertionFailureError struct {
99+ message string
100+ }
101+
102+ func (e * assertionFailureError ) Error () string {
103+ return e .message
104+ }
105+
106+ const (
107+ runtimeErrorTypeBase = "RuntimeError"
108+ runtimeErrorTypeAssertion = "AssertionError"
109+ )
110+
97111var (
98112 errLoopBreak = errors .New ("loop break" )
99113 errLoopNext = errors .New ("loop next" )
@@ -127,6 +141,37 @@ func (re *RuntimeError) Unwrap() error {
127141 return nil
128142}
129143
144+ func canonicalRuntimeErrorType (name string ) (string , bool ) {
145+ switch {
146+ case strings .EqualFold (name , runtimeErrorTypeBase ), strings .EqualFold (name , "Error" ):
147+ return runtimeErrorTypeBase , true
148+ case strings .EqualFold (name , runtimeErrorTypeAssertion ):
149+ return runtimeErrorTypeAssertion , true
150+ default :
151+ return "" , false
152+ }
153+ }
154+
155+ func classifyRuntimeErrorType (err error ) string {
156+ if err == nil {
157+ return runtimeErrorTypeBase
158+ }
159+ var assertionErr * assertionFailureError
160+ if errors .As (err , & assertionErr ) {
161+ return runtimeErrorTypeAssertion
162+ }
163+ if runtimeErr , ok := err .(* RuntimeError ); ok {
164+ if kind , known := canonicalRuntimeErrorType (runtimeErr .Type ); known {
165+ return kind
166+ }
167+ }
168+ return runtimeErrorTypeBase
169+ }
170+
171+ func newAssertionFailureError (message string ) error {
172+ return & assertionFailureError {message : message }
173+ }
174+
130175func (exec * Execution ) step () error {
131176 exec .steps ++
132177 if exec .quota > 0 && exec .steps > exec .quota {
@@ -152,6 +197,16 @@ func (exec *Execution) errorAt(pos Position, format string, args ...any) error {
152197}
153198
154199func (exec * Execution ) newRuntimeError (message string , pos Position ) error {
200+ return exec .newRuntimeErrorWithType (runtimeErrorTypeBase , message , pos )
201+ }
202+
203+ func (exec * Execution ) newRuntimeErrorWithType (kind string , message string , pos Position ) error {
204+ if canonical , ok := canonicalRuntimeErrorType (kind ); ok {
205+ kind = canonical
206+ } else {
207+ kind = runtimeErrorTypeBase
208+ }
209+
155210 frames := make ([]StackFrame , 0 , len (exec .callStack )+ 1 )
156211
157212 if len (exec .callStack ) > 0 {
@@ -172,7 +227,7 @@ func (exec *Execution) newRuntimeError(message string, pos Position) error {
172227 if exec .script != nil {
173228 codeFrame = formatCodeFrame (exec .script .source , pos )
174229 }
175- return & RuntimeError {Message : message , CodeFrame : codeFrame , Frames : frames }
230+ return & RuntimeError {Type : kind , Message : message , CodeFrame : codeFrame , Frames : frames }
176231}
177232
178233func (exec * Execution ) wrapError (err error , pos Position ) error {
@@ -182,7 +237,7 @@ func (exec *Execution) wrapError(err error, pos Position) error {
182237 if _ , ok := err .(* RuntimeError ); ok {
183238 return err
184239 }
185- return exec .newRuntimeError ( err .Error (), pos )
240+ return exec .newRuntimeErrorWithType ( classifyRuntimeErrorType ( err ), err .Error (), pos )
186241}
187242
188243func (exec * Execution ) pushReceiver (v Value ) {
@@ -1317,7 +1372,7 @@ func (exec *Execution) evalUntilStatement(stmt *UntilStmt, env *Env) (Value, boo
13171372func (exec * Execution ) evalTryStatement (stmt * TryStmt , env * Env ) (Value , bool , error ) {
13181373 val , returned , err := exec .evalStatements (stmt .Body , env )
13191374
1320- if err != nil && len (stmt .Rescue ) > 0 {
1375+ if err != nil && len (stmt .Rescue ) > 0 && runtimeErrorMatchesRescueType ( err , stmt . RescueTy ) {
13211376 rescueVal , rescueReturned , rescueErr := exec .evalStatements (stmt .Rescue , env )
13221377 if rescueErr != nil {
13231378 val = NewNil ()
@@ -1346,6 +1401,36 @@ func (exec *Execution) evalTryStatement(stmt *TryStmt, env *Env) (Value, bool, e
13461401 return val , returned , nil
13471402}
13481403
1404+ func runtimeErrorMatchesRescueType (err error , rescueTy * TypeExpr ) bool {
1405+ if rescueTy == nil {
1406+ return true
1407+ }
1408+ errKind := classifyRuntimeErrorType (err )
1409+ return rescueTypeMatchesErrorKind (rescueTy , errKind )
1410+ }
1411+
1412+ func rescueTypeMatchesErrorKind (ty * TypeExpr , errKind string ) bool {
1413+ if ty == nil {
1414+ return false
1415+ }
1416+ if ty .Kind == TypeUnion {
1417+ for _ , option := range ty .Union {
1418+ if rescueTypeMatchesErrorKind (option , errKind ) {
1419+ return true
1420+ }
1421+ }
1422+ return false
1423+ }
1424+ canonical , ok := canonicalRuntimeErrorType (ty .Name )
1425+ if ! ok {
1426+ return false
1427+ }
1428+ if canonical == runtimeErrorTypeBase {
1429+ return true
1430+ }
1431+ return canonical == errKind
1432+ }
1433+
13491434func (exec * Execution ) getMember (obj Value , property string , pos Position ) (Value , error ) {
13501435 switch obj .Kind () {
13511436 case KindHash , KindObject :
0 commit comments