@@ -8,7 +8,8 @@ type Type =
8
8
| { kind : 'union' ; of : NonUnion [ ] } // constraint: nothing in the union dominates anything else in the union
9
9
| { kind : 'list' ; of : Type }
10
10
| { kind : 'record' } // TODO more precision
11
- | { kind : 'completion' ; of : Type }
11
+ | { kind : 'normal completion' ; of : Type }
12
+ | { kind : 'abrupt completion' }
12
13
| { kind : 'real' }
13
14
| { kind : 'integer' }
14
15
| { kind : 'non-negative integer' }
@@ -50,7 +51,7 @@ const simpleKinds = new Set<Type['kind']>([
50
51
const dominateGraph : Partial < Record < Type [ 'kind' ] , Type [ 'kind' ] [ ] > > = {
51
52
// @ts -expect-error TS does not know about __proto__
52
53
__proto__ : null ,
53
- record : [ 'completion' ] ,
54
+ record : [ 'normal completion' , 'abrupt completion'] ,
54
55
real : [
55
56
'integer' ,
56
57
'non-negative integer' ,
@@ -97,7 +98,7 @@ export function dominates(a: Type, b: Type): boolean {
97
98
}
98
99
if (
99
100
( a . kind === 'list' && b . kind === 'list' ) ||
100
- ( a . kind === 'completion' && b . kind === 'completion' )
101
+ ( a . kind === 'normal completion' && b . kind === 'normal completion' )
101
102
) {
102
103
return dominates ( a . of , b . of ) ;
103
104
}
@@ -170,7 +171,7 @@ export function join(a: Type, b: Type): Type {
170
171
}
171
172
if (
172
173
( a . kind === 'list' && b . kind === 'list' ) ||
173
- ( a . kind === 'completion' && b . kind === 'completion' )
174
+ ( a . kind === 'normal completion' && b . kind === 'normal completion' )
174
175
) {
175
176
return { kind : a . kind , of : join ( a . of , b . of ) } ;
176
177
}
@@ -193,7 +194,7 @@ export function meet(a: Type, b: Type): Type {
193
194
}
194
195
if (
195
196
( a . kind === 'list' && b . kind === 'list' ) ||
196
- ( a . kind === 'completion' && b . kind === 'completion' )
197
+ ( a . kind === 'normal completion' && b . kind === 'normal completion' )
197
198
) {
198
199
return { kind : a . kind , of : meet ( a . of , b . of ) } ;
199
200
}
@@ -224,13 +225,16 @@ export function serialize(type: Type): string {
224
225
case 'record' : {
225
226
return 'Record' ;
226
227
}
227
- case 'completion' : {
228
+ case 'normal completion' : {
228
229
if ( type . of . kind === 'never' ) {
229
- return 'an abrupt Completion Record ' ;
230
+ return 'never ' ;
230
231
} else if ( type . of . kind === 'unknown' ) {
231
- return 'a Completion Record ' ;
232
+ return 'a normal completion ' ;
232
233
}
233
- return 'a Completion Record normally holding ' + serialize ( type . of ) ;
234
+ return 'a normal completion containing ' + serialize ( type . of ) ;
235
+ }
236
+ case 'abrupt completion' : {
237
+ return 'an abrupt completion' ;
234
238
}
235
239
case 'real' : {
236
240
return 'mathematical value' ;
@@ -338,8 +342,17 @@ export function typeFromExpr(expr: Expr, biblio: Biblio): Type {
338
342
const remaining = stripWhitespace ( items . slice ( 1 ) ) ;
339
343
if ( remaining . length === 1 && [ 'call' , 'sdo-call' ] . includes ( remaining [ 0 ] . name ) ) {
340
344
const callType = typeFromExpr ( remaining [ 0 ] , biblio ) ;
341
- if ( callType . kind === 'completion' ) {
342
- return callType . of ;
345
+ if ( isCompletion ( callType ) ) {
346
+ const normal : Type =
347
+ callType . kind === 'normal completion'
348
+ ? callType . of
349
+ : callType . kind === 'abrupt completion'
350
+ ? // the expression `? _abrupt_` strictly speaking has type `never`
351
+ // however we mostly use `never` to mean user error, so use unknown instead
352
+ // this should only ever happen after `Return`
353
+ { kind : 'unknown' }
354
+ : callType . of . find ( k => k . kind === 'normal completion' ) ?. of ?? { kind : 'unknown' } ;
355
+ return normal ;
343
356
}
344
357
}
345
358
}
@@ -418,15 +431,23 @@ export function typeFromExprType(type: BiblioType): Type {
418
431
}
419
432
case 'completion' : {
420
433
if ( type . completionType === 'abrupt' ) {
421
- return { kind : 'completion' , of : { kind : 'never' } } ;
422
- }
423
- return {
424
- kind : 'completion' ,
425
- of :
434
+ return { kind : 'abrupt completion' } ;
435
+ } else {
436
+ const normalType : Type =
426
437
type . typeOfValueIfNormal == null
427
438
? { kind : 'unknown' }
428
- : typeFromExprType ( type . typeOfValueIfNormal ) ,
429
- } ;
439
+ : typeFromExprType ( type . typeOfValueIfNormal ) ;
440
+ if ( type . completionType === 'normal' ) {
441
+ return { kind : 'normal completion' , of : normalType } ;
442
+ } else if ( type . completionType === 'mixed' ) {
443
+ return {
444
+ kind : 'union' ,
445
+ of : [ { kind : 'normal completion' , of : normalType } , { kind : 'abrupt completion' } ] ,
446
+ } ;
447
+ } else {
448
+ throw new Error ( 'unreachable: completion kind ' + type . completionType ) ;
449
+ }
450
+ }
430
451
}
431
452
case 'opaque' : {
432
453
const text = type . type ;
@@ -514,6 +535,16 @@ export function typeFromExprType(type: BiblioType): Type {
514
535
return { kind : 'unknown' } ;
515
536
}
516
537
538
+ export function isCompletion (
539
+ type : Type ,
540
+ ) : type is Type & { kind : 'normal completion' | 'abrupt completion' | 'union' } {
541
+ return (
542
+ type . kind === 'normal completion' ||
543
+ type . kind === 'abrupt completion' ||
544
+ ( type . kind === 'union' && type . of . some ( isCompletion ) )
545
+ ) ;
546
+ }
547
+
517
548
export function stripWhitespace ( items : NonSeq [ ] ) {
518
549
items = [ ...items ] ;
519
550
while ( items [ 0 ] ?. name === 'text' && / ^ \s + $ / . test ( items [ 0 ] . contents ) ) {
0 commit comments