mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-08 14:03:23 +00:00
type annotations for variable tuple unpacking, better error messages (#22611)
* type annotations for variable tuple unpacking, better error messages
closes #17989, closes https://github.com/nim-lang/RFCs/issues/339
* update grammar
* fix test
(cherry picked from commit ba158d73dc)
This commit is contained in:
@@ -2286,7 +2286,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 '('
|
||||
@@ -2303,9 +2303,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 =
|
||||
|
||||
@@ -1824,9 +1824,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)
|
||||
|
||||
@@ -590,14 +590,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)']#
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user