@@ -227,6 +227,7 @@ TypeSystem.prototype.visitIf = function (node, scope) {
227227
228228 this . visitExpression ( node . cond , scope )
229229
230+ // Handle the main if block
230231 var blockScope = new Scope ( scope )
231232 this . visitBlock ( node . block , blockScope )
232233
@@ -239,6 +240,11 @@ TypeSystem.prototype.visitIf = function (node, scope) {
239240 this . visitBlock ( elseIf . block , elseIfBlockScope )
240241 }
241242 }
243+ // Handle the else block if present
244+ if ( node . elseBlock ) {
245+ var elseBlockScope = new Scope ( scope )
246+ this . visitBlock ( node . elseBlock , elseBlockScope )
247+ }
242248}
243249
244250TypeSystem . prototype . visitWhile = function ( node , scope ) {
@@ -251,12 +257,19 @@ TypeSystem.prototype.visitWhile = function (node, scope) {
251257}
252258
253259TypeSystem . prototype . visitReturn = function ( node , scope , parentNode ) {
254- if ( node . expr === null || node . expr === undefined ) {
255- throw new TypeError ( 'Cannot handle empty Return' )
260+ if ( node . expr === undefined ) {
261+ throw new TypeError ( 'Cannot handle undefined expression in Return' )
262+ }
263+ var exprType = null
264+ if ( node . expr === null ) {
265+ var voidType = this . root . getLocal ( 'Void' )
266+ exprType = new types . Instance ( voidType )
267+ } else {
268+ var expr = node . expr
269+ exprType = this . resolveExpression ( expr , scope )
256270 }
257- var expr = node . expr
258- var exprType = this . resolveExpression ( expr , scope )
259271 node . type = exprType
272+ // Handle the parent block if present
260273 if ( parentNode ) {
261274 if ( ! ( ( parentNode instanceof AST . Block ) || ( parentNode instanceof AST . Root ) ) ) {
262275 throw new TypeError ( 'Expected Block or Root as parent of Return' , node )
@@ -267,7 +280,7 @@ TypeSystem.prototype.visitReturn = function (node, scope, parentNode) {
267280 }
268281 // The expression should return an instance, we'll have to unbox that
269282 assertInstanceOf ( exprType , types . Instance , 'Expected Instance as argument to Return' )
270- parentNode . returnType = exprType . type
283+ parentNode . returnType = exprType ? exprType . type : null
271284 }
272285}
273286
@@ -464,12 +477,20 @@ function getAllReturnTypes (block) {
464477 if ( block . returnType ) { returnTypes . push ( block . returnType ) }
465478
466479 block . statements . forEach ( function ( stmt ) {
480+ var types = null
467481 switch ( stmt . constructor ) {
468482 case AST . If :
483+ types = getAllReturnTypes ( stmt . block )
484+ if ( stmt . elseBlock ) {
485+ types = types . concat ( getAllReturnTypes ( stmt . elseBlock ) )
486+ }
487+ returnTypes = returnTypes . concat ( types )
488+ break
469489 case AST . While :
470490 case AST . For :
471- var subblockTypes = getAllReturnTypes ( stmt . block )
472- returnTypes = returnTypes . concat ( subblockTypes )
491+ types = getAllReturnTypes ( stmt . block )
492+ returnTypes = returnTypes . concat ( types )
493+ break
473494 }
474495 } )
475496 return returnTypes
@@ -479,6 +500,8 @@ TypeSystem.prototype.visitFunction = function (node, parentScope, immediate) {
479500 if ( node . type ) { return node . type }
480501 var self = this
481502 var type = new types . Function ( this . rootObject )
503+ // Set the type of this node to an instance of the function type
504+ node . type = new types . Instance ( type )
482505
483506 if ( node . ret ) {
484507 type . ret = this . resolveType ( node . ret )
@@ -510,24 +533,63 @@ TypeSystem.prototype.visitFunction = function (node, parentScope, immediate) {
510533 // Begin by visiting our block
511534 this . visitBlock ( node . block , functionScope )
512535
536+ // Get all possible return types of this function (recursively collects
537+ // returning child blocks).
538+ var returnTypes = getAllReturnTypes ( node . block )
539+
540+ // If there is a declared return type then we need to check that all the found
541+ // returns match that type
513542 if ( type . ret ) {
514- // Get all possible return types of this function (recursively collects
515- // returning child blocks).
516- var returnTypes = getAllReturnTypes ( node . block )
517543 returnTypes . forEach ( function ( returnType ) {
518544 if ( ! type . ret . equals ( returnType ) ) {
519545 throw new TypeError ( 'Type returned by function does not match declared return type' )
520546 }
521547 } )
522- // Box the type and return
523- node . type = new types . Instance ( type )
524548 return
525549 }
526- throw new TypeError ( 'Inferred return types not supported yet' , node )
527550
528- // Then we'll find all the `return`s and get their types
529- var returns = [ ]
530- // TODO: Actually visit and resolve return types
551+ // Otherwise we need to try to unify the returns; this could potentially be
552+ // a very expensive operation, so we'll warn the user if they do too many
553+ if ( returnTypes . length > 4 ) {
554+ var returns = returnTypes . length ,
555+ file = node . _file ,
556+ line = node . _line ,
557+ warning = "Warning: Encountered " + returns + " return statements in function\n" +
558+ " Computing type unions can be expensive and should be used carefully!\n" +
559+ " at " + file + ":" + line + "\n"
560+ process . stderr . write ( warning )
561+ }
562+ // Slow quadratic uniqueness checking to reduce the set of return types
563+ // to distinct ones
564+ var reducedTypes = uniqueWithComparator ( returnTypes , function ( a , b ) {
565+ return a . equals ( b )
566+ } )
567+ if ( reducedTypes . length > 1 ) {
568+ var t = reducedTypes . map ( function ( t ) { return t . inspect ( ) } ) . join ( ', ' )
569+ throw new TypeError ( 'Too many return types (have ' + t + ')' , node )
570+ }
571+ // Final return type
572+ var returnType = null
573+ if ( reducedTypes . length !== 0 ) {
574+ returnType = reducedTypes [ 0 ]
575+ }
576+ // Update the type definition (if there we 0 then it will be null which is
577+ // Void in the type-system)
578+ type . ret = returnType
579+ }
580+
581+ function uniqueWithComparator ( array , comparator ) {
582+ var acc = [ ] ,
583+ length = array . length
584+ for ( var i = 0 ; i < length ; i ++ ) {
585+ for ( var j = i + 1 ; j < length ; j ++ ) {
586+ var a = array [ i ] ,
587+ b = array [ j ]
588+ if ( comparator ( a , b ) ) { j = ++ i }
589+ }
590+ acc . push ( array [ i ] )
591+ }
592+ return acc
531593}
532594
533595
@@ -609,7 +671,8 @@ TypeSystem.prototype.visitChain = function (node, scope) {
609671 // Get the Instance type of the passing argument node
610672 var itemArgInstance = itemArg . type
611673 // Verify that the passed argument's type is an Instance box
612- assertInstanceOf ( itemArgInstance , types . Instance , 'Expected Instance as function argument' )
674+ var failureMessage = 'Expected Instance as function argument, got: ' + itemArgInstance . inspect ( )
675+ assertInstanceOf ( itemArgInstance , types . Instance , failureMessage )
613676 // Unbox the instance
614677 var itemArgType = itemArgInstance . type
615678 // Then get the type from the function definition to compare to the
@@ -665,4 +728,3 @@ TypeSystem.prototype.visitMulti = function (node, scope) {
665728
666729
667730module . exports = { TypeSystem : TypeSystem }
668-
0 commit comments