From 9fd63a239ca3d463bdd14ecf9ed429ec9e7105d6 Mon Sep 17 00:00:00 2001 From: Mark Hills Date: Tue, 19 Apr 2016 07:27:28 -0400 Subject: [PATCH] Specialized assignment into tuples with literal subscripts, resolves #948 --- .../library/lang/rascal/types/CheckTypes.rsc | 50 ++++++++++++++----- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/src/org/rascalmpl/library/lang/rascal/types/CheckTypes.rsc b/src/org/rascalmpl/library/lang/rascal/types/CheckTypes.rsc index c55dc32709f..f27cb3135ac 100644 --- a/src/org/rascalmpl/library/lang/rascal/types/CheckTypes.rsc +++ b/src/org/rascalmpl/library/lang/rascal/types/CheckTypes.rsc @@ -1240,18 +1240,20 @@ public CheckResult checkExp(Expression exp:(Expression)` [ <{Expre else return markLocationType(c,exp@\loc,Symbol::\value()); } else if (isTupleType(t1)) { - if (size(tl) != 1) + if (size(tl) != 1) { return markLocationFailed(c,exp@\loc,makeFailType("Expected only 1 subscript for a tuple expression, not ",exp@\loc)); - else if (!isIntType(tl[0])) + } else if (!isIntType(tl[0])) { return markLocationFailed(c,exp@\loc,makeFailType("Expected subscript of type int, not ",exp@\loc)); - else if ((Expression)`` := head(eslist)) { + } else if ((Expression)`` := head(eslist)) { tupleIndex = toInt(""); - if (tupleIndex < 0 || tupleIndex >= size(getTupleFields(t1))) + if (tupleIndex < 0 || tupleIndex >= size(getTupleFields(t1))) { return markLocationFailed(c,exp@\loc,makeFailType("Tuple index must be between 0 and ",exp@\loc)); - else + } else { return markLocationType(c,exp@\loc,getTupleFields(t1)[tupleIndex]); - } else + } + } else { return markLocationType(c,exp@\loc,lubList(getTupleFields(t1))); + } } else if (isStrType(t1)) { if (size(tl) != 1) return markLocationFailed(c,exp@\loc,makeFailType("Expected only 1 subscript for a string expression, not ",exp@\loc)); @@ -5144,6 +5146,7 @@ public anno set[int] AssignableTree@defs; @doc{Allows AssignableTree nodes to be annotated with types.} public anno Symbol AssignableTree@otype; public anno Symbol AssignableTree@atype; +public anno int AssignableTree@literalIndex; @doc{Result of building the assignable tree.} alias ATResult = tuple[Configuration, AssignableTree]; @@ -5197,22 +5200,36 @@ public ATResult buildAssignableTree(Assignable assn:(Assignable)` < c, atree > = buildAssignableTree(ar, false, c); < c, tsub > = checkExp(sub, c); - if (isFailType(atree@atype) || isFailType(tsub)) + if (isFailType(atree@atype) || isFailType(tsub)) { return < c, subscriptNode(atree,tsub)[@atype=collapseFailTypes({atree@atype,tsub})][@at=assn@\loc] >; + } if (!concreteType(atree@atype)) { failtype = makeFailType("Assignable must have an actual type before subscripting", assn@\loc); return < c, subscriptNode(atree,tsub)[@atype=failtype][@at=assn@\loc] >; } - if (isListType(atree@atype) && isIntType(tsub)) + if (isListType(atree@atype) && isIntType(tsub)) { return < c, subscriptNode(atree,tsub)[@atype=getListElementType(atree@atype)][@at=assn@\loc] >; + } - if (isNodeType(atree@atype) && isIntType(tsub)) + if (isNodeType(atree@atype) && isIntType(tsub)) { return < c, subscriptNode(atree,tsub)[@atype=Symbol::\value()][@at=assn@\loc] >; + } - if (isTupleType(atree@atype) && isIntType(tsub)) - return < c, subscriptNode(atree,tsub)[@atype=Symbol::\value()][@at=assn@\loc] >; + if (isTupleType(atree@atype) && isIntType(tsub)) { + if ((Expression)`` := sub) { + tupleIndex = toInt(""); + if (tupleIndex < 0 || tupleIndex >= size(getTupleFields(atree@atype))) { + failtype = makeFailType("Tuple index must be between 0 and ", sub@\loc); + return < c, subscriptNode(atree,tsub)[@atype=failtype][@at=assn@\loc] >; + } else { + return < c, subscriptNode(atree,tsub)[@atype=getTupleFields(atree@atype)[tupleIndex]][@at=assn@\loc][@literalIndex=tupleIndex] >; + } + } else { + return < c, subscriptNode(atree,tsub)[@atype=Symbol::\value()][@at=assn@\loc] >; + } + } if (isMapType(atree@atype)) { if (avar:variableNode(vname) := atree) { @@ -5232,8 +5249,9 @@ public ATResult buildAssignableTree(Assignable assn:(Assignable)` return < c, subscriptNode(atree,tsub)[@atype=(isMapType(atree@atype))?getMapRangeType(atree@atype):atree@atype][@at=assn@\loc] >; } - if (isRelType(atree@atype) && size(getRelFields(atree@atype)) == 2 && subtype(tsub,getRelFields(atree@atype)[0])) + if (isRelType(atree@atype) && size(getRelFields(atree@atype)) == 2 && subtype(tsub,getRelFields(atree@atype)[0])) { return < c, subscriptNode(atree,tsub)[@atype=getRelFields(atree@atype)[1]][@at=assn@\loc] >; + } return < c, subscriptNode(atree,tsub)[@atype=makeFailType("Cannot subscript assignable of type ",assn@\loc)][@at=assn@\loc] >; } @@ -5709,7 +5727,13 @@ public ATResult bindAssignable(AssignableTree atree:subscriptNode(AssignableTree // in range, all we can infer about the resulting type is that, since // we could assign to each field, each field could have a type based // on the lub of the existing field type and the subject type. - < c, receiver > = bindAssignable(receiver, \tuple([lub(tupleFields[idx],st) | idx <- index(tupleFields)]), c); + if ( (atree@literalIndex)?) { + updatedTupleFields = tupleFields; + updatedTupleFields[atree@literalIndex] = lub(atree@atype,st); + < c, receiver > = bindAssignable(receiver, \tuple(updatedTupleFields), c); + } else { + < c, receiver > = bindAssignable(receiver, \tuple([lub(tupleFields[idx],st) | idx <- index(tupleFields)]), c); + } return < c, atree[receiver=receiver][@otype=receiver@otype][@atype=Symbol::\value()] >; } else if (isMapType(receiver@atype)) { < c, receiver > = bindAssignable(receiver, \map(getMapDomainType(receiver@atype), lub(st,getMapRangeType(receiver@atype))), c);