@@ -166,7 +166,6 @@ export default class TypeCheckTraversal extends AstTraversal {
166166 }
167167
168168 node . type = node . index === 0 ? ( node . tuple . type as TupleType ) . leftType : ( node . tuple . type as TupleType ) . rightType ;
169-
170169 return node ;
171170 }
172171
@@ -179,9 +178,7 @@ export default class TypeCheckTraversal extends AstTraversal {
179178 expectInt ( node , node . start . type ) ;
180179 expectInt ( node , node . end . type ) ;
181180
182- // TODO: Proper typing
183- node . type = node . element . type instanceof BytesType ? new BytesType ( ) : PrimitiveType . STRING ;
184-
181+ node . type = inferSliceType ( node ) ;
185182 return node ;
186183 }
187184
@@ -385,6 +382,10 @@ function expectParameters(node: NodeWithParameters, actual: Type[], expected: Ty
385382
386383// We only call this function for the split operator, so we assume that the node.op is SPLIT
387384function inferTupleType ( node : BinaryOpNode ) : Type {
385+ if ( node . right instanceof IntLiteralNode && Number ( node . right . value ) < 0 ) {
386+ throw new IndexOutOfBoundsError ( node ) ;
387+ }
388+
388389 // string.split() -> string, string
389390 if ( node . left . type === PrimitiveType . STRING ) {
390391 return new TupleType ( PrimitiveType . STRING , PrimitiveType . STRING ) ;
@@ -406,10 +407,6 @@ function inferTupleType(node: BinaryOpNode): Type {
406407 return new TupleType ( new BytesType ( splitIndex ) , new BytesType ( ) ) ;
407408 }
408409
409- if ( splitIndex < 0 ) {
410- throw new IndexOutOfBoundsError ( node ) ;
411- }
412-
413410 if ( splitIndex > expressionType . bound ) {
414411 throw new IndexOutOfBoundsError ( node ) ;
415412 }
@@ -420,3 +417,48 @@ function inferTupleType(node: BinaryOpNode): Type {
420417 new BytesType ( expressionType . bound ! - splitIndex ) ,
421418 ) ;
422419}
420+
421+ function inferSliceType ( node : SliceNode ) : Type {
422+ if ( node . start instanceof IntLiteralNode && Number ( node . start . value ) < 0 ) {
423+ throw new IndexOutOfBoundsError ( node ) ;
424+ }
425+
426+ if ( node . end instanceof IntLiteralNode && Number ( node . end . value ) < 0 ) {
427+ throw new IndexOutOfBoundsError ( node ) ;
428+ }
429+
430+ // string.slice() -> string
431+ if ( node . element . type === PrimitiveType . STRING ) {
432+ return PrimitiveType . STRING ;
433+ }
434+
435+ // If the expression is not a bytes type, then it must be a different compatible type (e.g. sig/pubkey)
436+ const expressionType = node . element . type instanceof BytesType ? node . element . type : new BytesType ( ) ;
437+
438+ if ( expressionType . bound !== undefined ) {
439+ if ( node . start instanceof IntLiteralNode && Number ( node . start . value ) >= expressionType . bound ) {
440+ throw new IndexOutOfBoundsError ( node ) ;
441+ }
442+
443+ if ( node . end instanceof IntLiteralNode && Number ( node . end . value ) > expressionType . bound ) {
444+ throw new IndexOutOfBoundsError ( node ) ;
445+ }
446+ }
447+
448+ // bytes.slice(variable, variable) -> bytes
449+ // bytes.slice(NumberLiteral, variable) -> bytes
450+ // bytes.slice(variable, NumberLiteral) -> bytes
451+ if ( ! ( node . start instanceof IntLiteralNode ) || ! ( node . end instanceof IntLiteralNode ) ) {
452+ return new BytesType ( ) ;
453+ }
454+
455+ const start = Number ( node . start . value ) ;
456+ const end = Number ( node . end . value ) ;
457+
458+ if ( start > end ) {
459+ throw new IndexOutOfBoundsError ( node ) ;
460+ }
461+
462+ // bytes.slice(NumberLiteral start, NumberLiteral end) -> bytes(end - start)
463+ return new BytesType ( end - start ) ;
464+ }
0 commit comments