Skip to content

Commit

Permalink
type annotations for variable tuple unpacking, better error messages (#…
Browse files Browse the repository at this point in the history
…22611)

* type annotations for variable tuple unpacking, better error messages

closes #17989, closes nim-lang/RFCs#339

* update grammar

* fix test
  • Loading branch information
metagn authored Sep 1, 2023
1 parent b3912c2 commit ba158d7
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 8 deletions.
9 changes: 7 additions & 2 deletions compiler/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2287,7 +2287,7 @@ proc parseTypeDef(p: var Parser): PNode =
setEndInfo()

proc parseVarTuple(p: var Parser): PNode =
#| varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')'
#| varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' (':' optInd typeDescExpr)?
#| varTuple = varTupleLhs '=' optInd expr
result = newNodeP(nkVarTuple, p)
getTok(p) # skip '('
Expand All @@ -2304,9 +2304,14 @@ proc parseVarTuple(p: var Parser): PNode =
if p.tok.tokType != tkComma: break
getTok(p)
skipComment(p, a)
result.add(p.emptyNode) # no type desc
optPar(p)
eat(p, tkParRi)
if p.tok.tokType == tkColon:
getTok(p)
optInd(p, result)
result.add(parseTypeDesc(p, fullExpr = true))
else:
result.add(p.emptyNode) # no type desc
setEndInfo()

proc parseVariable(p: var Parser): PNode =
Expand Down
6 changes: 4 additions & 2 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1834,9 +1834,11 @@ proc makeTupleAssignments(c: PContext; n: PNode): PNode =
let lhs = n[0]
let value = semExprWithType(c, n[1], {efTypeAllowed})
if value.typ.kind != tyTuple:
localError(c.config, n[1].info, errXExpected, "tuple")
localError(c.config, n[1].info, errTupleUnpackingTupleExpected %
[typeToString(value.typ, preferDesc)])
elif lhs.len != value.typ.len:
localError(c.config, n.info, errWrongNumberOfVariables)
localError(c.config, n.info, errTupleUnpackingDifferentLengths %
[$lhs.len, typeToString(value.typ, preferDesc), $value.typ.len])
result = newNodeI(nkStmtList, n.info)

let temp = newSym(skTemp, getIdent(c.cache, "tmpTupleAsgn"), c.idgen, getCurrOwner(c), n.info)
Expand Down
10 changes: 8 additions & 2 deletions compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -597,14 +597,20 @@ proc globalVarInitCheck(c: PContext, n: PNode) =
if n.isLocalVarSym or n.kind in nkCallKinds and usesLocalVar(n):
localError(c.config, n.info, errCannotAssignToGlobal)

const
errTupleUnpackingTupleExpected = "tuple expected for tuple unpacking, but got '$1'"
errTupleUnpackingDifferentLengths = "tuple with $1 elements expected, but got '$2' with $3 elements"

proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSymKind, origResult: var PNode): PNode =
## expand tuple unpacking assignments into new var/let/const section
##
## mirrored with semexprs.makeTupleAssignments
if typ.kind != tyTuple:
localError(c.config, a.info, errXExpected, "tuple")
localError(c.config, a.info, errTupleUnpackingTupleExpected %
[typeToString(typ, preferDesc)])
elif a.len-2 != typ.len:
localError(c.config, a.info, errWrongNumberOfVariables)
localError(c.config, a.info, errTupleUnpackingDifferentLengths %
[$(a.len-2), typeToString(typ, preferDesc), $typ.len])
var
tempNode: PNode = nil
lastDef: PNode
Expand Down
2 changes: 1 addition & 1 deletion doc/grammar.txt
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
&IND{>} stmt
typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue
indAndComment?
varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')'
varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' (':' optInd typeDescExpr)?
varTuple = varTupleLhs '=' optInd expr
colonBody = colcom stmt postExprBlocks?
variable = (varTuple / identColonEquals) colonBody? indAndComment
Expand Down
2 changes: 1 addition & 1 deletion tests/errmsgs/tassignunpack.nim
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
var a, b = 0
(a, b) = 1 #[tt.Error
^ 'tuple' expected]#
^ tuple expected for tuple unpacking, but got 'int literal(1)']#
17 changes: 17 additions & 0 deletions tests/parser/ttupleunpack.nim
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,20 @@ block: # unary assignment unpacking
var a: int
(a,) = (1,)
doAssert a == 1

block: # type annotations
block: # basic
let (a, b): (int, int) = (1, 2)
doAssert (a, b) == (1, 2)
block: # type inference
let (a, b): (byte, float) = (1, 2)
doAssert (a, b) == (1.byte, 2.0)
block: # type mismatch
doAssert not (compiles do:
let (a, b): (int, string) = (1, 2))
block: # nested
let (a, (b, c)): (int, (int, int)) = (1, (2, 3))
doAssert (a, b, c) == (1, 2, 3)
block: # nested type inference
let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
doAssert (a, b, c) == (1.byte, 2.0, cstring"abc")

0 comments on commit ba158d7

Please sign in to comment.