From 7ab1aafc6bbb94da8becc1a42bd1bf839df90746 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 9 Jun 2018 11:52:03 +0300 Subject: [PATCH 01/13] stdlib work --- lib/pure/collections/deques.nim | 57 +++++++++++++++++++++++++++++---- lib/pure/streams.nim | 16 +++++++-- lib/system/nimscript.nim | 2 +- 3 files changed, 66 insertions(+), 9 deletions(-) diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index 328308a9b3..409240b377 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -38,7 +38,7 @@ ## Note: For inter thread communication use ## a `Channel `_ instead. -import math +import math, typetraits type Deque*[T] = object @@ -160,16 +160,18 @@ proc peekLast*[T](deq: Deque[T]): T {.inline.} = emptyCheck(deq) result = deq.data[(deq.tail - 1) and deq.mask] -template default[T](t: typedesc[T]): T = - var v: T - v +template destroy(x: untyped) = + when defined(nimNewRuntime) and not supportsCopyMem(type(x)): + `=destroy`(x) + else: + reset(x) proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} = ## Remove and returns the first element of the `deq`. emptyCheck(deq) dec deq.count result = deq.data[deq.head] - deq.data[deq.head] = default(type(result)) + destroy(deq.data[deq.head]) deq.head = (deq.head + 1) and deq.mask proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} = @@ -178,7 +180,34 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} = dec deq.count deq.tail = (deq.tail - 1) and deq.mask result = deq.data[deq.tail] - deq.data[deq.tail] = default(type(result)) + destroy(deq.data[deq.tail]) + +proc clear*[T](deq: var Deque[T]) {.inline.} = + ## Resets the deque so that it is empty. + for el in mitems(deq): destroy(el) + deq.count = 0 + deq.tail = deq.head + +proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) = + ## Remove `fromFirst` elements from the front of the deque and + ## `fromLast` elements from the back. If the supplied number of + ## elements exceeds the total number of elements in the deque, + ## the deque will remain empty. + ## + ## Any user defined destructors + if fromFirst + fromLast > deq.count: + clear(deq) + return + + for i in 0 ..< fromFirst: + destroy(deq.data[deq.head]) + deq.head = (deq.head + 1) and deq.mask + + for i in 0 ..< fromLast: + destroy(deq.data[deq.tail]) + deq.tail = (deq.tail - 1) and deq.mask + + dec deq.count, fromFirst + fromLast proc `$`*[T](deq: Deque[T]): string = ## Turn a deque into its string representation. @@ -215,6 +244,22 @@ when isMainModule: assert deq.find(6) >= 0 assert deq.find(789) < 0 + block: + var d = initDeque[int](1) + d.addLast 7 + d.addLast 8 + d.addLast 10 + d.addFirst 5 + d.addFirst 2 + d.addFirst 1 + d.addLast 20 + d.shrink(fromLast = 2) + doAssert($d == "[1, 2, 5, 7, 8]") + d.shrink(2, 1) + doAssert($d == "[5, 7]") + d.shrink(2, 2) + doAssert d.len == 0 + for i in -2 .. 10: if i in deq: assert deq.contains(i) and deq.find(i) >= 0 diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index a0bba05a4f..1ab73faea3 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -135,6 +135,11 @@ proc write*(s: Stream, x: string) = else: if x.len > 0: writeData(s, cstring(x), x.len) +proc write*(s: Stream, args: varargs[string, `$`]) = + ## writes one or more strings to the the stream. No length fields or + ## terminating zeros are written. + for str in args: write(s, str) + proc writeLine*(s: Stream, args: varargs[string, `$`]) = ## writes one or more strings to the the stream `s` followed ## by a new line. No length field or terminating zero is written. @@ -266,8 +271,8 @@ proc peekStr*(s: Stream, length: int): TaintedString = proc readLine*(s: Stream, line: var TaintedString): bool = ## reads a line of text from the stream `s` into `line`. `line` must not be ## ``nil``! May throw an IO exception. - ## A line of text may be delimited by ``CR``, ``LF`` or - ## ``CRLF``. The newline character(s) are not part of the returned string. + ## A line of text may be delimited by ```LF`` or ``CRLF``. + ## The newline character(s) are not part of the returned string. ## Returns ``false`` if the end of the file has been reached, ``true`` ## otherwise. If ``false`` is returned `line` contains no new data. line.string.setLen(0) @@ -317,6 +322,13 @@ proc peekLine*(s: Stream): TaintedString = defer: setPosition(s, pos) result = readLine(s) +iterator lines*(s: Stream): TaintedString = + ## Iterates over every line in the stream. + ## The iteration is based on ``readLine``. + var line: TaintedString + while s.readLine(line): + yield line + when not defined(js): type diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index 7671e5962d..5bf69dd94b 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -182,7 +182,7 @@ template checkOsError = if err.len > 0: raise newException(OSError, err) template log(msg: string, body: untyped) = - if mode == ScriptMode.Verbose or mode == ScriptMode.Whatif: + if mode in {ScriptMode.Verbose, ScriptMode.Whatif}: echo "[NimScript] ", msg if mode != ScriptMode.WhatIf: body From 8633b1b3097e1627f16a52fc648f0e14c647a688 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 20 Apr 2018 12:54:06 +0300 Subject: [PATCH 02/13] Starting test recording the current state of the parser In the next commit, I'll introduce changes to the parser bringing consistent handling of all type modifiers (ref, ptr, var, static and type). The goal of this commit is to record precisely what is going to be changed (i.e. by allowing you to look at the diff). To preserve the diff, please don't squash upon merging. --- tests/parser/ttypemodifiers.nim | 481 ++++++++++++++++++++++++++++++++ 1 file changed, 481 insertions(+) create mode 100644 tests/parser/ttypemodifiers.nim diff --git a/tests/parser/ttypemodifiers.nim b/tests/parser/ttypemodifiers.nim new file mode 100644 index 0000000000..db7ab903f3 --- /dev/null +++ b/tests/parser/ttypemodifiers.nim @@ -0,0 +1,481 @@ +discard """ +nimout: ''' +StmtList + TypeSection + TypeDef + Ident "BarePtr" + Empty + PtrTy + TypeDef + Ident "GenericPtr" + Empty + PtrTy + Bracket + Ident "int" + TypeDef + Ident "PrefixPtr" + Empty + PtrTy + Ident "int" + TypeDef + Ident "PtrTuple" + Empty + PtrTy + Par + Ident "int" + Ident "string" + TypeDef + Ident "BareRef" + Empty + RefTy + TypeDef + Ident "GenericRef" + Empty + RefTy + Bracket + Ident "int" + TypeDef + Ident "RefTupleCl" + Empty + RefTy + TupleTy + TypeDef + Ident "RefTupleType" + Empty + RefTy + Par + Ident "int" + Ident "string" + TypeDef + Ident "RefTupleVars" + Empty + RefTy + Par + Ident "a" + Ident "b" + TypeDef + Ident "GenericStatic" + Empty + StaticTy + Ident "int" + TypeDef + Ident "PrefixStatic" + Empty + StaticExpr + Ident "int" + TypeDef + Ident "StaticTupleCl" + Empty + StaticExpr + TupleClassTy + TypeDef + Ident "StaticTuple" + Empty + StaticExpr + Par + Ident "int" + Ident "string" + TypeDef + Ident "BareType" + Empty + Ident "type" + TypeDef + Ident "GenericType" + Empty + BracketExpr + Ident "type" + Ident "float" + TypeDef + Ident "TypeTupleGen" + Empty + BracketExpr + Ident "type" + TupleClassTy + TypeDef + Ident "TypeInstance" + Empty + Command + Ident "type" + BracketExpr + Ident "Foo" + RefTy + TypeDef + Ident "bareTypeDesc" + Empty + Ident "typedesc" + TypeDef + Ident "TypeOfVar" + Empty + Call + Ident "type" + Ident "a" + TypeDef + Ident "TypeOfTuple1" + Empty + Call + Ident "type" + Ident "a" + TypeDef + Ident "TypeOfTuple2" + Empty + Call + Ident "type" + Ident "a" + Ident "b" + TypeDef + Ident "GenericTypedesc" + Empty + BracketExpr + Ident "typedesc" + Ident "int" + TypeDef + Ident "T" + Empty + Ident "type" + ProcDef + Ident "foo" + Empty + Empty + FormalParams + Ident "type" + IdentDefs + Ident "bareType" + Ident "type" + Empty + IdentDefs + Ident "genType" + BracketExpr + Ident "type" + Ident "int" + Empty + IdentDefs + Ident "typeInt" + Command + Ident "type" + Ident "int" + Empty + IdentDefs + Ident "typeIntAlt" + Call + Ident "type" + Ident "int" + Empty + IdentDefs + Ident "typeOfVar" + Call + Ident "type" + Ident "a" + Empty + IdentDefs + Ident "typeDotType" + DotExpr + Ident "foo" + Ident "type" + Empty + IdentDefs + Ident "genStatic" + StaticTy + Ident "int" + Empty + IdentDefs + Ident "staticInt" + StaticExpr + Ident "int" + Empty + IdentDefs + Ident "staticVal1" + StaticExpr + IntLit 10 + Empty + IdentDefs + Ident "staticVal2" + StaticExpr + Par + StrLit "str" + Empty + IdentDefs + Ident "staticVal3" + StaticExpr + StrLit "str" + Empty + IdentDefs + Ident "staticDotVal" + DotExpr + IntLit 10 + Ident "static" + Empty + IdentDefs + Ident "bareRef" + RefTy + Empty + IdentDefs + Ident "refTuple1" + RefTy + Par + Ident "int" + Empty + IdentDefs + Ident "refTuple1A" + RefTy + TupleConstr + Ident "int" + Empty + IdentDefs + Ident "refTuple2" + RefTy + Par + Ident "int" + Ident "string" + Empty + IdentDefs + Ident "genRef" + RefTy + Bracket + Ident "int" + Empty + IdentDefs + Ident "refInt" + RefTy + Ident "int" + Empty + IdentDefs + Ident "refCall" + RefTy + Par + Ident "a" + Empty + IdentDefs + Ident "macroCall1" + Command + Ident "foo" + Ident "bar" + Empty + IdentDefs + Ident "macroCall2" + Call + Ident "foo" + Ident "bar" + Empty + IdentDefs + Ident "macroCall3" + Call + DotExpr + Ident "foo" + Ident "bar" + Ident "baz" + Empty + IdentDefs + Ident "macroCall4" + Call + BracketExpr + Ident "foo" + Ident "bar" + Ident "baz" + Empty + IdentDefs + Ident "macroCall5" + Command + Ident "foo" + Command + Ident "bar" + Ident "baz" + IntLit 10 + Empty + Empty + StmtList + Asgn + Ident "staticTen" + StaticExpr + IntLit 10 + Asgn + Ident "staticA" + StaticExpr + Par + Ident "a" + Asgn + Ident "staticAspace" + StaticExpr + Par + Ident "a" + Asgn + Ident "staticAtuple" + StaticExpr + TupleConstr + Ident "a" + Asgn + Ident "staticTuple" + StaticExpr + Par + Ident "a" + Ident "b" + Asgn + Ident "staticTypeTuple" + StaticExpr + Par + Ident "int" + Ident "string" + Asgn + Ident "staticCall" + StaticExpr + Call + Ident "foo" + IntLit 1 + Asgn + Ident "staticStrCall" + StaticExpr + CallStrLit + Ident "foo" + RStrLit "x" + Asgn + Ident "staticChainCall" + StaticExpr + Command + Ident "foo" + Ident "bar" + Asgn + Ident "typeTen" + Command + Ident "type" + IntLit 10 + Asgn + Ident "typeA" + Call + Ident "type" + Ident "a" + Asgn + Ident "typeCall" + Command + Ident "type" + Call + Ident "foo" + IntLit 1 + Asgn + Ident "typeStrCall" + Command + Ident "type" + CallStrLit + Ident "foo" + RStrLit "x" + Asgn + Ident "typeChainCall" + Command + Ident "type" + Command + Ident "foo" + Ident "bar" + Asgn + Ident "normalChainCall" + Command + Ident "foo" + Command + Ident "bar" + Ident "baz" + Asgn + Ident "normalTupleCall2" + Call + Ident "foo" + Ident "a" + Ident "b" + StaticStmt + StmtList + Ident "singleStaticStmt" + StaticStmt + StmtList + Ident "staticStmtList1" + Ident "staticStmtList2" +''' +""" + +import macros + +dumpTree: + type + BarePtr = ptr + GenericPtr = ptr[int] + PrefixPtr = ptr int + PtrTuple = ptr (int, string) + BareRef = ref + GenericRef = ref[int] + RefTupleCl = ref tuple + RefTupleType = ref (int, string) + RefTupleVars = ref (a, b) + # BareStatic = static # Error: invalid indentation + GenericStatic = static[int] + PrefixStatic = static int + StaticTupleCl = static tuple + StaticTuple = static (int, string) + BareType = type + GenericType = type[float] + TypeTupleGen = type[tuple] + # TypeTupleCl = type tuple # Error: invalid indentation + TypeInstance = type Foo[ref] + bareTypeDesc = typedesc + TypeOfVar = type(a) + # TypeOfVarAlt= type (a) # Error: invalid indentation + TypeOfTuple1 = type(a,) + TypeOfTuple2 = type(a,b) + # TypeOfTuple1A = type (a,) # Error: invalid indentation + # TypeOfTuple2A = type (a,b) # Error: invalid indentation + # TypeTuple = type (int, string) # Error: invalid indentation + GenericTypedesc = typedesc[int] + T = type + + proc foo( + bareType : type, + genType : type[int], + typeInt : type int, + typeIntAlt : type(int), + typeOfVar : type(a), + typeDotType : foo.type, + # typeTupleCl : type tuple, # Error: ')' expected + # bareStatic : static, # Error: expression expected, but found ',' + genStatic : static[int], + staticInt : static int, + staticVal1 : static 10, + staticVal2 : static("str"), + staticVal3 : static "str", + # staticVal4 : static"str", # Error: expression expected, but found 'str' + staticDotVal : 10.static, + bareRef : ref, + refTuple1 : ref (int), + refTuple1A : ref (int,), + refTuple2 : ref (int,string), + genRef : ref[int], + refInt : ref int, + refCall : ref(a), + macroCall1 : foo bar, + macroCall2 : foo(bar), + macroCall3 : foo.bar(baz), + macroCall4 : foo[bar](baz), + macroCall5 : foo bar baz = 10 + ): type = + staticTen = static 10 + staticA = static(a) + staticAspace = static (a) + staticAtuple = static (a,) + staticTuple = static (a,b) + staticTypeTuple = static (int,string) + staticCall = static foo(1) + staticStrCall = static foo"x" + staticChainCall = static foo bar + + typeTen = type 10 + typeA = type(a) + # typeAspace = type (a) # Error: invalid indentation + # typeAtuple = type (a,) # Error: invalid indentation + # typeTuple = type (a,b) # Error: invalid indentation + # typeTypeTuple = type (int,string) # Error: invalid indentation + typeCall = type foo(1) + typeStrCall = type foo"x" + typeChainCall = type foo bar + + normalChainCall = foo bar baz + # normalTupleCall1 = foo(a,) # Error: invalid indentation + normalTupleCall2 = foo(a,b) + # normalTupleCall3 = foo (a,b) # Error: invalid indentation + + static: singleStaticStmt + static: + staticStmtList1 + staticStmtList2 + From fb27357b6217f53dc882e83fc128da578ad51764 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 20 Apr 2018 11:44:13 +0300 Subject: [PATCH 03/13] A minimal patch enabling the new typedesc and static types syntax --- compiler/ast.nim | 4 +- compiler/condsyms.nim | 1 + compiler/parser.nim | 63 +++++++------ compiler/sem.nim | 3 + compiler/semexprs.nim | 39 ++++++-- compiler/semtypes.nim | 38 +++++--- compiler/sigmatch.nim | 14 +-- lib/system.nim | 22 ++++- tests/lexer/tmissingnl.nim | 2 +- tests/parser/ttypemodifiers.nim | 152 +++++++++++++++++++++----------- 10 files changed, 228 insertions(+), 110 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 40c1b064d2..d266e20b09 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -571,8 +571,8 @@ type TMagic* = enum # symbols that require compiler magic: mNone, mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn, - mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin, - mEcho, mShallowCopy, mSlurp, mStaticExec, + mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mType, mTypeOf, + mRoof, mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic, mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst, mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mNewSeqOfCap, diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 56587a4faf..8b2d36722c 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -44,6 +44,7 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimcomputedgoto") defineSymbol("nimunion") defineSymbol("nimnewshared") + defineSymbol("nimNewTypedesc") defineSymbol("nimrequiresnimframe") defineSymbol("nimparsebiggestfloatmagic") defineSymbol("nimalias") diff --git a/compiler/parser.nim b/compiler/parser.nim index c0465b05ea..33ee8c9e6d 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -50,6 +50,9 @@ type SymbolMode = enum smNormal, smAllowNil, smAfterDot + TPrimaryMode = enum + pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix + proc parseAll*(p: var TParser): PNode proc closeParser*(p: var TParser) proc parseTopLevelStmt*(p: var TParser): PNode @@ -81,6 +84,9 @@ proc parsePragma(p: var TParser): PNode proc postExprBlocks(p: var TParser, x: PNode): PNode proc parseExprStmt(p: var TParser): PNode proc parseBlock(p: var TParser): PNode +proc primary(p: var TParser, mode: TPrimaryMode): PNode +proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode + # implementation proc getTok(p: var TParser) = @@ -332,6 +338,8 @@ proc colcom(p: var TParser, n: PNode) = eat(p, tkColon) skipComment(p, n) +const tkBuiltInMagics = {tkType, tkStatic, tkAddr} + proc parseSymbol(p: var TParser, mode = smNormal): PNode = #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' #| | IDENT | KEYW @@ -340,7 +348,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode = result = newIdentNodeP(p.tok.ident, p) getTok(p) of tokKeywordLow..tokKeywordHigh: - if p.tok.tokType == tkAddr or p.tok.tokType == tkType or mode == smAfterDot: + if p.tok.tokType in tkBuiltInMagics or mode == smAfterDot: # for backwards compatibility these 2 are always valid: result = newIdentNodeP(p.tok.ident, p) getTok(p) @@ -525,9 +533,6 @@ proc parseGStrLit(p: var TParser, a: PNode): PNode = else: result = a -type - TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix - proc complexOrSimpleStmt(p: var TParser): PNode proc simpleExpr(p: var TParser, mode = pmNormal): PNode @@ -625,7 +630,7 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']' case p.tok.tokType - of tkSymbol, tkType, tkAddr: + of tkSymbol, tkBuiltInMagics: result = newIdentNodeP(p.tok.ident, p) getTok(p) result = parseGStrLit(p, result) @@ -741,7 +746,12 @@ proc commandParam(p: var TParser, isFirstParam: var bool): PNode = addSon(result, parseExpr(p)) isFirstParam = false -proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = +const + tkTypeClasses = {tkRef, tkPtr, tkVar, tkStatic, tkType, + tkEnum, tkTuple, tkObject, tkProc} + +proc primarySuffix(p: var TParser, r: PNode, + baseIndent: int, mode: TPrimaryMode): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? #| | doBlocks #| | '.' optInd symbol generalizedLit? @@ -758,7 +768,14 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = case p.tok.tokType of tkParLe: # progress guaranteed - somePar() + if p.tok.strongSpaceA > 0: + # inside type sections, expressions such as `ref (int, bar)` + # are parsed as a nkCommand with a single tuple argument (nkPar) + if mode == pmTypeDef: + result = newNodeP(nkCommand, p) + result.addSon r + result.addSon primary(p, pmNormal) + break result = namedParams(p, result, nkCall, tkParRi) if result.len > 1 and result.sons[1].kind == nkExprColonExpr: result.kind = nkObjConstr @@ -774,9 +791,15 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = # progress guaranteed somePar() result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) - of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType, - tkOpr, tkDotDot: - if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}): + of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, + tkOpr, tkDotDot, tkTypeClasses - {tkRef, tkPtr}: + # XXX: In type sections we allow the free application of the + # command syntax, with the exception of expressions such as + # `foo ref` or `foo ptr`. Unfortunately, these two are also + # used as infix operators for the memory regions feature and + # the current parsing rules don't play well here. + if mode == pmTypeDef or + (p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot})): # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet # solution, but pragmas.nim can't handle that let a = result @@ -800,9 +823,6 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = else: break -proc primary(p: var TParser, mode: TPrimaryMode): PNode -proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode - proc parseOperators(p: var TParser, headNode: PNode, limit: int, mode: TPrimaryMode): PNode = result = headNode @@ -1107,9 +1127,9 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode = proc isExprStart(p: TParser): bool = case p.tok.tokType of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, - tkProc, tkFunc, tkIterator, tkBind, tkAddr, + tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics, tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, - tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut: + tkTuple, tkObject, tkWhen, tkCase, tkOut: result = true else: result = false @@ -1170,7 +1190,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = #| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum' #| primary = typeKeyw typeDescK #| / prefixOperator* identOrLiteral primarySuffix* - #| / 'static' primary #| / 'bind' primary if isOperator(p.tok): let isSigil = isSigilLike(p.tok) @@ -1183,7 +1202,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = #XXX prefix operators let baseInd = p.lex.currLineIndent addSon(result, primary(p, pmSkipSuffix)) - result = primarySuffix(p, result, baseInd) + result = primarySuffix(p, result, baseInd, mode) else: addSon(result, primary(p, pmNormal)) return @@ -1213,14 +1232,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = result = parseTypeClass(p) else: parMessage(p, "the 'concept' keyword is only valid in 'type' sections") - of tkStatic: - let info = parLineInfo(p) - getTokNoInd(p) - let next = primary(p, pmNormal) - if next.kind == nkBracket and next.sonsLen == 1: - result = newNode(nkStaticTy, info, @[next.sons[0]]) - else: - result = newNode(nkStaticExpr, info, @[next]) of tkBind: result = newNodeP(nkBind, p) getTok(p) @@ -1235,7 +1246,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = let baseInd = p.lex.currLineIndent result = identOrLiteral(p, mode) if mode != pmSkipSuffix: - result = primarySuffix(p, result, baseInd) + result = primarySuffix(p, result, baseInd, mode) proc parseTypeDesc(p: var TParser): PNode = #| typeDesc = simpleExpr diff --git a/compiler/sem.nim b/compiler/sem.nim index 3b16e0938b..3c3ffa1948 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -48,6 +48,9 @@ proc semQuoteAst(c: PContext, n: PNode): PNode proc finishMethod(c: PContext, s: PSym) proc evalAtCompileTime(c: PContext, n: PNode): PNode proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode +proc semStaticExpr(c: PContext, n: PNode): PNode +proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType +proc semTypeOf(c: PContext; n: PNode): PNode proc isArrayConstr(n: PNode): bool {.inline.} = result = n.kind == nkBracket and diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9d7c493a7b..4b45ccf874 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -191,7 +191,25 @@ proc semConv(c: PContext, n: PNode): PNode = return n result = newNodeI(nkConv, n.info) - var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc}) + + var targetType = semTypeNode(c, n.sons[0], nil) + if targetType.kind == tyTypeDesc: + internalAssert targetType.len > 0 + if targetType.base.kind == tyNone: + return semTypeOf(c, n[1]) + else: + targetType = targetType.base + elif targetType.kind == tyStatic: + var evaluated = semStaticExpr(c, n[1]) + if evaluated.kind == nkType or evaluated.typ.kind == tyTypeDesc: + result = n + result.typ = c.makeTypeDesc semStaticType(c, evaluated, nil) + return + elif targetType.base.kind == tyNone: + return evaluated + else: + targetType = targetType.base + maybeLiftType(targetType, c, n[0].info) if targetType.kind in {tySink, tyLent}: @@ -632,7 +650,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = # echo "SUCCESS evaluated at compile time: ", call.renderTree proc semStaticExpr(c: PContext, n: PNode): PNode = - let a = semExpr(c, n.sons[0]) + let a = semExpr(c, n) if a.findUnresolvedStatic != nil: return a result = evalStaticExpr(c.module, c.graph, a, c.p.owner) if result.isNil: @@ -1034,7 +1052,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = of skType: markUsed(c.config, n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) - if s.typ.kind == tyStatic and s.typ.n != nil: + if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil: return s.typ.n result = newSymNode(s, n.info) result.typ = makeTypeDesc(c, s.typ) @@ -1261,8 +1279,18 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = # make sure we don't evaluate generic macros/templates n.sons[0] = semExprWithType(c, n.sons[0], {efNoEvaluateGeneric}) - let arr = skipTypes(n.sons[0].typ, {tyGenericInst, + var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) + if arr.kind == tyStatic: + if arr.base.kind == tyNone: + result = n + result.typ = semStaticType(c, n[1], nil) + return + elif arr.n != nil: + return semSubscript(c, arr.n, flags) + else: + arr = arr.base + case arr.kind of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, tyCString: @@ -2426,8 +2454,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # handling of sym choices is context dependent # the node is left intact for now discard - of nkStaticExpr: - result = semStaticExpr(c, n) + of nkStaticExpr: result = semStaticExpr(c, n[0]) of nkAsgn: result = semAsgn(c, n) of nkBlockStmt, nkBlockExpr: result = semBlock(c, n) of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 3e62652a71..597522f6fe 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -37,6 +37,12 @@ const errNoGenericParamsAllowedForX = "no generic parameters allowed for $1" errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types" +const + mStaticTy = {mStatic} + mTypeTy = {mType, mTypeOf} + # XXX: This should be needed only temporarily until the C + # sources are rebuilt + proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType = if prev == nil: result = newTypeS(kind, c) @@ -363,6 +369,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = if result != nil: markUsed(c.config, n.info, result, c.graph.usageSym) styleCheckUse(n.info, result) + if result.kind == skParam and result.typ.kind == tyTypeDesc: # This is a typedesc param. is it already bound? # it's not bound when it's used multiple times in the @@ -388,8 +395,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = else: localError(c.config, n.info, errTypeExpected) return errorSym(c, n) - - if result.kind != skType: + if result.kind != skType and result.magic notin (mStaticTy + mTypeTy): # this implements the wanted ``var v: V, x: V`` feature ... var ov: TOverloadIter var amb = initOverloadIter(ov, c, n) @@ -856,9 +862,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result = addImplicitGenericImpl(c, newTypeS(tyGenericParam, c), nil) of tyStatic: - # proc(a: expr{string}, b: expr{nkLambda}) - # overload on compile time values and AST trees - if paramType.n != nil: return # this is a concrete type + if paramType.base.kind != tyNone and paramType.n != nil: + # this is a concrete type + return if tfUnresolved in paramType.flags: return # already lifted let base = paramType.base.maybeLift if base.isMetaType and procKind == skMacro: @@ -879,7 +885,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyDistinct: if paramType.sonsLen == 1: # disable the bindOnce behavior for the type class - result = liftingWalk(paramType.sons[0], true) + result = liftingWalk(paramType.base, true) of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef, tyProc: @@ -1349,6 +1355,12 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym = localError(c.config, n.info, errTypeExpected) result = errorSym(c, n) +proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType = + result = newOrPrevType(tyStatic, prev, c) + var base = semTypeNode(c, childNode, nil).skipTypes({tyTypeDesc, tyAlias}) + result.rawAddSon(base) + result.flags.incl tfHasStatic + proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = nil inc c.inTypeContext @@ -1456,7 +1468,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mSeq: result = semContainer(c, n, tySequence, "seq", prev) of mOpt: result = semContainer(c, n, tyOpt, "opt", prev) of mVarargs: result = semVarargs(c, n, prev) - of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) + of mTypeDesc, mTypeTy: result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) + of mStaticTy: result = semStaticType(c, n[1], prev) of mExpr: result = semTypeNode(c, n.sons[0], nil) if result != nil: @@ -1545,11 +1558,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev) of nkVarTy: result = semVarType(c, n, prev) of nkDistinctTy: result = semDistinct(c, n, prev) - of nkStaticTy: - result = newOrPrevType(tyStatic, prev, c) - var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc}) - result.rawAddSon(base) - result.flags.incl tfHasStatic + of nkStaticTy: result = semStaticType(c, n[0], prev) of nkIteratorTy: if n.sonsLen == 0: result = newTypeS(tyBuiltInTypeClass, c) @@ -1645,9 +1654,12 @@ proc processMagicType(c: PContext, m: PSym) = of mStmt: setMagicType(c.config, m, tyStmt, 0) if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt - of mTypeDesc: + of mTypeDesc, mType: setMagicType(c.config, m, tyTypeDesc, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) + of mStatic: + setMagicType(m, tyStatic, 0) + rawAddSon(m.typ, newTypeS(tyNone, c)) of mVoidType: setMagicType(c.config, m, tyVoid, 0) of mArray: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 9a3c752618..5a556732b8 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1666,13 +1666,17 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, let prev = PType(idTableGet(c.bindings, f)) if prev == nil: if aOrig.kind == tyStatic: - result = typeRel(c, f.lastSon, a) - if result != isNone and f.n != nil: - if not exprStructuralEquivalent(f.n, aOrig.n): - result = isNone + if f.base.kind != tyNone: + result = typeRel(c, f.base, a) + if result != isNone and f.n != nil: + if not exprStructuralEquivalent(f.n, aOrig.n): + result = isNone + else: + result = isGeneric if result != isNone: put(c, f, aOrig) elif aOrig.n != nil and aOrig.n.typ != nil: - result = typeRel(c, f.lastSon, aOrig.n.typ) + result = if f.base.kind != tyNone: typeRel(c, f.lastSon, aOrig.n.typ) + else: isGeneric if result != isNone: var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ]) boundType.n = aOrig.n diff --git a/lib/system.nim b/lib/system.nim index fb02fde23e..15f952feb0 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -179,10 +179,24 @@ proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = ## Cannot be overloaded. discard -proc `type`*(x: untyped): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} = - ## Builtin 'type' operator for accessing the type of an expression. - ## Cannot be overloaded. - discard +when defined(nimNewTypedesc): + type + `static`* {.magic: "Static".}[T] + ## meta type representing all values that can be evaluated at compile-time. + ## + ## The type coercion ``static(x)`` can be used to force the compile-time + ## evaluation of the given expression ``x``. + + `type`* {.magic: "Type".}[T] + ## meta type representing the type of all type values. + ## + ## The coercion ``type(x)`` can be used to obtain the type of the given + ## expression ``x``. +else: + proc `type`*(x: untyped): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} = + ## Builtin 'type' operator for accessing the type of an expression. + ## Cannot be overloaded. + discard proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.} ## Boolean not; returns true iff ``x == false``. diff --git a/tests/lexer/tmissingnl.nim b/tests/lexer/tmissingnl.nim index 33b7debf10..095d9570e5 100644 --- a/tests/lexer/tmissingnl.nim +++ b/tests/lexer/tmissingnl.nim @@ -4,7 +4,7 @@ discard """ errormsg: "invalid indentation" """ -import strutils var s: seq[int] = @[0, 1, 2, 3, 4, 5, 6] +import strutils let s: seq[int] = @[0, 1, 2, 3, 4, 5, 6] #s[1..3] = @[] diff --git a/tests/parser/ttypemodifiers.nim b/tests/parser/ttypemodifiers.nim index db7ab903f3..2c322b44ba 100644 --- a/tests/parser/ttypemodifiers.nim +++ b/tests/parser/ttypemodifiers.nim @@ -53,25 +53,33 @@ StmtList Par Ident "a" Ident "b" + TypeDef + Ident "BareStatic" + Empty + Ident "static" TypeDef Ident "GenericStatic" Empty - StaticTy + BracketExpr + Ident "static" Ident "int" TypeDef Ident "PrefixStatic" Empty - StaticExpr + Command + Ident "static" Ident "int" TypeDef Ident "StaticTupleCl" Empty - StaticExpr + Command + Ident "static" TupleClassTy TypeDef Ident "StaticTuple" Empty - StaticExpr + Command + Ident "static" Par Ident "int" Ident "string" @@ -91,6 +99,12 @@ StmtList BracketExpr Ident "type" TupleClassTy + TypeDef + Ident "TypeTupleCl" + Empty + Command + Ident "type" + TupleClassTy TypeDef Ident "TypeInstance" Empty @@ -109,6 +123,13 @@ StmtList Call Ident "type" Ident "a" + TypeDef + Ident "TypeOfVarAlt" + Empty + Command + Ident "type" + Par + Ident "a" TypeDef Ident "TypeOfTuple1" Empty @@ -122,6 +143,29 @@ StmtList Ident "type" Ident "a" Ident "b" + TypeDef + Ident "TypeOfTuple1A" + Empty + Command + Ident "type" + TupleConstr + Ident "a" + TypeDef + Ident "TypeOfTuple2A" + Empty + Command + Ident "type" + Par + Ident "a" + Ident "b" + TypeDef + Ident "TypeTuple" + Empty + Command + Ident "type" + Par + Ident "int" + Ident "string" TypeDef Ident "GenericTypedesc" Empty @@ -172,32 +216,52 @@ StmtList Ident "foo" Ident "type" Empty + IdentDefs + Ident "typeTupleCl" + Command + Ident "type" + TupleClassTy + Empty + IdentDefs + Ident "bareStatic" + Ident "static" + Empty IdentDefs Ident "genStatic" - StaticTy + BracketExpr + Ident "static" Ident "int" Empty IdentDefs Ident "staticInt" - StaticExpr + Command + Ident "static" Ident "int" Empty IdentDefs Ident "staticVal1" - StaticExpr + Command + Ident "static" IntLit 10 Empty IdentDefs Ident "staticVal2" - StaticExpr - Par - StrLit "str" + Call + Ident "static" + StrLit "str" Empty IdentDefs Ident "staticVal3" - StaticExpr + Command + Ident "static" StrLit "str" Empty + IdentDefs + Ident "staticVal4" + CallStrLit + Ident "static" + RStrLit "str" + Empty IdentDefs Ident "staticDotVal" DotExpr @@ -285,50 +349,32 @@ StmtList StmtList Asgn Ident "staticTen" - StaticExpr + Command + Ident "static" IntLit 10 Asgn Ident "staticA" - StaticExpr - Par - Ident "a" - Asgn - Ident "staticAspace" - StaticExpr - Par - Ident "a" - Asgn - Ident "staticAtuple" - StaticExpr - TupleConstr - Ident "a" - Asgn - Ident "staticTuple" - StaticExpr - Par - Ident "a" - Ident "b" - Asgn - Ident "staticTypeTuple" - StaticExpr - Par - Ident "int" - Ident "string" + Call + Ident "static" + Ident "a" Asgn Ident "staticCall" - StaticExpr + Command + Ident "static" Call Ident "foo" IntLit 1 Asgn Ident "staticStrCall" - StaticExpr + Command + Ident "static" CallStrLit Ident "foo" RStrLit "x" Asgn Ident "staticChainCall" - StaticExpr + Command + Ident "static" Command Ident "foo" Ident "bar" @@ -399,7 +445,7 @@ dumpTree: RefTupleCl = ref tuple RefTupleType = ref (int, string) RefTupleVars = ref (a, b) - # BareStatic = static # Error: invalid indentation + BareStatic = static # Used to be Error: invalid indentation GenericStatic = static[int] PrefixStatic = static int StaticTupleCl = static tuple @@ -407,16 +453,16 @@ dumpTree: BareType = type GenericType = type[float] TypeTupleGen = type[tuple] - # TypeTupleCl = type tuple # Error: invalid indentation + TypeTupleCl = type tuple # Used to be Error: invalid indentation TypeInstance = type Foo[ref] bareTypeDesc = typedesc TypeOfVar = type(a) - # TypeOfVarAlt= type (a) # Error: invalid indentation + TypeOfVarAlt = type (a) # Used to be Error: invalid indentation TypeOfTuple1 = type(a,) TypeOfTuple2 = type(a,b) - # TypeOfTuple1A = type (a,) # Error: invalid indentation - # TypeOfTuple2A = type (a,b) # Error: invalid indentation - # TypeTuple = type (int, string) # Error: invalid indentation + TypeOfTuple1A = type (a,) # Used to be Error: invalid indentation + TypeOfTuple2A = type (a,b) # Used to be Error: invalid indentation + TypeTuple = type (int, string) # Used to be Error: invalid indentation GenericTypedesc = typedesc[int] T = type @@ -427,14 +473,14 @@ dumpTree: typeIntAlt : type(int), typeOfVar : type(a), typeDotType : foo.type, - # typeTupleCl : type tuple, # Error: ')' expected - # bareStatic : static, # Error: expression expected, but found ',' + typeTupleCl : type tuple, # Used to be Error: ')' expected + bareStatic : static, # Used to be Error: expression expected, but found ',' genStatic : static[int], staticInt : static int, staticVal1 : static 10, staticVal2 : static("str"), staticVal3 : static "str", - # staticVal4 : static"str", # Error: expression expected, but found 'str' + staticVal4 : static"str", # Used to be Error: expression expected, but found 'str' staticDotVal : 10.static, bareRef : ref, refTuple1 : ref (int), @@ -451,10 +497,10 @@ dumpTree: ): type = staticTen = static 10 staticA = static(a) - staticAspace = static (a) - staticAtuple = static (a,) - staticTuple = static (a,b) - staticTypeTuple = static (int,string) + # staticAspace = static (a) # With newTypedesc: Error: invalid indentation + # staticAtuple = static (a,) # With newTypedesc: Error: invalid indentation + # staticTuple = static (a,b) # With newTypedesc: Error: invalid indentation + # staticTypeTuple = static (int,string) # With newTypedesc: Error: invalid indentation staticCall = static foo(1) staticStrCall = static foo"x" staticChainCall = static foo bar From 509d6e923284f1f02c5dbc64e43aee9df1a012d3 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 22 Apr 2018 17:26:10 +0300 Subject: [PATCH 04/13] Bugfix: aliases to generic types were not considered implicit generic parameters --- compiler/semtypes.nim | 3 +++ tests/statictypes/tstatictypes.nim | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/statictypes/tstatictypes.nim diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 597522f6fe..19bb53209a 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -887,6 +887,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, # disable the bindOnce behavior for the type class result = liftingWalk(paramType.base, true) + of tyAlias: + result = liftingWalk(paramType.base) + of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef, tyProc: # XXX: this is a bit strange, but proc(s: seq) diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim new file mode 100644 index 0000000000..a646b61f7f --- /dev/null +++ b/tests/statictypes/tstatictypes.nim @@ -0,0 +1,17 @@ +discard """ +nimout: ''' +staticAlialProc instantiated with 4 +staticAlialProc instantiated with 6 +''' +""" + +type + StaticTypeAlias = static[int] + +proc staticAliasProc(s: StaticTypeAlias) = + static: echo "staticAlialProc instantiated with ", s + 1 + +staticAliasProc 1+2 +staticAliasProc 3 +staticAliasProc 5 + From ab9969ed3be2d57fdeda170cc9960be7ba628149 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 22 Apr 2018 18:11:22 +0300 Subject: [PATCH 05/13] Bugfix: the size of an array may be a static tuple element --- compiler/semtypes.nim | 3 ++- tests/statictypes/tstatictypes.nim | 40 ++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 19bb53209a..6b27c80329 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -301,7 +301,8 @@ proc semArrayIndex(c: PContext, n: PNode): PType = localError(c.config, info, errOrdinalTypeExpected) result = makeRangeWithStaticExpr(c, e) if c.inGenericContext > 0: result.flags.incl tfUnresolved - elif e.kind in nkCallKinds and hasGenericArguments(e): + elif e.kind in (nkCallKinds + {nkBracketExpr}) and + hasGenericArguments(e): if not isOrdinalType(e.typ): localError(c.config, n[1].info, errOrdinalTypeExpected) # This is an int returning call, depending on an diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index a646b61f7f..5234866fa6 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -5,13 +5,39 @@ staticAlialProc instantiated with 6 ''' """ -type - StaticTypeAlias = static[int] +import macros -proc staticAliasProc(s: StaticTypeAlias) = - static: echo "staticAlialProc instantiated with ", s + 1 +proc plus(a, b: int): int = a + b -staticAliasProc 1+2 -staticAliasProc 3 -staticAliasProc 5 +when true: + type + StaticTypeAlias = static[int] + + proc staticAliasProc(s: StaticTypeAlias) = + static: echo "staticAlialProc instantiated with ", s + 1 + echo s + + staticAliasProc 1+2 + staticAliasProc 3 + staticAliasProc 5 + +when true: + type + ArrayWrapper1[S: static int] = object + data: array[S + 1, int] + + ArrayWrapper2[S: static[int]] = object + data: array[S.plus(2), int] + + ArrayWrapper3[S: static[(int, string)]] = object + data: array[S[0], int] + + var aw1: ArrayWrapper1[5] + var aw2: ArrayWrapper2[5] + var aw3: ArrayWrapper3[(10, "str")] + + static: + assert aw1.data.high == 5 + assert aw2.data.high == 6 + assert aw3.data.high == 9 From a49b06a52a3c24258e9eb04593a2f83ae057755f Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 23 Apr 2018 17:23:14 +0300 Subject: [PATCH 06/13] Implement the `is` operator for the new static and typedesc type classes This also makes the first baby steps towards a sound treatment of higher-order kinds (type type int). Adds test cases showcasing the new features. * Also fixes breakage after the rebase --- compiler/parser.nim | 3 +- compiler/semdata.nim | 3 +- compiler/semexprs.nim | 81 +++++++++++++++++++++------- compiler/semtypes.nim | 7 ++- tests/metatype/ttypedesc1.nim | 34 +++++++++--- tests/metatype/ttypedesc3.nim | 6 +-- tests/metatype/ttypeselectors.nim | 6 +-- tests/parser/ttypeclasses.nim | 39 +++++++++++--- tests/statictypes/tstatictypes.nim | 84 ++++++++++++++++++++++++++---- tests/types/tisopr.nim | 2 +- 10 files changed, 210 insertions(+), 55 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 33ee8c9e6d..5c99363c9b 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -798,8 +798,7 @@ proc primarySuffix(p: var TParser, r: PNode, # `foo ref` or `foo ptr`. Unfortunately, these two are also # used as infix operators for the memory regions feature and # the current parsing rules don't play well here. - if mode == pmTypeDef or - (p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot})): + if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}): # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet # solution, but pragmas.nim can't handle that let a = result diff --git a/compiler/semdata.nim b/compiler/semdata.nim index c858b68392..aa0cb6e8ec 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -288,7 +288,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = result.addSonSkipIntLit(typ) proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = - let typedesc = makeTypeDesc(c, typ) + let typedesc = newTypeS(tyTypeDesc, c) + typedesc.addSonSkipIntLit(assertNotNil(c.config, typ)) let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info, c.config.options).linkTo(typedesc) return newSymNode(sym, info) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 4b45ccf874..a072deb7da 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -194,7 +194,7 @@ proc semConv(c: PContext, n: PNode): PNode = var targetType = semTypeNode(c, n.sons[0], nil) if targetType.kind == tyTypeDesc: - internalAssert targetType.len > 0 + internalAssert c.config, targetType.len > 0 if targetType.base.kind == tyNone: return semTypeOf(c, n[1]) else: @@ -316,57 +316,98 @@ proc semSizeof(c: PContext, n: PNode): PNode = n.typ = getSysType(c.graph, n.info, tyInt) result = n +proc fixupStaticType(c: PContext, n: PNode) = + # This proc can be applied to evaluated expressions to assign + # them a static type. + # + # XXX: with implicit static, this should not be necessary, + # because the output type of operations such as `semConstExpr` + # should be a static type (as well as the type of any other + # expression that can be implicitly evaluated). For now, we + # apply this measure only in code that is enlightened to work + # with static types. + if n.typ.kind != tyStatic: + n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ]) + n.typ.n = n # XXX: cycles like the one here look dangerous. + # Consider using `n.copyTree` + proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = - internalAssert c.config, n.sonsLen == 3 and - n[1].typ != nil and n[1].typ.kind == tyTypeDesc and + internalAssert c.config, + n.sonsLen == 3 and + n[1].typ != nil and n[2].kind in {nkStrLit..nkTripleStrLit, nkType} - let t1 = n[1].typ.skipTypes({tyTypeDesc}) + var + res = false + t1 = n[1].typ + t2 = n[2].typ + + if t1.kind == tyTypeDesc and t2.kind != tyTypeDesc: + t1 = t1.base if n[2].kind in {nkStrLit..nkTripleStrLit}: case n[2].strVal.normalize of "closure": let t = skipTypes(t1, abstractRange) - result = newIntNode(nkIntLit, ord(t.kind == tyProc and - t.callConv == ccClosure and - tfIterator notin t.flags)) + res = t.kind == tyProc and + t.callConv == ccClosure and + tfIterator notin t.flags else: - result = newIntNode(nkIntLit, 0) + res = false else: - var rhsOrigType = n[2].typ - var t2 = rhsOrigType.skipTypes({tyTypeDesc}) maybeLiftType(t2, c, n.info) var m: TCandidate initCandidate(c, m, t2) if efExplain in flags: m.diagnostics = @[] m.diagnosticsEnabled = true - let match = typeRel(m, t2, t1) >= isSubtype # isNone - result = newIntNode(nkIntLit, ord(match)) + res = typeRel(m, t2, t1) >= isSubtype # isNone + result = newIntNode(nkIntLit, ord(res)) result.typ = n.typ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode = if sonsLen(n) != 3: localError(c.config, n.info, "'is' operator takes 2 arguments") + let boolType = getSysType(c.graph, n.info, tyBool) result = n - n.typ = getSysType(c.graph, n.info, tyBool) + n.typ = boolType + var liftLhs = true n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator}) if n[2].kind notin {nkStrLit..nkTripleStrLit}: let t2 = semTypeNode(c, n[2], nil) n.sons[2] = newNodeIT(nkType, n[2].info, t2) + if t2.kind == tyStatic: + let evaluated = tryConstExpr(c, n[1]) + if evaluated != nil: + c.fixupStaticType(evaluated) + n[1] = evaluated + else: + result = newIntNode(nkIntLit, 0) + result.typ = boolType + return + elif t2.kind == tyTypeDesc and + (t2.base.kind == tyNone or tfExplicit in t2.flags): + # When the right-hand side is an explicit type, we must + # not allow regular values to be matched against the type: + liftLhs = false - let lhsType = n[1].typ + var lhsType = n[1].typ if lhsType.kind != tyTypeDesc: - n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info) - elif lhsType.base.kind == tyNone: - # this is a typedesc variable, leave for evals - return + if liftLhs: + n[1] = makeTypeSymNode(c, lhsType, n[1].info) + lhsType = n[1].typ + else: + if lhsType.base.kind == tyNone: + # this is a typedesc variable, leave for evals + return + if lhsType.base.containsGenericType: + # BUGFIX: don't evaluate this too early: ``T is void`` + return - # BUGFIX: don't evaluate this too early: ``T is void`` - if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags) + result = isOpImpl(c, n, flags) proc semOpAux(c: PContext, n: PNode) = const flags = {efDetermineType} diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 6b27c80329..64783f8903 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1472,8 +1472,11 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mSeq: result = semContainer(c, n, tySequence, "seq", prev) of mOpt: result = semContainer(c, n, tyOpt, "opt", prev) of mVarargs: result = semVarargs(c, n, prev) - of mTypeDesc, mTypeTy: result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) - of mStaticTy: result = semStaticType(c, n[1], prev) + of mTypeDesc, mTypeTy: + result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) + result.flags.incl tfExplicit + of mStaticTy: + result = semStaticType(c, n[1], prev) of mExpr: result = semTypeNode(c, n.sons[0], nil) if result != nil: diff --git a/tests/metatype/ttypedesc1.nim b/tests/metatype/ttypedesc1.nim index e9eee581f8..837c8eccca 100644 --- a/tests/metatype/ttypedesc1.nim +++ b/tests/metatype/ttypedesc1.nim @@ -5,15 +5,16 @@ type x: T y: U -proc getTypeName(t: typedesc): string = t.name +proc getTypeName1(t: typedesc): string = t.name +proc getTypeName2(t: type): string = t.name -proc foo(T: typedesc[float], a: auto): string = +proc foo(T: type float, a: auto): string = result = "float " & $(a.len > 5) proc foo(T: typedesc[TFoo], a: int): string = result = "TFoo " & $(a) -proc foo(T: typedesc[int or bool]): string = +proc foo(T: type[int or bool]): string = var a: T a = 10 result = "int or bool " & ($a) @@ -23,8 +24,8 @@ template foo(T: typedesc[seq]): string = "seq" test "types can be used as proc params": # XXX: `check` needs to know that TFoo[int, float] is a type and # cannot be assigned for a local variable for later inspection - check ((string.getTypeName == "string")) - check ((getTypeName(int) == "int")) + check ((string.getTypeName1 == "string")) + check ((getTypeName2(int) == "int")) check ((foo(TFoo[int, float], 1000) == "TFoo 1000")) @@ -37,6 +38,25 @@ test "types can be used as proc params": check ((foo(seq[int]) == "seq")) check ((foo(seq[TFoo[bool, string]]) == "seq")) -when false: - proc foo(T: typedesc[seq], s: T) = nil +template accept(x) = + static: assert(compiles(x)) + +template reject(x) = + static: assert(not compiles(x)) + +var + si: seq[int] + ss: seq[string] + +proc foo(T: typedesc[seq], s: T) = + discard + +accept: + foo seq[int], si + +reject: + foo seq[string], si + +reject: + foo seq[int], ss diff --git a/tests/metatype/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim index 9f19bd6e38..3d1cf2ec95 100644 --- a/tests/metatype/ttypedesc3.nim +++ b/tests/metatype/ttypedesc3.nim @@ -4,9 +4,9 @@ type Base = object of RootObj Child = object of Base -proc pr(T: typedesc[Base]) = echo "proc " & T.name -method me(T: typedesc[Base]) = echo "method " & T.name -iterator it(T: typedesc[Base]): auto = yield "yield " & T.name +proc pr(T: type[Base]) = echo "proc " & T.name +method me(T: type[Base]) = echo "method " & T.name +iterator it(T: type[Base]): auto = yield "yield " & T.name Base.pr Child.pr diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim index 1209fe78f2..2a2455adb5 100644 --- a/tests/metatype/ttypeselectors.nim +++ b/tests/metatype/ttypeselectors.nim @@ -5,16 +5,16 @@ output: "8\n8\n4" import macros, typetraits -template selectType(x: int): typeDesc = +template selectType(x: int): type = when x < 10: int else: string -template simpleTypeTempl: typeDesc = +template simpleTypeTempl: type = string -macro typeFromMacro: typedesc = string +macro typeFromMacro: type = string # The tests below check that the result variable of the # selected type matches the literal types in the code: diff --git a/tests/parser/ttypeclasses.nim b/tests/parser/ttypeclasses.nim index 9f487c7a8c..46fd206864 100644 --- a/tests/parser/ttypeclasses.nim +++ b/tests/parser/ttypeclasses.nim @@ -1,17 +1,42 @@ -discard """ - action: run -""" - type R = ref V = var D = distinct P = ptr + T = type + S = static + OBJ = object + TPL = tuple + SEQ = seq +var i: int var x: ref int var y: distinct int var z: ptr int +const C = @[1, 2, 3] + +static: + assert x is ref + assert y is distinct + assert z is ptr + assert C is static + assert C[1] is static[int] + assert C[0] is static[SomeInteger] + assert C isnot static[string] + assert C is SEQ|OBJ + assert C isnot OBJ|TPL + assert int is int + assert int is T + assert int is SomeInteger + assert seq[int] is type + assert seq[int] is type[seq] + assert seq[int] isnot type[seq[float]] + assert i isnot type[int] + assert type(i) is type[int] + assert x isnot T + assert y isnot S + assert z isnot enum + assert x isnot object + assert y isnot tuple + assert z isnot seq -doAssert x is ref -doAssert y is distinct -doAssert z is ptr \ No newline at end of file diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index 5234866fa6..789bd75886 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -1,25 +1,91 @@ discard """ nimout: ''' -staticAlialProc instantiated with 4 -staticAlialProc instantiated with 6 +staticAlialProc instantiated with 358 +staticAlialProc instantiated with 368 +''' +output: ''' +16 +16 +b is 2 times a +17 ''' """ import macros +template ok(x) = assert(x) +template no(x) = assert(not x) + +template accept(x) = + static: assert(compiles(x)) + +template reject(x) = + static: assert(not compiles(x)) + proc plus(a, b: int): int = a + b +template isStatic(x: static): bool = true +template isStatic(x: auto): bool = false + +var v = 1 + when true: + # test that `isStatic` works as expected + const C = 2 + + static: + ok C.isStatic + ok isStatic(plus(1, 2)) + ok plus(C, 2).isStatic + + no isStatic(v) + no plus(1, v).isStatic + +when true: + # test that proc instantiation works as expected type StaticTypeAlias = static[int] - proc staticAliasProc(s: StaticTypeAlias) = - static: echo "staticAlialProc instantiated with ", s + 1 - echo s + proc staticAliasProc(a: StaticTypeAlias, + b: static[int], + c: static int) = + static: + assert a.isStatic and b.isStatic and c.isStatic + assert isStatic(a + plus(b, c)) + echo "staticAlialProc instantiated with ", a, b, c - staticAliasProc 1+2 - staticAliasProc 3 - staticAliasProc 5 + when b mod a == 0: + echo "b is ", b div a, " times a" + + echo a + b + c + + staticAliasProc 1+2, 5, 8 + staticAliasProc 3, 2+3, 9-1 + staticAliasProc 3, 3+3, 4+4 + +when true: + # test static coercions. normal cases that should work: + accept: + var s1 = static[int] plus(1, 2) + var s2 = static(plus(1,2)) + var s3 = static plus(1,2) + var s4 = static[SomeInteger](1 + 2) + + # the sub-script operator can be used only with types: + reject: + var just_static3 = static[plus(1,2)] + + # static coercion takes into account the type: + reject: + var x = static[string](plus(1, 2)) + reject: + var x = static[string] plus(1, 2) + reject: + var x = static[SomeFloat] plus(3, 4) + + # you cannot coerce a run-time variable + reject: + var x = static(v) when true: type @@ -35,7 +101,7 @@ when true: var aw1: ArrayWrapper1[5] var aw2: ArrayWrapper2[5] var aw3: ArrayWrapper3[(10, "str")] - + static: assert aw1.data.high == 5 assert aw2.data.high == 6 diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim index 2f9dbf245c..f05f443def 100644 --- a/tests/types/tisopr.nim +++ b/tests/types/tisopr.nim @@ -5,7 +5,7 @@ false false true true -no''' +yes''' """ proc IsVoid[T](): string = From ea36e0ebbe0033b304a1261de802e2ed9308abf4 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 23 Apr 2018 22:30:23 +0300 Subject: [PATCH 07/13] document the new `type[T]` and `static[T]` features --- changelog.md | 7 +++ doc/docgen.rst | 6 +- doc/manual.rst | 156 ++++++++++++++++++++++++++++--------------------- doc/tut2.rst | 6 +- 4 files changed, 104 insertions(+), 71 deletions(-) diff --git a/changelog.md b/changelog.md index 4067cb6939..2e75d8ad93 100644 --- a/changelog.md +++ b/changelog.md @@ -116,6 +116,13 @@ - In order to make ``for`` loops and iterators more flexible to use Nim now supports so called "for-loop macros". See the `manual `_ for more details. +- the `typedesc` special type has been renamed to just `type`. +- `static` and `type` are now also modifiers similar to `ref` and `ptr`. + They denote the special types `static[T]` and `type[T]`. +- Forcing compile-time evaluation with `static` now supports specifying + the desired target type (as a concrete type or as a type class) +- The `type` operator now supports checking that the supplied expression + matches an expected type constraint. ### Language changes diff --git a/doc/docgen.rst b/doc/docgen.rst index e6693e1537..d196b3a18c 100644 --- a/doc/docgen.rst +++ b/doc/docgen.rst @@ -303,9 +303,9 @@ symbols in the `system module `_. `#len,seq[T] `_ * ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>** `#pairs.i,seq[T] `_ -* ``template newException[](exceptn: typedesc; message: string): expr`` **=>** - `#newException.t,typedesc,string - `_ +* ``template newException[](exceptn: type; message: string): expr`` **=>** + `#newException.t,type,string + `_ Index (idx) file format diff --git a/doc/manual.rst b/doc/manual.rst index 8e548afdc1..88dae89b0a 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1732,7 +1732,7 @@ But it seems all this boilerplate code needs to be repeated for the ``Euro`` currency. This can be solved with templates_. .. code-block:: nim - template additive(typ: typedesc) = + template additive(typ: type) = proc `+` *(x, y: typ): typ {.borrow.} proc `-` *(x, y: typ): typ {.borrow.} @@ -1740,13 +1740,13 @@ currency. This can be solved with templates_. proc `+` *(x: typ): typ {.borrow.} proc `-` *(x: typ): typ {.borrow.} - template multiplicative(typ, base: typedesc) = + template multiplicative(typ, base: type) = proc `*` *(x: typ, y: base): typ {.borrow.} proc `*` *(x: base, y: typ): typ {.borrow.} proc `div` *(x: typ, y: base): typ {.borrow.} proc `mod` *(x: typ, y: base): typ {.borrow.} - template comparable(typ: typedesc) = + template comparable(typ: type) = proc `<` * (x, y: typ): bool {.borrow.} proc `<=` * (x, y: typ): bool {.borrow.} proc `==` * (x, y: typ): bool {.borrow.} @@ -2396,7 +2396,7 @@ argument's resolution: rem unresolvedExpression(undeclaredIdentifier) ``untyped`` and ``varargs[untyped]`` are the only metatype that are lazy in this sense, the other -metatypes ``typed`` and ``typedesc`` are not lazy. +metatypes ``typed`` and ``type`` are not lazy. Varargs matching @@ -4274,29 +4274,6 @@ therefore very useful for type specialization within generic code: deletedKeys: seq[bool] -Type operator -------------- - -The ``type`` (in many other languages called `typeof`:idx:) operator can -be used to get the type of an expression: - -.. code-block:: nim - var x = 0 - var y: type(x) # y has type int - -If ``type`` is used to determine the result type of a proc/iterator/converter -call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the -interpretation where ``c`` is an iterator is preferred over the -other interpretations: - -.. code-block:: nim - import strutils - - # strutils contains both a ``split`` proc and iterator, but since an - # an iterator is the preferred interpretation, `y` has the type ``string``: - var y: type("a b c".split) - - Type Classes ------------ @@ -4450,10 +4427,10 @@ the presence of callable symbols with specific signatures: OutputStream = concept var s s.write(string) -In order to check for symbols accepting ``typedesc`` params, you must prefix -the type with an explicit ``type`` modifier. The named instance of the type, -following the ``concept`` keyword is also considered an explicit ``typedesc`` -value that will be matched only as a type. +In order to check for symbols accepting ``type`` params, you must prefix +the type with the explicit ``type`` modifier. The named instance of the +type, following the ``concept`` keyword is also considered to have the +explicit modifier and will be matched only as a type. .. code-block:: nim type @@ -4513,7 +4490,7 @@ The concept types can be parametric just like the regular generic types: import typetraits type - AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M + AnyMatrix*[R, C: static int; T] = concept m, var mvar, type M M.ValueType is T M.Rows == R M.Cols == C @@ -4523,7 +4500,7 @@ The concept types can be parametric just like the regular generic types: type TransposedType = stripGenericParams(M)[C, R, T] - AnySquareMatrix*[N: static[int], T] = AnyMatrix[N, N, T] + AnySquareMatrix*[N: static int, T] = AnyMatrix[N, N, T] AnyTransform3D* = AnyMatrix[4, 4, float] @@ -4542,7 +4519,7 @@ The concept types can be parametric just like the regular generic types: ### matrix.nim type - Matrix*[M, N: static[int]; T] = object + Matrix*[M, N: static int; T] = object data: array[M*N, T] proc `[]`*(M: Matrix; m, n: int): M.T = @@ -4554,7 +4531,7 @@ The concept types can be parametric just like the regular generic types: # Adapt the Matrix type to the concept's requirements template Rows*(M: type Matrix): expr = M.M template Cols*(M: type Matrix): expr = M.N - template ValueType*(M: type Matrix): typedesc = M.T + template ValueType*(M: type Matrix): type = M.T ------------- ### usage.nim @@ -4582,7 +4559,7 @@ operator and also when types dependent on them are being matched: .. code-block:: nim type - MatrixReducer[M, N: static[int]; T] = concept x + MatrixReducer[M, N: static int; T] = concept x x.reduce(SquareMatrix[N, T]) is array[M, int] The Nim compiler includes a simple linear equation solver, allowing it to @@ -4771,12 +4748,12 @@ object inheritance syntax involving the ``of`` keyword: # the varargs param will here be converted to an array of StringRefValues # the proc will have only two instantiations for the two character types - proc log(format: static[string], varargs[StringRef]) + proc log(format: static string, varargs[StringRef]) # this proc will allow char and wchar values to be mixed in # the same call at the cost of additional instantiations # the varargs param will be converted to a tuple - proc log(format: static[string], varargs[distinct StringRef]) + proc log(format: static string, varargs[distinct StringRef]) .. @@ -4940,9 +4917,8 @@ templates: | ``notin`` and ``isnot`` have the obvious meanings. The "types" of templates can be the symbols ``untyped``, -``typed`` or ``typedesc`` (stands for *type -description*). These are "meta types", they can only be used in certain -contexts. Real types can be used too; this implies that ``typed`` expressions +``typed`` or ``type``. These are "meta types", they can only be used in certain +contexts. Regular types can be used too; this implies that ``typed`` expressions are expected. @@ -5109,7 +5085,7 @@ In templates identifiers can be constructed with the backticks notation: .. code-block:: nim :test: "nim c $1" - template typedef(name: untyped, typ: typedesc) = + template typedef(name: untyped, typ: type) = type `T name`* {.inject.} = typ `P name`* {.inject.} = ref `T name` @@ -5171,7 +5147,7 @@ template cannot be accessed in the instantiation context: .. code-block:: nim :test: "nim c $1" - template newException*(exceptn: typedesc, message: string): untyped = + template newException*(exceptn: type, message: string): untyped = var e: ref exceptn # e is implicitly gensym'ed here new(e) @@ -5493,7 +5469,7 @@ As their name suggests, static parameters must be known at compile-time: .. code-block:: nim - proc precompiledRegex(pattern: static[string]): RegEx = + proc precompiledRegex(pattern: static string): RegEx = var res {.global.} = re(pattern) return res @@ -5513,9 +5489,9 @@ Static params can also appear in the signatures of generic types: .. code-block:: nim type - Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T] + Matrix[M,N: static int; T: Number] = array[0..(M*N - 1), T] # Note how `Number` is just a type constraint here, while - # `static[int]` requires us to supply a compile-time int value + # `static int` requires us to supply a compile-time int value AffineTransform2D[T] = Matrix[3, 3, T] AffineTransform3D[T] = Matrix[4, 4, T] @@ -5523,53 +5499,75 @@ Static params can also appear in the signatures of generic types: var m1: AffineTransform3D[float] # OK var m2: AffineTransform2D[string] # Error, `string` is not a `Number` +Please note that ``static T`` is just a syntactic convenience for the +underlying generic type ``static[T]``. This means that you can omit the +type param to obtain the type class of all values, known at compile-time +and you can restrict the matched values by instantiating ``static`` with +another type class. -typedesc --------- +You can force the evaluation of a certain expression at compile-time by +coercing it to a corresponding ``static`` type: -`typedesc` is a special type allowing one to treat types as compile-time values -(i.e. if types are compile-time values and all values have a type, then -typedesc must be their type). +.. code-block:: nim + import math -When used as a regular proc param, typedesc acts as a type class. The proc -will be instantiated for each unique type parameter and one can refer to the -instantiation type using the param name: + echo static(fac(5)), " ", static[bool](16.isPowerOfTwo) + +The complier will report any failure to evaluate the expression or a +possible type mismatch error. + +type[T] +------- + +In many contexts, Nim allows you to treat the names of types as regular +values. These values exists only during the compilation phase, but since +all values must have a type, ``type`` is considered their special type. + +``type`` acts like a generic type. For instance, the type of the symbol +``int`` is ``type[int]``. Just like with regular generic types, when the +generic param is ommited, ``type`` denotes the type class of all types. +As a syntactic convenience, you can also use ``type`` as a modifier. +``type int`` is considered the same as ``type[int]``. + +Procs featuring ``type`` params will be considered implicitly generic. +They will be instantiated for each unique combination of supplied types +and within the body of the proc, the name of each param will refer to +the bound concrete type: .. code-block:: nim - proc new(T: typedesc): ref T = + proc new(T: type): ref T = echo "allocating ", T.name new(result) var n = Node.new var tree = new(BinaryTree[int]) -When multiple typedesc params are present, they will bind freely to different -types. To force a bind-once behavior -one can use an explicit ``typedesc[T]`` generic param: +When multiple type params are present, they will bind freely to different +types. To force a bind-once behavior one can use an explicit generic param: .. code-block:: nim - proc acceptOnlyTypePairs[T, U](A, B: typedesc[T]; C, D: typedesc[U]) + proc acceptOnlyTypePairs[T, U](A, B: type[T]; C, D: type[U]) -Once bound, typedesc params can appear in the rest of the proc signature: +Once bound, type params can appear in the rest of the proc signature: .. code-block:: nim :test: "nim c $1" - template declareVariableWithType(T: typedesc, value: T) = + template declareVariableWithType(T: type, value: T) = var x: T = value declareVariableWithType int, 42 Overload resolution can be further influenced by constraining the set of -types that will match the typedesc param: +types that will match the type param: .. code-block:: nim :test: "nim c $1" - template maxval(T: typedesc[int]): int = high(int) - template maxval(T: typedesc[float]): float = Inf + template maxval(T: type int): int = high(int) + template maxval(T: type float): float = Inf var i = int.maxval var f = float.maxval @@ -5578,7 +5576,35 @@ types that will match the typedesc param: The constraint can be a concrete type or a type class. +type operator +------------- +You can obtain the type of a given expression by constructing a ``type`` +value from it (in many other languages this is known as the `typeof`:idx: +operator): + +.. code-block:: nim + var x = 0 + var y: type(x) # y has type int + +You may add a constraint to the resulting type to trigger a compile-time error +if the expression doesn't have the expected type: + +.. code-block:: nim + var x = 0 + var y: type[object](x) # Error: type mismatch: got but expected 'object' + +If ``type`` is used to determine the result type of a proc/iterator/converter +call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the +interpretation where ``c`` is an iterator is preferred over the +other interpretations: + +.. code-block:: nim + import strutils + + # strutils contains both a ``split`` proc and iterator, but since an + # an iterator is the preferred interpretation, `y` has the type ``string``: + var y: type("a b c".split) Special Operators @@ -7458,7 +7484,7 @@ Custom pragmas are defined using templates annotated with pragma ``pragma``: .. code-block:: nim template dbTable(name: string, table_space: string = "") {.pragma.} template dbKey(name: string = "", primary_key: bool = false) {.pragma.} - template dbForeignKey(t: typedesc) {.pragma.} + template dbForeignKey(t: type) {.pragma.} template dbIgnore {.pragma.} diff --git a/doc/tut2.rst b/doc/tut2.rst index 91cb528341..39e3bd89ad 100644 --- a/doc/tut2.rst +++ b/doc/tut2.rst @@ -618,9 +618,9 @@ Turning the ``log`` proc into a template solves this problem: log("x has the value: " & $x) The parameters' types can be ordinary types or the meta types ``untyped``, -``typed``, or ``typedesc``. -``typedesc`` stands for *type description*, and ``untyped`` means symbol lookups and -type resolution is not performed before the expression is passed to the template. +``typed``, or ``type``. ``type`` suggests that only a type symbol may be given +as an argument, and ``untyped`` means symbol lookups and type resolution is not +performed before the expression is passed to the template. If the template has no explicit return type, ``void`` is used for consistency with procs and methods. From 5bcf8bcb598d2ca0162f50d2b7250a847026b2c9 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 12 Jun 2018 23:45:18 +0300 Subject: [PATCH 08/13] fixes #7222; fixes #5595; fixes #3747 * late instantiation for the generic procs' default param values * automatic mixin behaviour in concepts Other fixes: * don't render the automatically inserted default params in calls * better rendering of tyFromExpr --- compiler/ast.nim | 1 + compiler/renderer.nim | 6 +- compiler/sem.nim | 2 +- compiler/semcall.nim | 23 +++- compiler/semexprs.nim | 30 +++++ compiler/semgnrc.nim | 31 ++++-- compiler/seminst.nim | 37 +++--- compiler/semtypes.nim | 35 ++++-- compiler/semtypinst.nim | 24 ++-- compiler/sigmatch.nim | 23 +++- compiler/types.nim | 5 +- tests/concepts/libs/trie.nim | 26 +++++ tests/concepts/libs/trie_database.nim | 12 ++ tests/concepts/ttrieconcept.nim | 7 ++ tests/generics/tlateboundgenericparams.nim | 124 +++++++++++++++++++++ 15 files changed, 321 insertions(+), 65 deletions(-) create mode 100644 tests/concepts/libs/trie.nim create mode 100644 tests/concepts/libs/trie_database.nim create mode 100644 tests/concepts/ttrieconcept.nim create mode 100644 tests/generics/tlateboundgenericparams.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index d266e20b09..085a243b3d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -454,6 +454,7 @@ type nfPreventCg # this node should be ignored by the codegen nfBlockArg # this a stmtlist appearing in a call (e.g. a do block) nfFromTemplate # a top-level node returned from a template + nfDefaultParam # an automatically inserter default parameter TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index ba87838db3..3ce2e157de 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -387,8 +387,10 @@ proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int = assert(theEnd < 0) result = 0 for i in countup(start, sonsLen(n) + theEnd): - inc(result, lsub(g, n.sons[i])) - inc(result, 2) # for ``, `` + let param = n.sons[i] + if nfDefaultParam notin param.flags: + inc(result, lsub(g, param)) + inc(result, 2) # for ``, `` if result > 0: dec(result, 2) # last does not get a comma! diff --git a/compiler/sem.nim b/compiler/sem.nim index 3c3ffa1948..afc794a379 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -51,7 +51,7 @@ proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode proc semStaticExpr(c: PContext, n: PNode): PNode proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType proc semTypeOf(c: PContext; n: PNode): PNode - +proc hasUnresolvedArgs(c: PContext, n: PNode): bool proc isArrayConstr(n: PNode): bool {.inline.} = result = n.kind == nkBracket and n.typ.skipTypes(abstractInst).kind == tyArray diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 5d3df064f4..0de22cfb3d 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -391,7 +391,21 @@ proc inferWithMetatype(c: PContext, formal: PType, result = copyTree(arg) result.typ = formal -proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = +proc updateDefaultParams(call: PNode) = + # In generic procs, the default parameter may be unique for each + # instantiation (see tlateboundgenericparams). + # After a call is resolved, we need to re-assign any default value + # that was used during sigmatch. sigmatch is responsible for marking + # the default params with `nfDefaultParam` and `instantiateProcType` + # computes correctly the default values for each instantiation. + let calleeParams = call[0].sym.typ.n + for i in countdown(call.len - 1, 1): + if nfDefaultParam notin call[i].flags: + return + call[i] = calleeParams[i].sym.ast + +proc semResolvedCall(c: PContext, x: TCandidate, + n: PNode, flags: TExprFlags): PNode = assert x.state == csMatch var finalCallee = x.calleeSym markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym) @@ -424,8 +438,9 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = result = x.call instGenericConvertersSons(c, result, x) - result.sons[0] = newSymNode(finalCallee, result.sons[0].info) + result[0] = newSymNode(finalCallee, result[0].info) result.typ = finalCallee.typ.sons[0] + updateDefaultParams(result) proc canDeref(n: PNode): bool {.inline.} = result = n.len >= 2 and (let t = n[1].typ; @@ -447,7 +462,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, message(c.config, n.info, hintUserRaw, "Non-matching candidates for " & renderTree(n) & "\n" & candidates) - result = semResolvedCall(c, n, r) + result = semResolvedCall(c, r, n, flags) elif implicitDeref in c.features and canDeref(n): # try to deref the first argument and then try overloading resolution again: # @@ -458,7 +473,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, # n.sons[1] = n.sons[1].tryDeref var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) - if r.state == csMatch: result = semResolvedCall(c, n, r) + if r.state == csMatch: result = semResolvedCall(c, r, n, flags) else: # get rid of the deref again for a better error message: n.sons[1] = n.sons[1].sons[0] diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a072deb7da..82bd761367 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -542,6 +542,36 @@ proc fixAbstractType(c: PContext, n: PNode) = proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult = result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr) +proc isUnresolvedSym(s: PSym): bool = + return s.kind == skGenericParam or + tfInferrableStatic in s.typ.flags or + (s.kind == skParam and s.typ.isMetaType) or + (s.kind == skType and + s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {}) + +proc hasUnresolvedArgs(c: PContext, n: PNode): bool = + # Checks whether an expression depends on generic parameters that + # don't have bound values yet. E.g. this could happen in situations + # such as: + # type Slot[T] = array[T.size, byte] + # proc foo[T](x: default(T)) + # + # Both static parameter and type parameters can be unresolved. + case n.kind + of nkSym: + return isUnresolvedSym(n.sym) + of nkIdent, nkAccQuoted: + let ident = considerQuotedIdent(c.config, n) + let sym = searchInScopes(c, ident) + if sym != nil: + return isUnresolvedSym(sym) + else: + return false + else: + for i in 0.. 1: resetIdTable(cl.symMap) resetIdTable(cl.localCache) - result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) - propagateToOwner(result, result.sons[i]) + let needsStaticSkipping = result[i].kind == tyFromExpr + result[i] = replaceTypeVarsT(cl, result[i]) + if needsStaticSkipping: + result[i] = result[i].skipTypes({tyStatic}) internalAssert c.config, originalParams[i].kind == nkSym - when true: - let oldParam = originalParams[i].sym - let param = copySym(oldParam) - param.owner = prc - param.typ = result.sons[i] - if oldParam.ast != nil: - param.ast = fitNode(c, param.typ, oldParam.ast, oldParam.ast.info) + let oldParam = originalParams[i].sym + let param = copySym(oldParam) + param.owner = prc + param.typ = result[i] + if oldParam.ast != nil: + var def = oldParam.ast.copyTree + if def.kind == nkCall: + for i in 1 ..< def.len: + def[i] = replaceTypeVarsN(cl, def[i]) + def = semExprWithType(c, def) + param.ast = fitNode(c, param.typ, def, def.info) + param.typ = param.ast.typ - # don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])! - result.n.sons[i] = newSymNode(param) - addDecl(c, param) - else: - let param = replaceTypeVarsN(cl, originalParams[i]) - result.n.sons[i] = param - param.sym.owner = prc - addDecl(c, result.n.sons[i].sym) + result.n[i] = newSymNode(param) + result[i] = param.typ + propagateToOwner(result, result[i]) + addDecl(c, param) resetIdTable(cl.symMap) resetIdTable(cl.localCache) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 64783f8903..85c6e30565 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -244,7 +244,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0])) for i in 0..1: - if hasGenericArguments(range[i]): + if hasUnresolvedArgs(c, range[i]): result.n.addSon makeStaticExpr(c, range[i]) result.flags.incl tfUnresolved else: @@ -301,8 +301,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType = localError(c.config, info, errOrdinalTypeExpected) result = makeRangeWithStaticExpr(c, e) if c.inGenericContext > 0: result.flags.incl tfUnresolved - elif e.kind in (nkCallKinds + {nkBracketExpr}) and - hasGenericArguments(e): + elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e): if not isOrdinalType(e.typ): localError(c.config, n[1].info, errOrdinalTypeExpected) # This is an int returning call, depending on an @@ -1006,13 +1005,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, prev: PType, kind: TSymKind; isType=false): PType = # for historical reasons (code grows) this is invoked for parameter # lists too and then 'isType' is false. - var cl: IntSet checkMinSonsLen(n, 1, c.config) result = newProcType(c, n.info, prev) - if genericParams != nil and sonsLen(genericParams) == 0: - cl = initIntSet() var check = initIntSet() var counter = 0 + for i in countup(1, n.len - 1): var a = n.sons[i] if a.kind != nkIdentDefs: @@ -1022,6 +1019,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, # pass over this instantiation: if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue illFormedAst(a, c.config) + checkMinSonsLen(a, 3, c.config) var typ: PType = nil @@ -1030,26 +1028,38 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, length = sonsLen(a) hasType = a.sons[length-2].kind != nkEmpty hasDefault = a.sons[length-1].kind != nkEmpty + if hasType: typ = semParamType(c, a.sons[length-2], constraint) if hasDefault: - def = semExprWithType(c, a.sons[length-1]) - # check type compatibility between def.typ and typ: + def = a[^1] + block determineType: + if genericParams != nil and genericParams.len > 0: + def = semGenericStmt(c, def) + if hasUnresolvedArgs(c, def): + def.typ = makeTypeFromExpr(c, def.copyTree) + break determineType + + def = semExprWithType(c, def, {efDetermineType}) + if typ == nil: typ = def.typ - elif def != nil: - # and def.typ != nil and def.typ.kind != tyNone: + else: + # if def.typ != nil and def.typ.kind != tyNone: # example code that triggers it: # proc sort[T](cmp: proc(a, b: T): int = cmp) if not containsGenericType(typ): + # check type compatibility between def.typ and typ: def = fitNode(c, typ, def, def.info) + if not hasType and not hasDefault: if isType: localError(c.config, a.info, "':' expected") if kind in {skTemplate, skMacro}: typ = newTypeS(tyExpr, c) elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid: continue + for j in countup(0, length-3): var arg = newSymG(skParam, a.sons[j], c) if not hasType and not hasDefault and kind notin {skTemplate, skMacro}: @@ -1065,7 +1075,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, arg.position = counter arg.constraint = constraint inc(counter) - if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def) + if def != nil and def.kind != nkEmpty: + arg.ast = copyTree(def) if containsOrIncl(check, arg.name.id): localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'") addSon(result.n, newSymNode(arg)) @@ -1320,7 +1331,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = incl dummyParam.flags, sfUsed addDecl(c, dummyParam) - result.n.sons[3] = semConceptBody(c, n[3]) + result.n[3] = semConceptBody(c, n[3]) closeScope(c) proc semProcTypeWithScope(c: PContext, n: PNode, diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index a24972d040..f02c45e469 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -144,17 +144,6 @@ proc isTypeParam(n: PNode): bool = (n.sym.kind == skGenericParam or (n.sym.kind == skType and sfFromGeneric in n.sym.flags)) -proc hasGenericArguments*(n: PNode): bool = - if n.kind == nkSym: - return n.sym.kind == skGenericParam or - tfInferrableStatic in n.sym.typ.flags or - (n.sym.kind == skType and - n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {}) - else: - for i in 0.. Date: Wed, 13 Jun 2018 01:35:46 +0300 Subject: [PATCH 09/13] Support default type parameters progress on #7516 --- compiler/seminst.nim | 19 ++++++++++++++++--- compiler/semtypes.nim | 4 ++++ tests/metatype/ttypedesc2.nim | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 752a2b5994..0047fd68e1 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -247,26 +247,39 @@ proc instantiateProcType(c: PContext, pt: TIdTable, if i > 1: resetIdTable(cl.symMap) resetIdTable(cl.localCache) + + # take a note of the original type. If't a free type parameter + # we'll need to keep it unbount for the `fitNode` operation below... + var typeToFit = result[i] + let needsStaticSkipping = result[i].kind == tyFromExpr result[i] = replaceTypeVarsT(cl, result[i]) if needsStaticSkipping: result[i] = result[i].skipTypes({tyStatic}) + + # ...otherwise, we use the instantiated type in `fitNode` + if typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone: + typeToFit = result[i] + internalAssert c.config, originalParams[i].kind == nkSym let oldParam = originalParams[i].sym let param = copySym(oldParam) param.owner = prc param.typ = result[i] + + # The default value is instantiated and fitted against the final + # concrete param type. We avoid calling `replaceTypeVarsN` on the + # call head symbol, because this leads to infinite recursion. if oldParam.ast != nil: var def = oldParam.ast.copyTree if def.kind == nkCall: for i in 1 ..< def.len: def[i] = replaceTypeVarsN(cl, def[i]) def = semExprWithType(c, def) - param.ast = fitNode(c, param.typ, def, def.info) - param.typ = param.ast.typ + param.ast = fitNode(c, typeToFit, def, def.info) + param.typ = result[i] result.n[i] = newSymNode(param) - result[i] = param.typ propagateToOwner(result, result[i]) addDecl(c, param) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 85c6e30565..2f16151f2e 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1045,6 +1045,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if typ == nil: typ = def.typ + if typ.kind == tyTypeDesc: + # default typedesc values are mapped to the unbound typedesc type: + typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)]) + else: # if def.typ != nil and def.typ.kind != tyNone: # example code that triggers it: diff --git a/tests/metatype/ttypedesc2.nim b/tests/metatype/ttypedesc2.nim index 4b6cfe6bc7..94a7367e78 100644 --- a/tests/metatype/ttypedesc2.nim +++ b/tests/metatype/ttypedesc2.nim @@ -34,3 +34,17 @@ when true: type Point[T] = tuple[x, y: T] proc origin(T: typedesc): Point[T] = discard discard origin(int) + +# https://github.com/nim-lang/Nim/issues/7516 +import typetraits + +proc hasDefault1(T: type = int): auto = return T.name +doAssert hasDefault1(int) == "int" +doAssert hasDefault1(string) == "string" +doAssert hasDefault1() == "int" + +proc hasDefault2(T = string): auto = return T.name +doAssert hasDefault2(int) == "int" +doAssert hasDefault2(string) == "string" +doAssert hasDefault2() == "string" + From 59d19946c034d343ab4f5b8ad57683ff9f80de85 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Wed, 13 Jun 2018 01:46:44 +0300 Subject: [PATCH 10/13] fix some breakage after rebasing --- compiler/semexprs.nim | 2 +- compiler/semtypes.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 82bd761367..238ae01945 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -561,7 +561,7 @@ proc hasUnresolvedArgs(c: PContext, n: PNode): bool = of nkSym: return isUnresolvedSym(n.sym) of nkIdent, nkAccQuoted: - let ident = considerQuotedIdent(c.config, n) + let ident = considerQuotedIdent(c, n) let sym = searchInScopes(c, ident) if sym != nil: return isUnresolvedSym(sym) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 2f16151f2e..f60b57d33d 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1680,7 +1680,7 @@ proc processMagicType(c: PContext, m: PSym) = setMagicType(c.config, m, tyTypeDesc, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) of mStatic: - setMagicType(m, tyStatic, 0) + setMagicType(c.config, m, tyStatic, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) of mVoidType: setMagicType(c.config, m, tyVoid, 0) From e719f211c634d2be7b5ae118db7051a2382a8e3e Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 14 Jun 2018 12:47:07 +0300 Subject: [PATCH 11/13] fix #6928; fix #7208 --- compiler/astalgo.nim | 9 +++++++++ compiler/seminst.nim | 7 ++++--- compiler/semtypes.nim | 3 +++ tests/generics/tlateboundgenericparams.nim | 21 +++++++++++++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 0afe56bb70..a4a14405e5 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -31,6 +31,15 @@ when declared(echo): proc debug*(conf: ConfigRef; n: PType) {.deprecated.} proc debug*(conf: ConfigRef; n: PNode) {.deprecated.} + template debug*(x: PSym|PType|PNode) {.deprecated.} = + when compiles(c.config): + debug(c.config, x) + else: + error() + + template debug*(x: auto) {.deprecated.} = + echo x + template mdbg*: bool {.dirty.} = when compiles(c.module): c.module.fileIdx == c.config.projectMainIdx diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 0047fd68e1..0ad1fb8729 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -248,8 +248,8 @@ proc instantiateProcType(c: PContext, pt: TIdTable, resetIdTable(cl.symMap) resetIdTable(cl.localCache) - # take a note of the original type. If't a free type parameter - # we'll need to keep it unbount for the `fitNode` operation below... + # take a note of the original type. If't a free type or static parameter + # we'll need to keep it unbound for the `fitNode` operation below... var typeToFit = result[i] let needsStaticSkipping = result[i].kind == tyFromExpr @@ -258,7 +258,8 @@ proc instantiateProcType(c: PContext, pt: TIdTable, result[i] = result[i].skipTypes({tyStatic}) # ...otherwise, we use the instantiated type in `fitNode` - if typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone: + if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and + (typeToFit.kind != tyStatic): typeToFit = result[i] internalAssert c.config, originalParams[i].kind == nkSym diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index f60b57d33d..ff2820ec8f 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1056,6 +1056,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if not containsGenericType(typ): # check type compatibility between def.typ and typ: def = fitNode(c, typ, def, def.info) + elif typ.kind == tyStatic: + def = semConstExpr(c, def) + def = fitNode(c, typ, def, def.info) if not hasType and not hasDefault: if isType: localError(c.config, a.info, "':' expected") diff --git a/tests/generics/tlateboundgenericparams.nim b/tests/generics/tlateboundgenericparams.nim index 02f99dc551..9f0580fd27 100644 --- a/tests/generics/tlateboundgenericparams.nim +++ b/tests/generics/tlateboundgenericparams.nim @@ -1,3 +1,11 @@ +discard """ + output: "1\n10\n1\n10" + nimout: ''' +bar instantiated with 1 +bar instantiated with 10 +''' +""" + import typetraits type @@ -122,3 +130,16 @@ when true: var p = getOrigin[float]() var rotated = p.rotate(2.1) + test 7: + proc bar(x: static[int]) = + static: echo "bar instantiated with ", x + echo x + + proc foo(x: static[int] = 1) = + bar(x) + + foo() + foo(10) + foo(1) + foo(10) + From 31651ecd613b7fddea037cb0cc7d433bec6c902d Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 16 Jun 2018 03:51:31 +0300 Subject: [PATCH 12/13] allow referencing other parameters in default parameter values fix #7756 fix #1201 fix #7000 fix #3002 fix #1046 --- compiler/ast.nim | 8 ++- compiler/astalgo.nim | 42 ++++++------ compiler/lowerings.nim | 12 ++++ compiler/sem.nim | 18 +++-- compiler/semcall.nim | 4 +- compiler/seminst.nim | 26 +++++++- compiler/semtypes.nim | 2 + compiler/sigmatch.nim | 3 +- compiler/transf.nim | 46 +++++++++++++ tests/misc/tparamsindefault.nim | 114 ++++++++++++++++++++++++++++++++ 10 files changed, 244 insertions(+), 31 deletions(-) create mode 100644 tests/misc/tparamsindefault.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 085a243b3d..6302c21b98 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -293,6 +293,10 @@ const # the compiler will avoid printing such names # in user messages. + sfHoisted* = sfForward + # an expression was hoised to an anonymous variable. + # the flag is applied to the var/let symbol + sfNoForward* = sfRegister # forward declarations are not required (per module) sfReorder* = sfForward @@ -455,6 +459,8 @@ type nfBlockArg # this a stmtlist appearing in a call (e.g. a do block) nfFromTemplate # a top-level node returned from a template nfDefaultParam # an automatically inserter default parameter + nfDefaultRefsParam # a default param value references another parameter + # the flag is applied to proc default values and to calls TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that) @@ -972,7 +978,7 @@ const PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfDotSetter, nfDotField, nfIsRef, nfPreventCg, nfLL, - nfFromTemplate} + nfFromTemplate, nfDefaultRefsParam} namePos* = 0 patternPos* = 1 # empty except for term rewriting macros genericParamsPos* = 2 diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index a4a14405e5..290ac05ee9 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -34,32 +34,36 @@ when declared(echo): template debug*(x: PSym|PType|PNode) {.deprecated.} = when compiles(c.config): debug(c.config, x) + elif compiles(c.graph.config): + debug(c.graph.config, x) else: error() template debug*(x: auto) {.deprecated.} = echo x -template mdbg*: bool {.dirty.} = - when compiles(c.module): - c.module.fileIdx == c.config.projectMainIdx - elif compiles(c.c.module): - c.c.module.fileIdx == c.c.config.projectMainIdx - elif compiles(m.c.module): - m.c.module.fileIdx == m.c.config.projectMainIdx - elif compiles(cl.c.module): - cl.c.module.fileIdx == cl.c.config.projectMainIdx - elif compiles(p): - when compiles(p.lex): - p.lex.fileIdx == p.lex.config.projectMainIdx + template mdbg*: bool {.deprecated.} = + when compiles(c.graph): + c.module.fileIdx == c.graph.config.projectMainIdx + elif compiles(c.module): + c.module.fileIdx == c.config.projectMainIdx + elif compiles(c.c.module): + c.c.module.fileIdx == c.c.config.projectMainIdx + elif compiles(m.c.module): + m.c.module.fileIdx == m.c.config.projectMainIdx + elif compiles(cl.c.module): + cl.c.module.fileIdx == cl.c.config.projectMainIdx + elif compiles(p): + when compiles(p.lex): + p.lex.fileIdx == p.lex.config.projectMainIdx + else: + p.module.module.fileIdx == p.config.projectMainIdx + elif compiles(m.module.fileIdx): + m.module.fileIdx == m.config.projectMainIdx + elif compiles(L.fileIdx): + L.fileIdx == L.config.projectMainIdx else: - p.module.module.fileIdx == p.config.projectMainIdx - elif compiles(m.module.fileIdx): - m.module.fileIdx == m.config.projectMainIdx - elif compiles(L.fileIdx): - L.fileIdx == L.config.projectMainIdx - else: - error() + error() # --------------------------- ident tables ---------------------------------- proc idTableGet*(t: TIdTable, key: PIdObj): RootRef diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 24a4f186e7..1b17f620c8 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -336,6 +336,18 @@ proc typeNeedsNoDeepCopy(t: PType): bool = if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon result = not containsGarbageCollectedRef(t) +proc hoistExpr*(varSection, expr: PNode, name: PIdent, owner: PSym): PSym = + result = newSym(skLet, name, owner, varSection.info, owner.options) + result.flags.incl sfHoisted + result.typ = expr.typ + + var varDef = newNodeI(nkIdentDefs, varSection.info, 3) + varDef.sons[0] = newSymNode(result) + varDef.sons[1] = newNodeI(nkEmpty, varSection.info) + varDef.sons[2] = expr + + varSection.add varDef + proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType; v: PNode; useShallowCopy=false): PSym = result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info, diff --git a/compiler/sem.nim b/compiler/sem.nim index afc794a379..299286545e 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -73,6 +73,16 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode; # echo "passing to safeSemExpr: ", renderTree(n) discard safeSemExpr(c, n) +proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode = + result = arg + let x = result.skipConv + if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr: + changeType(c, x, formal, check=true) + else: + result = skipHiddenSubConv(result) + #result.typ = takeType(formal, arg.typ) + #echo arg.info, " picked ", result.typ.typeToString + proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = if arg.typ.isNil: localError(c.config, arg.info, "expression has no type: " & @@ -88,13 +98,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = result = copyTree(arg) result.typ = formal else: - let x = result.skipConv - if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr: - changeType(c, x, formal, check=true) - else: - result = skipHiddenSubConv(result) - #result.typ = takeType(formal, arg.typ) - #echo arg.info, " picked ", result.typ.typeToString + result = fitNodePostMatch(c, formal, result) proc inferWithMetatype(c: PContext, formal: PType, arg: PNode, coerceDistincts = false): PNode diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 0de22cfb3d..67fe992321 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -402,7 +402,9 @@ proc updateDefaultParams(call: PNode) = for i in countdown(call.len - 1, 1): if nfDefaultParam notin call[i].flags: return - call[i] = calleeParams[i].sym.ast + let def = calleeParams[i].sym.ast + if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam + call[i] = def proc semResolvedCall(c: PContext, x: TCandidate, n: PNode, flags: TExprFlags): PNode = diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 0ad1fb8729..fac04e3a0b 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -220,6 +220,14 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, result = replaceTypeVarsT(cl, header) closeScope(c) +proc referencesAnotherParam(n: PNode, p: PSym): bool = + if n.kind == nkSym: + return n.sym.kind == skParam and n.sym.owner == p + else: + for i in 0.. Date: Sat, 16 Jun 2018 05:05:53 +0300 Subject: [PATCH 13/13] requested pull-request changes --- compiler/semexprs.nim | 1 + compiler/semtypes.nim | 9 +++++++-- compiler/semtypinst.nim | 5 +++-- compiler/sigmatch.nim | 20 ++++++++++---------- doc/manual.rst | 8 ++++---- 5 files changed, 25 insertions(+), 18 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 238ae01945..a9258fb622 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -857,6 +857,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = else: result = m.call instGenericConvertersSons(c, result, m) + elif t != nil and t.kind == tyTypeDesc: if n.len == 1: return semObjConstr(c, n, flags) return semConv(c, n) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index f0f22e87c8..a0144500e9 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -863,7 +863,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyStatic: if paramType.base.kind != tyNone and paramType.n != nil: - # this is a concrete type + # this is a concrete static value return if tfUnresolved in paramType.flags: return # already lifted let base = paramType.base.maybeLift @@ -1048,7 +1048,12 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if typ == nil: typ = def.typ if typ.kind == tyTypeDesc: - # default typedesc values are mapped to the unbound typedesc type: + # consider a proc such as: + # proc takesType(T = int) + # a naive analysis may conclude that the proc type is type[int] + # which will prevent other types from matching - clearly a very + # surprising behavior. We must instead fix the expected type of + # the proc to be the unbound typedesc type: typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)]) else: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index f02c45e469..22ea09af1e 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -223,8 +223,9 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = # XXX: Bound symbols in default parameter expressions may reach here. # We cannot process them, becase `sym.n` may point to a proc body with - # cyclic references that will lead to an infinite recursion. Perhaps we - # should not use a black-list here, but a whitelist instead. + # cyclic references that will lead to an infinite recursion. + # Perhaps we should not use a black-list here, but a whitelist instead + # (e.g. skGenericParam and skType). # Note: `s.magic` may be `mType` in an example such as: # proc foo[T](a: T, b = myDefault(type(a))) if s.kind == skProc or s.magic != mNone: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 0bb7c4fdd3..784b5c11cc 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1089,13 +1089,8 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, else: isNone of tyAnything: - if f.kind in {tyAnything}: - return isGeneric - - if tfWildCard in a.flags and f.kind == tyTypeDesc: - return isGeneric - - return isNone + if f.kind == tyAnything: return isGeneric + else: return isNone of tyUserTypeClass, tyUserTypeClassInst: if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4: @@ -2366,6 +2361,14 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = m.firstMismatch = f break else: + if formal.ast.kind == nkEmpty: + # The default param value is set to empty in `instantiateProcType` + # when the type of the default expression doesn't match the type + # of the instantiated proc param: + localError(c.config, m.call.info, + ("The default parameter '$1' has incompatible type " & + "with the explicitly requested proc instantiation") % + formal.name.s) if nfDefaultRefsParam in formal.ast.flags: m.call.flags.incl nfDefaultRefsParam var def = copyTree(formal.ast) @@ -2375,9 +2378,6 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = put(m, formal.typ, def.typ) def.flags.incl nfDefaultParam setSon(m.call, formal.position + 1, def) - # XXX: Instead of setting a default value here, we may place a special - # marker value instead. Later, we will replace it in `semResolvedCall`. - # Unfortunately, this causes some breakage at the moment. inc(f) # forget all inferred types if the overload matching failed if m.state == csNoMatch: diff --git a/doc/manual.rst b/doc/manual.rst index 88dae89b0a..c267c706f8 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -5500,9 +5500,9 @@ Static params can also appear in the signatures of generic types: var m2: AffineTransform2D[string] # Error, `string` is not a `Number` Please note that ``static T`` is just a syntactic convenience for the -underlying generic type ``static[T]``. This means that you can omit the -type param to obtain the type class of all values, known at compile-time -and you can restrict the matched values by instantiating ``static`` with +underlying generic type ``static[T]``. The type param can be omitted +to obtain the type class of all values known at compile-time. A more +specific type class can be created by instantiating ``static`` with another type class. You can force the evaluation of a certain expression at compile-time by @@ -5529,7 +5529,7 @@ generic param is ommited, ``type`` denotes the type class of all types. As a syntactic convenience, you can also use ``type`` as a modifier. ``type int`` is considered the same as ``type[int]``. -Procs featuring ``type`` params will be considered implicitly generic. +Procs featuring ``type`` params are considered implicitly generic. They will be instantiated for each unique combination of supplied types and within the body of the proc, the name of each param will refer to the bound concrete type: