From 027f30610e44633b661befcca1b5dd39e9eaa283 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 19 Dec 2013 01:06:38 +0200 Subject: [PATCH 01/15] static params: expr[T] is now static[T] This introduces tyStatic and successfully bootstraps and handles few simple test cases. Static params within macros are no longer treated as PNimrodNodes - they are now equivalent to constants of the designated type. --- compiler/ast.nim | 8 +++-- compiler/ccgutils.nim | 2 +- compiler/cgen.nim | 4 +-- compiler/evalffi.nim | 2 +- compiler/evals.nim | 7 +++-- compiler/jsgen.nim | 2 +- compiler/parser.nim | 26 ++++++++++------ compiler/ropes.nim | 17 ++++++----- compiler/semexprs.nim | 9 +++--- compiler/semfold.nim | 2 +- compiler/seminst.nim | 8 ++--- compiler/semmagic.nim | 2 +- compiler/semtypes.nim | 38 ++++++++++++----------- compiler/semtypinst.nim | 4 +-- compiler/sigmatch.nim | 60 ++++++++++++++++++++----------------- compiler/types.nim | 13 ++++---- compiler/vmgen.nim | 6 ++-- lib/core/macros.nim | 20 +++++++------ lib/system.nim | 2 +- tests/run/tmemoization.nim | 2 +- tests/run/tstaticparams.nim | 10 +++---- todo.txt | 1 - 22 files changed, 135 insertions(+), 110 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 1e5276d68d..462bad24f0 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -192,6 +192,7 @@ type nkObjectTy, # object body nkTupleTy, # tuple body nkTypeClassTy, # user-defined type class + nkStaticTy, # ``static[T]`` nkRecList, # list of object parts nkRecCase, # case section of object nkRecWhen, # when section of object @@ -336,12 +337,13 @@ type tyIter, # unused tyProxy # used as errornous type (for idetools) tyTypeClass + tyParametricTypeClass # structured similarly to tyGenericInst + # lastSon is the body of the type class tyAnd tyOr tyNot tyAnything - tyParametricTypeClass # structured similarly to tyGenericInst - # lastSon is the body of the type class + tyStatic const tyPureObject* = tyTuple @@ -1232,7 +1234,7 @@ proc propagateToOwner*(owner, elem: PType) = if tfShared in elem.flags: owner.flags.incl tfHasShared - if elem.kind in {tyExpr, tyTypeDesc}: + if elem.kind in {tyExpr, tyStatic, tyTypeDesc}: owner.flags.incl tfHasMeta elif elem.kind in {tyString, tyRef, tySequence} or elem.kind == tyProc and elem.callConv == ccClosure: diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 310f7204ae..01928b22c9 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -88,7 +88,7 @@ proc GetUniqueType*(key: PType): PType = result = key of tyTypeDesc, tyTypeClasses: InternalError("value expected, but got a type") - of tyGenericParam: + of tyGenericParam, tyStatic: InternalError("GetUniqueType") of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter: result = GetUniqueType(lastSon(key)) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index b0c90de766..c2bba76b96 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -194,7 +194,7 @@ when compileTimeRopeFmt: if i - 1 >= start: yield (kind: ffLit, value: substr(s, start, i-1), intValue: 0) - macro rfmt(m: BModule, fmt: expr[string], args: varargs[PRope]): expr = + macro rfmt(m: BModule, fmt: static[string], args: varargs[PRope]): expr = ## Experimental optimized rope-formatting operator ## The run-time code it produces will be very fast, but will it speed up ## the compilation of nimrod itself or will the macro execution time @@ -209,7 +209,7 @@ when compileTimeRopeFmt: of ffParam: result.add(args[frag.intValue]) else: - template rfmt(m: BModule, fmt: expr[string], args: varargs[PRope]): expr = + template rfmt(m: BModule, fmt: string, args: varargs[PRope]): expr = ropecg(m, fmt, args) proc appcg(m: BModule, c: var PRope, frmt: TFormatStr, diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 21a131996a..848f706f2b 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -78,7 +78,7 @@ proc mapType(t: ast.PType): ptr libffi.TType = of tyFloat, tyFloat64: result = addr libffi.type_double of tyFloat32: result = addr libffi.type_float of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr, - tyStmt, tyTypeDesc, tyProc, tyArray, tyArrayConstr, tyNil: + tyStmt, tyTypeDesc, tyProc, tyArray, tyArrayConstr, tyStatic, tyNil: result = addr libffi.type_pointer of tyDistinct: result = mapType(t.sons[0]) diff --git a/compiler/evals.nim b/compiler/evals.nim index b4ea973e89..4a2586d5f8 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -91,6 +91,7 @@ proc evalMacroCall*(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode = + if defined(debug): writeStackTrace() result = newNodeI(nkExceptBranch, info) # creating a nkExceptBranch without sons # means that it could not be evaluated @@ -263,8 +264,8 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = result = newNodeIT(nkUIntLit, info, t) of tyFloat..tyFloat128: result = newNodeIt(nkFloatLit, info, t) - of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr, - tyStmt, tyTypeDesc, tyProc: + of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr, + tyStmt, tyTypeDesc, tyStatic, tyProc: result = newNodeIT(nkNilLit, info, t) of tyObject: result = newNodeIT(nkPar, info, t) @@ -358,7 +359,7 @@ proc evalVar(c: PEvalContext, n: PNode): PNode = proc aliasNeeded(n: PNode, flags: TEvalFlags): bool = result = efLValue in flags or n.typ == nil or - n.typ.kind in {tyExpr, tyStmt, tyTypeDesc} + n.typ.kind in {tyExpr, tyStatic, tyStmt, tyTypeDesc} proc evalVariable(c: PStackFrame, sym: PSym, flags: TEvalFlags): PNode = # We need to return a node to the actual value, diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index a3c88824d0..9912115ee1 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -130,7 +130,7 @@ proc mapType(typ: PType): TJSTypeKind = result = etyObject of tyNil: result = etyNull of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvokation, tyNone, - tyForward, tyEmpty, tyExpr, tyStmt, tyTypeDesc, tyTypeClasses: + tyForward, tyEmpty, tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses: result = etyNone of tyProc: result = etyProc of tyCString: result = etyString diff --git a/compiler/parser.nim b/compiler/parser.nim index fd51b04ecd..c8c14780d5 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -965,14 +965,18 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = of tkTuple: result = parseTuple(p, mode == pmTypeDef) of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}) of tkIterator: - if mode in {pmTypeDesc, pmTypeDef}: - result = parseProcExpr(p, false) - result.kind = nkIteratorTy + when true: + if mode in {pmTypeDesc, pmTypeDef}: + result = parseProcExpr(p, false) + result.kind = nkIteratorTy + else: + # no anon iterators for now: + parMessage(p, errExprExpected, p.tok) + getTok(p) # we must consume a token here to prevend endless loops! + result = ast.emptyNode else: - # no anon iterators for now: - parMessage(p, errExprExpected, p.tok) - getTok(p) # we must consume a token here to prevend endless loops! - result = ast.emptyNode + result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}) + result.kind = nkIteratorTy of tkEnum: if mode == pmTypeDef: result = parseEnum(p) @@ -995,9 +999,13 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = getTokNoInd(p) addSon(result, primary(p, pmNormal)) of tkStatic: - result = newNodeP(nkStaticExpr, p) + let info = parLineInfo(p) getTokNoInd(p) - addSon(result, primary(p, pmNormal)) + 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) diff --git a/compiler/ropes.nim b/compiler/ropes.nim index 707c291232..9a3647c0aa 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -282,13 +282,16 @@ proc ropef(frmt: TFormatStr, args: varargs[PRope]): PRope = app(result, substr(frmt, start, i - 1)) assert(RopeInvariant(result)) -{.push stack_trace: off, line_trace: off.} -proc `~`*(r: expr[string]): PRope = - # this is the new optimized "to rope" operator - # the mnemonic is that `~` looks a bit like a rope :) - var r {.global.} = r.ropef - return r -{.pop.} +when true: + template `~`*(r: string): PRope = r.ropef +else: + {.push stack_trace: off, line_trace: off.} + proc `~`*(r: static[string]): PRope = + # this is the new optimized "to rope" operator + # the mnemonic is that `~` looks a bit like a rope :) + var r {.global.} = r.ropef + return r + {.pop.} proc appf(c: var PRope, frmt: TFormatStr, args: varargs[PRope]) = app(c, ropef(frmt, args)) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index ccbb1e3673..a7fd1eaa08 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -102,7 +102,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = # if a proc accesses a global variable, it is not side effect free: if sfGlobal in s.flags: incl(c.p.owner.flags, sfSideEffect) - elif s.kind == skParam and s.typ.kind == tyExpr and s.typ.n != nil: + elif s.kind == skParam and s.typ.kind == tyStatic and s.typ.n != nil: # XXX see the hack in sigmatch.nim ... return s.typ.n result = newSymNode(s, n.info) @@ -111,7 +111,7 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = # var len = 0 # but won't be called # genericThatUsesLen(x) # marked as taking a closure? of skGenericParam: - if s.typ.kind == tyExpr: + if s.typ.kind == tyStatic: result = newSymNode(s, n.info) result.typ = s.typ elif s.ast != nil: @@ -668,6 +668,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, else: result = semOverloadedCall(c, n, nOrig, {skProc, skMethod, skConverter, skMacro, skTemplate}) + if result != nil: if result.sons[0].kind != nkSym: InternalError("semOverloadedCallAnalyseEffects") @@ -937,7 +938,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = let tParam = tbody.sons[s] if tParam.sym.name == i: let rawTyp = ty.sons[s + 1] - if rawTyp.kind == tyExpr: + if rawTyp.kind == tyStatic: return rawTyp.n else: let foundTyp = makeTypeDesc(c, rawTyp) @@ -1882,7 +1883,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkBind: Message(n.info, warnDeprecated, "bind") result = semExpr(c, n.sons[0], flags) - of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy: + of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy, nkStaticTy: var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc}) result.typ = makeTypeDesc(c, typ) #result = symNodeFromType(c, typ, n.info) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index ca06ea1b66..ddbe3053c7 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -587,7 +587,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of skType: result = newSymNodeTypeDesc(s, n.info) of skGenericParam: - if s.typ.kind == tyExpr: + if s.typ.kind == tyStatic: result = s.typ.n result.typ = s.typ.sons[0] else: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index d7d64fd547..a76c673dad 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -20,7 +20,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, if a.kind != nkSym: InternalError(a.info, "instantiateGenericParamList; no symbol") var q = a.sym - if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyExpr}+tyTypeClasses: + if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses: continue var s = newSym(skType, q.name, getCurrOwner(), q.info) s.flags = s.flags + {sfUsed, sfFromGeneric} @@ -145,11 +145,11 @@ proc lateInstantiateGeneric(c: PContext, invocation: PType, info: TLineInfo): PT pushInfoContext(info) for i in 0 .. 0 if not isOrdinalType(e.typ.lastSon): @@ -206,7 +206,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = indx = e.typ.skipTypes({tyTypeDesc}) addSonSkipIntLit(result, indx) if indx.kind == tyGenericInst: indx = lastSon(indx) - if indx.kind notin {tyGenericParam, tyExpr}: + if indx.kind notin {tyGenericParam, tyStatic}: if not isOrdinalType(indx): LocalError(n.sons[1].info, errOrdinalTypeExpected) elif enumHasHoles(indx): @@ -577,9 +577,9 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = pragma(c, s, n.sons[0], typePragmas) if base == nil and tfInheritable notin result.flags: incl(result.flags, tfFinal) - + proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = - if kind == skMacro and param.typ.kind != tyTypeDesc: + if kind == skMacro and param.typ.kind notin {tyTypeDesc, tyStatic}: # within a macro, every param has the type PNimrodNode! # and param.typ.kind in {tyTypeDesc, tyExpr, tyStmt}: let nn = getSysSym"PNimrodNode" @@ -629,15 +629,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, addImplicitGenericImpl(e, paramTypId) case paramType.kind: - of tyExpr: - if paramType.sonsLen == 0: - # proc(a, b: expr) - # no constraints, treat like generic param - result = addImplicitGeneric(newTypeS(tyGenericParam, c)) - else: - # proc(a: expr{string}, b: expr{nkLambda}) - # overload on compile time values and AST trees - result = addImplicitGeneric(c.newTypeWithSons(tyExpr, paramType.sons)) + of tyAnything: + result = addImplicitGeneric(newTypeS(tyGenericParam, c)) + of tyStatic: + # proc(a: expr{string}, b: expr{nkLambda}) + # overload on compile time values and AST trees + result = addImplicitGeneric(c.newTypeWithSons(tyStatic, paramType.sons)) of tyTypeDesc: if tfUnresolved notin paramType.flags: # naked typedescs are not bindOnce types @@ -743,8 +740,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if not containsGenericType(typ): def = fitNode(c, typ, def) if not (hasType or hasDefault): - typ = newTypeS(tyExpr, c) - + let tdef = if kind in {skTemplate, skMacro}: tyExpr else: tyAnything + typ = newTypeS(tdef, c) + if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue for j in countup(0, length-3): var arg = newSymG(skParam, a.sons[j], c) @@ -1000,6 +998,10 @@ 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) + result.rawAddSon(base) of nkProcTy, nkIteratorTy: if n.sonsLen == 0: result = newConstraint(c, tyProc) @@ -1105,7 +1107,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = if constraint.kind != nkEmpty: typ = semTypeNode(c, constraint, nil) - if typ.kind != tyExpr or typ.len == 0: + if typ.kind != tyStatic or typ.len == 0: if typ.kind == tyTypeDesc: if typ.len == 0: typ = newTypeS(tyTypeDesc, c) @@ -1116,7 +1118,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = def = semConstExpr(c, def) if typ == nil: if def.typ.kind != tyTypeDesc: - typ = newTypeWithSons(c, tyExpr, @[def.typ]) + typ = newTypeWithSons(c, tyStatic, @[def.typ]) else: if not containsGenericType(def.typ): def = fitNode(c, typ, def) @@ -1132,7 +1134,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = # of the parameter will be stored in the # attached symbol. var s = case finalType.kind - of tyExpr: + of tyStatic: newSymG(skGenericParam, a.sons[j], c).linkTo(finalType) else: newSymG(skType, a.sons[j], c).linkTo(finalType) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 61c31a4fe4..d05d063aab 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -201,7 +201,7 @@ proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = result = lookupTypeVar(cl, t) if result.kind == tyGenericInvokation: result = handleGenericInvokation(cl, result) - of tyExpr: + of tyStatic: if t.sym != nil and t.sym.kind == skGenericParam: result = lookupTypeVar(cl, t) of tyGenericInvokation: @@ -215,7 +215,7 @@ proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = else: if t.kind == tyArray: let idxt = t.sons[0] - if idxt.kind == tyExpr and + if idxt.kind == tyStatic and idxt.sym != nil and idxt.sym.kind == skGenericParam: let value = lookupTypeVar(cl, idxt).n t.sons[0] = makeRangeType(cl.c, 0, value.intVal - 1, value.info) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index cacf4782e3..87f1decf48 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -124,7 +124,7 @@ proc sumGeneric(t: PType): int = result = ord(t.kind == tyGenericInvokation) for i in 0 .. 0: argType.skipTypes({tyTypeDesc}) - else: argType - fMaybeExpr = f.skipTypes({tyDistinct}) + a0 = if c.InTypeClass > 0: argType.skipTypes({tyTypeDesc}) + else: argType + a = if a0 != nil: a0.skipTypes({tyStatic}) else: a0 + fMaybeStatic = f.skipTypes({tyDistinct}) - case fMaybeExpr.kind - of tyExpr: - if fMaybeExpr.sonsLen == 0: - r = isGeneric + case fMaybeStatic.kind + of tyStatic: + if a.kind == tyStatic: + InternalAssert a.len > 0 + r = typeRel(m, f.lastSon, a.lastSon) else: - if a.kind == tyExpr: - InternalAssert a.len > 0 - r = typeRel(m, f.lastSon, a.lastSon) + let match = matchTypeClass(m.bindings, fMaybeStatic, a) + if not match: r = isNone else: - let match = matchTypeClass(m.bindings, fMaybeExpr, a) - if not match: r = isNone - else: - # XXX: Ideally, this should happen much earlier somewhere near - # semOpAux, but to do that, we need to be able to query the - # overload set to determine whether compile-time value is expected - # for the param before entering the full-blown sigmatch algorithm. - # This is related to the immediate pragma since querying the - # overload set could help there too. - var evaluated = c.semConstExpr(c, arg) - if evaluated != nil: - r = isGeneric - arg.typ = newTypeS(tyExpr, c) - arg.typ.sons = @[evaluated.typ] - arg.typ.n = evaluated + # XXX: Ideally, this should happen much earlier somewhere near + # semOpAux, but to do that, we need to be able to query the + # overload set to determine whether compile-time value is expected + # for the param before entering the full-blown sigmatch algorithm. + # This is related to the immediate pragma since querying the + # overload set could help there too. + var evaluated = c.semConstExpr(c, arg) + if evaluated != nil: + r = isGeneric + arg.typ = newTypeS(tyStatic, c) + arg.typ.sons = @[evaluated.typ] + arg.typ.n = evaluated if r == isGeneric: put(m.bindings, f, arg.typ) + of tyTypeClass, tyParametricTypeClass: - if fMaybeExpr.n != nil: - let match = matchUserTypeClass(c, m, arg, fMaybeExpr, a) + if fMaybeStatic.n != nil: + let match = matchUserTypeClass(c, m, arg, fMaybeStatic, a) if match != nil: r = isGeneric arg = match @@ -935,6 +934,9 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, argType: PType, r = isNone else: r = typeRel(m, f, a) + of tyExpr: + r = isGeneric + put(m.bindings, f, arg.typ) else: r = typeRel(m, f, a) @@ -961,6 +963,8 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, argType: PType, result = argOrig[bodyPos] elif f.kind == tyTypeDesc: result = arg + elif f.kind == tyStatic: + result = arg.typ.n else: result = argOrig else: diff --git a/compiler/types.nim b/compiler/types.nim index 7e07a0667e..5fe128bbb9 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -409,7 +409,7 @@ const "uint", "uint8", "uint16", "uint32", "uint64", "bignum", "const ", "!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass", - "ParametricTypeClass", "and", "or", "not", "any"] + "ParametricTypeClass", "and", "or", "not", "any", "static"] proc consToStr(t: PType): string = if t.len > 0: result = t.typeToString @@ -445,6 +445,9 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = of tyTypeDesc: if t.len == 0: result = "typedesc" else: result = "typedesc[" & constraintsToStr(t) & "]" + of tyStatic: + InternalAssert t.len > 0 + result = "static[" & constraintsToStr(t) & "]" of tyTypeClass: if t.n != nil: return t.sym.owner.name.s case t.len @@ -828,9 +831,9 @@ proc SameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = if a.kind != b.kind: return false case a.Kind of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString, - tyInt..tyBigNum, tyStmt: + tyInt..tyBigNum, tyStmt, tyExpr: result = sameFlags(a, b) - of tyExpr: + of tyStatic: result = ExprStructuralEquivalent(a.n, b.n) and sameFlags(a, b) of tyObject: IfFastObjectTypeCheckFailed(a, b): @@ -1038,7 +1041,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind, if not result: break if result and t.sons[0] != nil: result = typeAllowedAux(marker, t.sons[0], skResult, flags) - of tyExpr, tyStmt, tyTypeDesc: + of tyExpr, tyStmt, tyTypeDesc, tyStatic: result = true # XXX er ... no? these should not be allowed! of tyEmpty: @@ -1314,7 +1317,7 @@ proc compatibleEffects*(formal, actual: PType): bool = result = true proc isCompileTimeOnly*(t: PType): bool {.inline.} = - result = t.kind in {tyTypedesc, tyExpr} + result = t.kind in {tyTypedesc, tyStatic} proc containsCompileTimeOnly*(t: PType): bool = if isCompileTimeOnly(t): return true diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 84d82e117c..f5ea24aa2c 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -777,7 +777,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = const atomicTypes = {tyBool, tyChar, - tyExpr, tyStmt, tyTypeDesc, + tyExpr, tyStmt, tyTypeDesc, tyStatic, tyEnum, tyOrdinal, tyRange, @@ -937,8 +937,8 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = result = newNodeIT(nkUIntLit, info, t) of tyFloat..tyFloat128: result = newNodeIt(nkFloatLit, info, t) - of tyVar, tyPointer, tyPtr, tyCString, tySequence, tyString, tyExpr, - tyStmt, tyTypeDesc, tyProc, tyRef: + of tyVar, tyPointer, tyPtr, tyCString, tySequence, tyString, tyExpr, + tyStmt, tyTypeDesc, tyStatic, tyProc, tyRef: result = newNodeIT(nkNilLit, info, t) of tyObject: result = newNodeIT(nkPar, info, t) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index d01d4ebee4..44a3a34c3b 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -59,7 +59,8 @@ type nnkBindStmt, nnkMixinStmt, nnkUsingStmt, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, - nnkTupleTy, nnkTypeClassTy, nnkRecList, nnkRecCase, nnkRecWhen, + nnkTupleTy, nnkTypeClassTy, nnkStaticTy, + nnkRecList, nnkRecCase, nnkRecWhen, nnkRefTy, nnkPtrTy, nnkVarTy, nnkConstTy, nnkMutableTy, nnkDistinctTy, @@ -285,14 +286,15 @@ proc quote*(bl: stmt, op = "``"): PNimrodNode {.magic: "QuoteAst".} ## if not `ex`: ## echo `info` & ": Check failed: " & `expString` -template emit*(e: expr[string]): stmt = - ## accepts a single string argument and treats it as nimrod code - ## that should be inserted verbatim in the program - ## Example: - ## - ## emit("echo " & '"' & "hello world".toUpper & '"') - ## - eval: result = e.parseStmt +when not defined(booting): + template emit*(e: static[string]): stmt = + ## accepts a single string argument and treats it as nimrod code + ## that should be inserted verbatim in the program + ## Example: + ## + ## emit("echo " & '"' & "hello world".toUpper & '"') + ## + eval: result = e.parseStmt proc expectKind*(n: PNimrodNode, k: TNimrodNodeKind) {.compileTime.} = ## checks that `n` is of kind `k`. If this is not the case, diff --git a/lib/system.nim b/lib/system.nim index dc5a406d14..e58378c05b 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2641,7 +2641,7 @@ when hostOS != "standalone": x[j+i] = item[j] inc(j) -proc compiles*(x: expr): bool {.magic: "Compiles", noSideEffect.} = +proc compiles*(x): bool {.magic: "Compiles", noSideEffect.} = ## Special compile-time procedure that checks whether `x` can be compiled ## without any semantic error. ## This can be used to check whether a type supports some operation: diff --git a/tests/run/tmemoization.nim b/tests/run/tmemoization.nim index 78f0515f32..b59ff44ea2 100644 --- a/tests/run/tmemoization.nim +++ b/tests/run/tmemoization.nim @@ -5,7 +5,7 @@ discard """ import strutils -proc foo(s: expr[string]): string = +proc foo(s: static[string]): string = static: echo s const R = s.toUpper diff --git a/tests/run/tstaticparams.nim b/tests/run/tstaticparams.nim index f2d6e1dd65..23d644bce3 100644 --- a/tests/run/tstaticparams.nim +++ b/tests/run/tstaticparams.nim @@ -4,15 +4,15 @@ discard """ """ type - TFoo[T; Val: expr[string]] = object + TFoo[T; Val: static[string]] = object data: array[4, T] - TBar[T; I: expr[int]] = object + TBar[T; I: static[int]] = object data: array[I, T] - TA1[T; I: expr[int]] = array[I, T] - TA2[T; I: expr[int]] = array[0..I, T] - TA3[T; I: expr[int]] = array[I-1, T] + #TA1[T; I: static[int]] = array[I, T] + #TA2[T; I: static[int]] = array[0..I, T] + TA3[T; I: static[int]] = array[I-1, T] proc takeFoo(x: TFoo) = echo "abracadabra" diff --git a/todo.txt b/todo.txt index da7585500d..a0a8ce2b5a 100644 --- a/todo.txt +++ b/todo.txt @@ -38,7 +38,6 @@ version 0.9.x - macros as type pragmas - implicit deref for parameter matching - lazy overloading resolution: - * get rid of ``expr[typ]``, use perhaps ``static[typ]`` instead * special case ``tyStmt`` - FFI: * test libffi on windows From 952dbc4b8fd41bdf0f6b587147f62d8d15a95751 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Wed, 25 Dec 2013 00:01:12 +0200 Subject: [PATCH 02/15] documented static params --- compiler/semtypes.nim | 2 ++ doc/manual.txt | 54 +++++++++++++++++++++++++++++++++++++++++++ lib/system.nim | 10 ++++++++ 3 files changed, 66 insertions(+) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index d4d9537571..85928ee751 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -684,6 +684,8 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result.sons.setLen(result.sons.len - 1) of tyTypeClass: result = addImplicitGeneric(copyType(paramType, getCurrOwner(), false)) + of tyExpr: + result = addImplicitGeneric(newTypeS(tyGenericParam, c)) else: nil # result = liftingWalk(paramType) diff --git a/doc/manual.txt b/doc/manual.txt index dabff3d69d..dca43ecbb3 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -3827,6 +3827,60 @@ This is a simple syntactic transformation into: Special Types ============= +static[T] +--------- + +As their name suggests, static params must be known at compile-time: + +.. code-block:: nimrod + + proc precompiledRegex(pattern: static[string]): TRegEx = + var res {.global.} = re(pattern) + return res + + precompiledRegex("/d+") # Replaces the call with a precompiled + # regex, stored in a global variable + + precompiledRegex(paramStr(1)) # Error, command-line options + # are not known at compile-time + + +For the purposes of code generation, all static params are treated as +generic params - the proc will be compiled separately for each unique +supplied value (or combination of values). + +Furthermore, the system module defines a `semistatic[T]` type than can be +used to declare procs accepting both static and run-time values, which can +optimize their body according to the supplied param using the `isStatic(p)` +predicate: + +.. code-block:: nimrod + + # The following proc will be compiled once for each unique static + # value and also once for the case handling all run-time values: + + proc re(pattern: semistatic[string]): TRegEx = + when isStatic(pattern): + return precompiledRegex(pattern) + else: + return compile(pattern) + +Static params can also appear in the signatures of generic types: + +.. code-block:: nimrod + + type + Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T] + # Please, note how `Number` is just a type constraint here, while + # `static[int]` requires us to supply a compile-time int value + + AffineTransform2D[T] = Matrix[3, 3, T] + AffineTransform3D[T] = Matrix[4, 4, T] + + AffineTransform3D[float] # OK + AffineTransform2D[string] # Error, `string` is not a `Number` + + typedesc -------- diff --git a/lib/system.nim b/lib/system.nim index e58378c05b..d45137b9eb 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2675,3 +2675,13 @@ proc locals*(): TObject {.magic: "Locals", noSideEffect.} = ## the official signature says, the return type is not ``TObject`` but a ## tuple of a structure that depends on the current scope. nil + +when not defined(booting): + type + semistatic*[T] = static[T] | T + # indicates a param of proc specialized for each static value, + # but also accepting run-time values + + template isStatic*(x): expr = compiles(static(x)) + # checks whether `x` is a value known at compile-time + From 299cefdc984ef756ffb8dabb4e961a8d2505104f Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Wed, 25 Dec 2013 00:58:22 +0200 Subject: [PATCH 03/15] make the current PContext accessible through TCandidate the goal here is to remove all the hacks from ParamTypeMatch and to handle all type matching in typeRel (the context there is required to evaluate any static params and to run the compilation tests of user-defined type classes) --- compiler/semcall.nim | 20 +++++++-------- compiler/semexprs.nim | 16 ++++++------ compiler/semtypes.nim | 2 +- compiler/sigmatch.nim | 59 ++++++++++++++++++++++++------------------- compiler/suggest.nim | 2 +- 5 files changed, 53 insertions(+), 46 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 9e9614796d..0880f5fdef 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -47,14 +47,14 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, var z: TCandidate if sym == nil: return - initCandidate(best, sym, initialBinding, symScope) - initCandidate(alt, sym, initialBinding, symScope) + initCandidate(c, best, sym, initialBinding, symScope) + initCandidate(c, alt, sym, initialBinding, symScope) best.state = csNoMatch while sym != nil: if sym.kind in filter: determineType(c, sym) - initCandidate(z, sym, initialBinding, o.lastOverloadScope) + initCandidate(c, z, sym, initialBinding, o.lastOverloadScope) z.calleeSym = sym matches(c, n, orig, z) if errors != nil: @@ -199,15 +199,15 @@ proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) = proc IndexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode = var m: TCandidate - initCandidate(m, f) - result = paramTypesMatch(c, m, f, a, arg, nil) + initCandidate(c, m, f) + result = paramTypesMatch(m, f, a, arg, nil) if m.genericConverter and result != nil: instGenericConvertersArg(c, result, m) proc ConvertTo*(c: PContext, f: PType, n: PNode): PNode = var m: TCandidate - initCandidate(m, f) - result = paramTypesMatch(c, m, f, n.typ, n, nil) + initCandidate(c, m, f) + result = paramTypesMatch(m, f, n.typ, n, nil) if m.genericConverter and result != nil: instGenericConvertersArg(c, result, m) @@ -243,9 +243,9 @@ proc explicitGenericInstError(n: PNode): PNode = result = n proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = - var x: TCandidate - initCandidate(x, s, n) - var newInst = generateInstance(c, s, x.bindings, n.info) + var m: TCandidate + initCandidate(c, m, s, n) + var newInst = generateInstance(c, s, m.bindings, n.info) markUsed(n, s) result = newSymNode(newInst, n.info) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a7fd1eaa08..67373c3032 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -142,7 +142,7 @@ proc checkConversionBetweenObjects(castDest, src: PType): TConvStatus = const IntegralTypes = {tyBool, tyEnum, tyChar, tyInt..tyUInt64} -proc checkConvertible(castDest, src: PType): TConvStatus = +proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus = result = convOK if sameType(castDest, src) and castDest.sym == src.sym: # don't annoy conversions that may be needed on another processor: @@ -163,7 +163,7 @@ proc checkConvertible(castDest, src: PType): TConvStatus = # accept conversion between integral types else: # we use d, s here to speed up that operation a bit: - case cmpTypes(d, s) + case cmpTypes(c, d, s) of isNone, isGeneric: if not compareTypes(castDest, src, dcEqIgnoreDistinct): result = convNotLegal @@ -202,7 +202,7 @@ proc semConv(c: PContext, n: PNode): PNode = var op = result.sons[1] if not isSymChoice(op): - let status = checkConvertible(result.typ, op.typ) + let status = checkConvertible(c, result.typ, op.typ) case status of convOK: nil of convNotNeedeed: @@ -213,7 +213,7 @@ proc semConv(c: PContext, n: PNode): PNode = else: for i in countup(0, sonsLen(op) - 1): let it = op.sons[i] - let status = checkConvertible(result.typ, it.typ) + let status = checkConvertible(c, result.typ, it.typ) if status == convOK: markUsed(n, it.sym) markIndirect(c, it.sym) @@ -324,15 +324,15 @@ proc isOpImpl(c: PContext, n: PNode): PNode = case t2.kind of tyTypeClasses: var m: TCandidate - InitCandidate(m, t2) + InitCandidate(c, m, t2) match = matchUserTypeClass(c, m, emptyNode, t2, t1) != nil of tyOrdinal: var m: TCandidate - InitCandidate(m, t2) + InitCandidate(c, m, t2) match = isOrdinalType(t1) of tySequence, tyArray, tySet: var m: TCandidate - InitCandidate(m, t2) + InitCandidate(c, m, t2) match = typeRel(m, t2, t1) != isNone else: match = sameType(t1, t2) @@ -707,7 +707,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = if t != nil and t.kind == tyProc: # This is a proc variable, apply normal overload resolution var m: TCandidate - initCandidate(m, t) + initCandidate(c, m, t) matches(c, n, nOrig, m) if m.state != csMatch: if c.inCompilesContext > 0: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 85928ee751..7e76e950b6 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -838,7 +838,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = else: internalAssert s.typ.kind == tyGenericBody - var m = newCandidate(s, n) + var m = newCandidate(c, s, n) matches(c, n, copyTree(n), m) if m.state != csMatch: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 87f1decf48..42eefec5a2 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -21,7 +21,8 @@ type TCandidateState* = enum csEmpty, csMatch, csNoMatch - TCandidate* {.final.} = object + TCandidate* {.final.} = object + c*: PContext exactMatches*: int # also misused to prefer iters over procs genericMatches: int # also misused to prefer constraints subtypeMatches: int @@ -58,7 +59,9 @@ const proc markUsed*(n: PNode, s: PSym) -proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} = +proc initCandidateAux(ctx: PContext, + c: var TCandidate, callee: PType) {.inline.} = + c.c = ctx c.exactMatches = 0 c.subtypeMatches = 0 c.convMatches = 0 @@ -71,17 +74,17 @@ proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} = c.genericConverter = false c.inheritancePenalty = 0 -proc initCandidate*(c: var TCandidate, callee: PType) = - initCandidateAux(c, callee) +proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PType) = + initCandidateAux(ctx, c, callee) c.calleeSym = nil initIdTable(c.bindings) proc put(t: var TIdTable, key, val: PType) {.inline.} = IdTablePut(t, key, val) -proc initCandidate*(c: var TCandidate, callee: PSym, binding: PNode, - calleeScope = -1) = - initCandidateAux(c, callee.typ) +proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, + binding: PNode, calleeScope = -1) = + initCandidateAux(ctx, c, callee.typ) c.calleeSym = callee c.calleeScope = calleeScope initIdTable(c.bindings) @@ -93,10 +96,12 @@ proc initCandidate*(c: var TCandidate, callee: PSym, binding: PNode, #debug(formalTypeParam) put(c.bindings, formalTypeParam, binding[i].typ) -proc newCandidate*(callee: PSym, binding: PNode, calleeScope = -1): TCandidate = - initCandidate(result, callee, binding, calleeScope) +proc newCandidate*(ctx: PContext, callee: PSym, + binding: PNode, calleeScope = -1): TCandidate = + initCandidate(ctx, result, callee, binding, calleeScope) proc copyCandidate(a: var TCandidate, b: TCandidate) = + a.c = b.c a.exactMatches = b.exactMatches a.subtypeMatches = b.subtypeMatches a.convMatches = b.convMatches @@ -762,10 +767,10 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation = result = isEqual else: internalError("typeRel: " & $f.kind) -proc cmpTypes*(f, a: PType): TTypeRelation = - var c: TCandidate - InitCandidate(c, f) - result = typeRel(c, f, a) +proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = + var m: TCandidate + InitCandidate(c, m, f) + result = typeRel(m, f, a) proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate, f: PType): PType = @@ -887,13 +892,14 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, result = arg put(m.bindings, f, a) -proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, argType: PType, +proc ParamTypesMatchAux(m: var TCandidate, f, argType: PType, argSemantized, argOrig: PNode): PNode = var r: TTypeRelation arg = argSemantized let + c = m.c a0 = if c.InTypeClass > 0: argType.skipTypes({tyTypeDesc}) else: argType a = if a0 != nil: a0.skipTypes({tyStatic}) else: a0 @@ -1007,19 +1013,20 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, argType: PType, else: result = userConvMatch(c, m, base(f), a, arg) -proc ParamTypesMatch*(c: PContext, m: var TCandidate, f, a: PType, +proc ParamTypesMatch*(m: var TCandidate, f, a: PType, arg, argOrig: PNode): PNode = if arg == nil or arg.kind notin nkSymChoices: - result = ParamTypesMatchAux(c, m, f, a, arg, argOrig) + result = ParamTypesMatchAux(m, f, a, arg, argOrig) else: # CAUTION: The order depends on the used hashing scheme. Thus it is # incorrect to simply use the first fitting match. However, to implement # this correctly is inefficient. We have to copy `m` here to be able to # roll back the side effects of the unification algorithm. + let c = m.c var x, y, z: TCandidate - initCandidate(x, m.callee) - initCandidate(y, m.callee) - initCandidate(z, m.callee) + initCandidate(c, x, m.callee) + initCandidate(c, y, m.callee) + initCandidate(c, z, m.callee) x.calleeSym = m.calleeSym y.calleeSym = m.calleeSym z.calleeSym = m.calleeSym @@ -1051,7 +1058,7 @@ proc ParamTypesMatch*(c: PContext, m: var TCandidate, f, a: PType, else: # only one valid interpretation found: markUsed(arg, arg.sons[best].sym) - result = ParamTypesMatchAux(c, m, f, arg.sons[best].typ, arg.sons[best], + result = ParamTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best], argOrig) proc setSon(father: PNode, at: int, son: PNode) = @@ -1142,7 +1149,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.baseTypeMatch = false n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1]) n.sons[a].typ = n.sons[a].sons[1].typ - var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, + var arg = ParamTypesMatch(m, formal.typ, n.sons[a].typ, n.sons[a].sons[1], nOrig.sons[a].sons[1]) if arg == nil: m.state = csNoMatch @@ -1172,7 +1179,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, elif formal != nil: m.baseTypeMatch = false n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) - var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, + var arg = ParamTypesMatch(m, formal.typ, n.sons[a].typ, n.sons[a], nOrig.sons[a]) if (arg != nil) and m.baseTypeMatch and (container != nil): addSon(container, arg) @@ -1195,7 +1202,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, return m.baseTypeMatch = false n.sons[a] = prepareOperand(c, formal.typ, n.sons[a]) - var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, + var arg = ParamTypesMatch(m, formal.typ, n.sons[a].typ, n.sons[a], nOrig.sons[a]) if arg == nil: m.state = csNoMatch @@ -1249,8 +1256,8 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = proc argtypeMatches*(c: PContext, f, a: PType): bool = var m: TCandidate - initCandidate(m, f) - let res = paramTypesMatch(c, m, f, a, ast.emptyNode, nil) + initCandidate(c, m, f) + let res = paramTypesMatch(m, f, a, ast.emptyNode, nil) #instantiateGenericConverters(c, res, m) # XXX this is used by patterns.nim too; I think it's better to not # instantiate generic converters for that @@ -1312,7 +1319,7 @@ tests: setup: var c: TCandidate - InitCandidate(c, nil) + InitCandidate(nil, c, nil) template yes(x, y) = test astToStr(x) & " is " & astToStr(y): diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 76a6c21d92..04a222db44 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -119,7 +119,7 @@ proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool = case candidate.kind of OverloadableSyms: var m: TCandidate - initCandidate(m, candidate, nil) + initCandidate(c, m, candidate, nil) sigmatch.partialMatch(c, n, nOrig, m) result = m.state != csNoMatch else: From 1d02f2ea531ad14f686a75c30af9228ba84fa194 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Wed, 25 Dec 2013 19:25:04 +0200 Subject: [PATCH 04/15] wip type class reforms (the compiler bootstraps fine) * replace tfAny and tfAll with tyAnd and tyOr * integrate matchTypeClass into typeRel * introduce tyBuiltInTypeClass to handle types such as tuple, object, proc, etc --- compiler/ast.nim | 8 ++--- compiler/msgs.nim | 2 ++ compiler/semdata.nim | 15 +++++++++ compiler/semexprs.nim | 2 +- compiler/semtypes.nim | 53 ++++++++++++++++++------------ compiler/semtypinst.nim | 2 +- compiler/sigmatch.nim | 40 +++++++++++++---------- compiler/types.nim | 71 +++++++++-------------------------------- 8 files changed, 94 insertions(+), 99 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 462bad24f0..64f959fe26 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -339,6 +339,7 @@ type tyTypeClass tyParametricTypeClass # structured similarly to tyGenericInst # lastSon is the body of the type class + tyBuiltInTypeClass tyAnd tyOr tyNot @@ -349,7 +350,8 @@ const tyPureObject* = tyTuple GcTypeKinds* = {tyRef, tySequence, tyString} tyError* = tyProxy # as an errornous node should match everything - tyTypeClasses* = {tyTypeClass, tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything} + tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass, + tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything} type TTypeKinds* = set[TTypeKind] @@ -384,9 +386,6 @@ type # proc foo(T: typedesc, list: seq[T]): var T tfRetType, # marks return types in proc (used to detect type classes # used as return types for return type inference) - tfAll, # type class requires all constraints to be met (default) - tfAny, # type class requires any constraint to be met - tfNot, # type class with a negative check tfCapturesEnv, # whether proc really captures some environment tfByCopy, # pass object/tuple by copy (C backend) tfByRef, # pass object/tuple by reference (C backend) @@ -399,6 +398,7 @@ type tfHasShared, # type constains a "shared" constraint modifier somewhere tfHasMeta, # type has "typedesc" or "expr" somewhere; or uses '|' tfHasGCedMem, # type contains GC'ed memory + tfGenericTypeParam TTypeFlags* = set[TTypeFlag] diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 895ba71f30..9c24295a69 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -643,6 +643,8 @@ proc toFileLine*(info: TLineInfo): string {.inline.} = proc toFileLineCol*(info: TLineInfo): string {.inline.} = result = info.toFilename & "(" & $info.line & "," & $info.col & ")" +template `$`*(info: TLineInfo): expr = toFileLineCol(info) + proc `??`* (info: TLineInfo, filename: string): bool = # only for debugging purposes result = filename in info.toFilename diff --git a/compiler/semdata.nim b/compiler/semdata.nim index d02359d4ce..1984bfccb7 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -213,6 +213,21 @@ proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc) return newSymNode(sym, info) +proc makeAndType*(c: PContext, t1, t2: PType): PType = + result = newTypeS(tyAnd, c) + result.sons = @[t1, t2] + result.flags.incl tfHasMeta + +proc makeOrType*(c: PContext, t1, t2: PType): PType = + result = newTypeS(tyOr, c) + result.sons = @[t1, t2] + result.flags.incl tfHasMeta + +proc makeNotType*(c: PContext, t1: PType): PType = + result = newTypeS(tyNot, c) + result.sons = @[t1] + result.flags.incl tfHasMeta + proc newTypeS(kind: TTypeKind, c: PContext): PType = result = newType(kind, getCurrOwner()) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 67373c3032..fde09400d9 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1163,7 +1163,7 @@ proc semAsgn(c: PContext, n: PNode): PNode = if lhsIsResult: n.typ = EnforceVoidContext if lhs.sym.typ.kind == tyGenericParam: - if matchTypeClass(lhs.typ, rhs.typ): + if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric: InternalAssert c.p.resultSym != nil lhs.typ = rhs.typ c.p.resultSym.typ = rhs.typ diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 7e76e950b6..17b7687b49 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -18,7 +18,7 @@ proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType = if result.kind == tyForward: result.kind = kind proc newConstraint(c: PContext, k: TTypeKind): PType = - result = newTypeS(tyTypeClass, c) + result = newTypeS(tyBuiltInTypeClass, c) result.addSonSkipIntLit(newTypeS(k, c)) proc semEnum(c: PContext, n: PNode, prev: PType): PType = @@ -603,6 +603,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType = let finalTypId = if typId != nil: typId else: getIdent(paramName & ":type") + if genericParams == nil: + # This happens with anonymous proc types appearing in signatures + # XXX: we need to lift these earlier + return # is this a bindOnce type class already present in the param list? for i in countup(0, genericParams.len - 1): if genericParams.sons[i].sym.name.id == finalTypId.id: @@ -674,7 +678,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramType.sons[i] = lifted result = paramType - if paramType.lastSon.kind == tyTypeClass: + if paramType.lastSon.kind == tyTypeClass and false: result = paramType result.kind = tyParametricTypeClass result = addImplicitGeneric(copyType(result, @@ -682,10 +686,16 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, elif result != nil: result.kind = tyGenericInvokation result.sons.setLen(result.sons.len - 1) - of tyTypeClass: + of tyTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot: result = addImplicitGeneric(copyType(paramType, getCurrOwner(), false)) of tyExpr: result = addImplicitGeneric(newTypeS(tyGenericParam, c)) + of tyGenericParam: + if tfGenericTypeParam in paramType.flags and false: + if paramType.sonsLen > 0: + result = liftingWalk(paramType.lastSon) + else: + result = addImplicitGeneric(newTypeS(tyGenericParam, c)) else: nil # result = liftingWalk(paramType) @@ -917,24 +927,27 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = var t1 = semTypeNode(c, n.sons[1], nil) t2 = semTypeNode(c, n.sons[2], nil) - if t1 == nil: + if t1 == nil: LocalError(n.sons[1].info, errTypeExpected) result = newOrPrevType(tyError, prev, c) - elif t2 == nil: + elif t2 == nil: LocalError(n.sons[2].info, errTypeExpected) result = newOrPrevType(tyError, prev, c) else: - result = newTypeS(tyTypeClass, c) - result.addSonSkipIntLit(t1) - result.addSonSkipIntLit(t2) - result.flags.incl(if op.id == ord(wAnd): tfAll else: tfAny) - result.flags.incl(tfHasMeta) + result = if op.id == ord(wAnd): makeAndType(c, t1, t2) + else: makeOrType(c, t1, t2) elif op.id == ord(wNot): - checkSonsLen(n, 3) - result = semTypeNode(c, n.sons[1], prev) - if result.kind in NilableTypes and n.sons[2].kind == nkNilLit: - result = freshType(result, prev) - result.flags.incl(tfNotNil) + case n.len + of 3: + result = semTypeNode(c, n.sons[1], prev) + if result.kind in NilableTypes and n.sons[2].kind == nkNilLit: + result = freshType(result, prev) + result.flags.incl(tfNotNil) + else: + LocalError(n.info, errGenerated, "invalid type") + of 2: + let negated = semTypeNode(c, n.sons[1], prev) + result = makeNotType(c, negated) else: LocalError(n.info, errGenerated, "invalid type") else: @@ -1088,11 +1101,7 @@ proc processMagicType(c: PContext, m: PSym) = else: LocalError(m.info, errTypeExpected) proc semGenericConstraints(c: PContext, x: PType): PType = - if x.kind in StructuralEquivTypes and ( - sonsLen(x) == 0 or x.sons[0].kind in {tyGenericParam, tyEmpty}): - result = newConstraint(c, x.kind) - else: - result = newTypeWithSons(c, tyGenericParam, @[x]) + result = newTypeWithSons(c, tyGenericParam, @[x]) proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = result = copyNode(n) @@ -1127,7 +1136,9 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = if typ == nil: typ = newTypeS(tyGenericParam, c) - + + typ.flags.incl tfGenericTypeParam + for j in countup(0, L-3): let finalType = if j == 0: typ else: copyType(typ, typ.owner, false) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index d05d063aab..f23d87763d 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -19,7 +19,7 @@ proc checkPartialConstructedType(info: TLineInfo, t: PType) = proc checkConstructedType*(info: TLineInfo, typ: PType) = var t = typ.skipTypes({tyDistinct}) - if t.kind in {tyTypeClass}: nil + if t.kind in tyTypeClasses: nil elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject: LocalError(info, errInvalidPragmaX, "acyclic") elif t.kind == tyVar and t.sons[0].kind == tyVar: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 42eefec5a2..03c37438cb 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -370,10 +370,6 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = of tyNil: result = f.allowsNil else: nil -proc matchTypeClass(c: var TCandidate, f, a: PType): TTypeRelation = - result = if matchTypeClass(c.bindings, f, a): isGeneric - else: isNone - proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = let a0 = firstOrd(a) @@ -406,7 +402,7 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation = # order to give preferrence to the most specific one: # # seq[seq[any]] is a strict subset of seq[any] and hence more specific. - + result = isNone assert(f != nil) assert(a != nil) @@ -462,10 +458,10 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation = else: nil case f.kind - of tyEnum: + of tyEnum: if a.kind == f.kind and sameEnumTypes(f, a): result = isEqual elif sameEnumTypes(f, skipTypes(a, {tyRange})): result = isSubtype - of tyBool, tyChar: + of tyBool, tyChar: if a.kind == f.kind: result = isEqual elif skipTypes(a, {tyRange}).kind == f.kind: result = isSubtype of tyRange: @@ -706,7 +702,20 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation = return isGeneric else: return typeRel(c, prev, a) - + + of tyBuiltInTypeClass: + var prev = PType(idTableGet(c.bindings, f)) + if prev == nil: + let targetKind = f.sons[0].kind + if targetKind == a.skipTypes({tyRange}).kind or + (targetKind in {tyProc, tyPointer} and a.kind == tyNil): + put(c.bindings, f, a) + return isGeneric + else: + return isNone + else: + result = typeRel(c, prev, a) + of tyGenericParam, tyTypeClass: var x = PType(idTableGet(c.bindings, f)) if x == nil: @@ -727,11 +736,11 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation = else: result = isNone else: - if a.kind == tyTypeClass: - result = isGeneric + if f.sonsLen > 0: + result = typeRel(c, f.lastSon, a) else: - result = matchTypeClass(c, f, a) - + result = isGeneric + if result == isGeneric: var concrete = concreteType(c, a) if concrete == nil: @@ -751,7 +760,7 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation = if f.sonsLen == 0: result = isGeneric else: - result = matchTypeClass(c, f, a.sons[0]) + result = typeRel(c, f, a.sons[0]) if result == isGeneric: put(c.bindings, f, a) else: @@ -911,9 +920,8 @@ proc ParamTypesMatchAux(m: var TCandidate, f, argType: PType, InternalAssert a.len > 0 r = typeRel(m, f.lastSon, a.lastSon) else: - let match = matchTypeClass(m.bindings, fMaybeStatic, a) - if not match: r = isNone - else: + r = typeRel(m, fMaybeStatic, a) + if r != isNone: # XXX: Ideally, this should happen much earlier somewhere near # semOpAux, but to do that, we need to be able to query the # overload set to determine whether compile-time value is expected diff --git a/compiler/types.nim b/compiler/types.nim index 5fe128bbb9..872834810e 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -409,18 +409,8 @@ const "uint", "uint8", "uint16", "uint32", "uint64", "bignum", "const ", "!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass", - "ParametricTypeClass", "and", "or", "not", "any", "static"] - -proc consToStr(t: PType): string = - if t.len > 0: result = t.typeToString - else: result = typeToStr[t.kind].strip - -proc constraintsToStr(t: PType): string = - let sep = if tfAny in t.flags: " or " else: " and " - result = "" - for i in countup(0, t.len - 1): - if i > 0: result.add(sep) - result.add(t.sons[i].consToStr) + "ParametricTypeClass", "BuiltInTypeClass", + "and", "or", "not", "any", "static"] proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = var t = typ @@ -444,19 +434,24 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = add(result, ']') of tyTypeDesc: if t.len == 0: result = "typedesc" - else: result = "typedesc[" & constraintsToStr(t) & "]" + else: result = "typedesc[" & typeToString(t) & "]" of tyStatic: InternalAssert t.len > 0 - result = "static[" & constraintsToStr(t) & "]" + result = "static[" & typeToString(t) & "]" of tyTypeClass: - if t.n != nil: return t.sym.owner.name.s - case t.len - of 0: result = "typeclass[]" - of 1: result = "typeclass[" & consToStr(t.sons[0]) & "]" - else: result = constraintsToStr(t) + InternalAssert t.sym != nil and t.sym.owner != nil + return t.sym.owner.name.s + of tyBuiltInTypeClass: + return "TypeClass" + of tyAnd: + result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1]) + of tyOr: + result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1]) + of tyNot: + result = "not " & typeToString(t.sons[0]) of tyExpr: if t.len == 0: result = "expr" - else: result = "expr[" & constraintsToStr(t) & "]" + else: result = "expr[" & typeToString(t) & "]" of tyArray: if t.sons[0].kind == tyRange: result = "array[" & rangeToStr(t.sons[0].n) & ", " & @@ -978,42 +973,6 @@ proc isGenericAlias*(t: PType): bool = proc skipGenericAlias*(t: PType): PType = return if t.isGenericAlias: t.lastSon else: t -proc matchTypeClass*(bindings: var TIdTable, typeClass, t: PType): bool = - for i in countup(0, typeClass.sonsLen - 1): - let req = typeClass.sons[i] - var match = req.kind == skipTypes(t, {tyRange, tyGenericInst}).kind - - if not match: - case req.kind - of tyGenericBody: - if t.kind == tyGenericInst and t.sons[0] == req: - match = true - IdTablePut(bindings, typeClass, t) - of tyTypeClass: - match = matchTypeClass(bindings, req, t) - elif t.kind == tyTypeClass: - match = matchTypeClass(bindings, t, req) - - elif t.kind in {tyObject} and req.len != 0: - # empty 'object' is fine as constraint in a type class - match = sameType(t, req) - - if tfAny in typeClass.flags: - if match: return true - else: - if not match: return false - - # if the loop finished without returning, either all constraints matched - # or none of them matched. - result = if tfAny in typeClass.flags: false else: true - if result == true: - IdTablePut(bindings, typeClass, t) - -proc matchTypeClass*(typeClass, typ: PType): bool = - var bindings: TIdTable - initIdTable(bindings) - result = matchTypeClass(bindings, typeClass, typ) - proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind, flags: TTypeAllowedFlags = {}): bool = assert(kind in {skVar, skLet, skConst, skParam, skResult}) From edab4aaad02bf414f7f0c6e3148ade8a7b485c40 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Wed, 25 Dec 2013 22:29:44 +0200 Subject: [PATCH 05/15] better integration of tyStatic into typeRel --- compiler/ast.nim | 1 + compiler/evals.nim | 2 +- compiler/msgs.nim | 14 +++++----- compiler/sem.nim | 10 +++++++ compiler/semdata.nim | 5 ++++ compiler/semtypes.nim | 16 +++++------ compiler/semtypinst.nim | 8 +++--- compiler/sigmatch.nim | 60 +++++++++++++++++++---------------------- compiler/types.nim | 8 +++--- 9 files changed, 66 insertions(+), 58 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 64f959fe26..130f5a5adc 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -399,6 +399,7 @@ type tfHasMeta, # type has "typedesc" or "expr" somewhere; or uses '|' tfHasGCedMem, # type contains GC'ed memory tfGenericTypeParam + tfHasStatic TTypeFlags* = set[TTypeFlag] diff --git a/compiler/evals.nim b/compiler/evals.nim index 4a2586d5f8..f7d94e4c0f 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -91,7 +91,7 @@ proc evalMacroCall*(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode = - if defined(debug): writeStackTrace() + if defined(debug) and gVerbosity >= 3: writeStackTrace() result = newNodeI(nkExceptBranch, info) # creating a nkExceptBranch without sons # means that it could not be evaluated diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 9c24295a69..81e738ee82 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -701,23 +701,21 @@ type TErrorHandling = enum doNothing, doAbort, doRaise proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = - template maybeTrace = - if defined(debug) or gVerbosity >= 3: - writeStackTrace() + template quit = + if defined(debug) or gVerbosity >= 3: writeStackTrace() + quit 1 if msg == errInternal: writeStackTrace() # we always want a stack trace here if msg >= fatalMin and msg <= fatalMax: - maybeTrace() - quit(1) + quit() if msg >= errMin and msg <= errMax: - maybeTrace() inc(gErrorCounter) options.gExitcode = 1'i8 if gErrorCounter >= gErrorMax: - quit(1) + quit() elif eh == doAbort and gCmd != cmdIdeTools: - quit(1) + quit() elif eh == doRaise: raiseRecoverableError(s) diff --git a/compiler/sem.nim b/compiler/sem.nim index 3ace623bca..67d400ac50 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -189,6 +189,15 @@ proc evalConstExpr(c: PContext, module: PSym, e: PNode): PNode = proc evalStaticExpr(c: PContext, module: PSym, e: PNode, prc: PSym): PNode = result = evalConstExprAux(c.createEvalContext(emStatic), module, prc, e) +proc tryConstExpr(c: PContext, n: PNode): PNode = + var e = semExprWithType(c, n) + if e == nil: return + result = getConstExpr(c.module, e) + if result == nil: + result = evalConstExpr(c, c.module, e) + if result == nil or result.kind == nkEmpty: + return nil + proc semConstExpr(c: PContext, n: PNode): PNode = var e = semExprWithType(c, n) if e == nil: @@ -282,6 +291,7 @@ proc myOpen(module: PSym): PPassContext = c.semConstExpr = semConstExpr c.semExpr = semExpr c.semTryExpr = tryExpr + c.semTryConstExpr = tryConstExpr c.semOperand = semOperand c.semConstBoolExpr = semConstBoolExpr c.semOverloadedCall = semOverloadedCall diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 1984bfccb7..874e5dab46 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -75,6 +75,7 @@ type semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} semTryExpr*: proc (c: PContext, n: PNode,flags: TExprFlags = {}, bufferErrors = false): PNode {.nimcall.} + semTryConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, @@ -217,11 +218,15 @@ proc makeAndType*(c: PContext, t1, t2: PType): PType = result = newTypeS(tyAnd, c) result.sons = @[t1, t2] result.flags.incl tfHasMeta + if tfHasStatic in t1.flags or tfHasStatic in t2.flags: + result.flags.incl tfHasStatic proc makeOrType*(c: PContext, t1, t2: PType): PType = result = newTypeS(tyOr, c) result.sons = @[t1, t2] result.flags.incl tfHasMeta + if tfHasStatic in t1.flags or tfHasStatic in t2.flags: + result.flags.incl tfHasStatic proc makeNotType*(c: PContext, t1: PType): PType = result = newTypeS(tyNot, c) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 17b7687b49..2016220162 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -678,12 +678,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramType.sons[i] = lifted result = paramType - if paramType.lastSon.kind == tyTypeClass and false: - result = paramType - result.kind = tyParametricTypeClass - result = addImplicitGeneric(copyType(result, - getCurrOwner(), false)) - elif result != nil: + if result == nil: + result = liftingWalk(paramType.lastSon) + else: result.kind = tyGenericInvokation result.sons.setLen(result.sons.len - 1) of tyTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot: @@ -1014,9 +1011,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = 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) - result.rawAddSon(base) + result = newOrPrevType(tyStatic, prev, c) + var base = semTypeNode(c, n.sons[0], nil) + result.rawAddSon(base) + result.flags.incl tfHasStatic of nkProcTy, nkIteratorTy: if n.sonsLen == 0: result = newConstraint(c, tyProc) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index f23d87763d..69d91766b9 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -194,16 +194,16 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = result = t - if t == nil: return + if t == nil: return + if t.kind == tyStatic and t.sym != nil and t.sym.kind == skGenericParam: + return lookupTypeVar(cl, t) + case t.kind of tyTypeClass: nil of tyGenericParam: result = lookupTypeVar(cl, t) if result.kind == tyGenericInvokation: result = handleGenericInvokation(cl, result) - of tyStatic: - if t.sym != nil and t.sym.kind == skGenericParam: - result = lookupTypeVar(cl, t) of tyGenericInvokation: result = handleGenericInvokation(cl, t) of tyGenericBody: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 03c37438cb..c3d197c3ba 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -753,6 +753,14 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation = result = isGeneric else: result = typeRel(c, x, a) # check if it fits + + of tyStatic: + if a.kind == tyStatic: + result = typeRel(c, f.lastSon, a.lastSon) + if result != isNone: put(c.bindings, f, a) + else: + result = isNone + of tyTypeDesc: var prev = PType(idTableGet(c.bindings, f)) if prev == nil: @@ -904,40 +912,28 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, proc ParamTypesMatchAux(m: var TCandidate, f, argType: PType, argSemantized, argOrig: PNode): PNode = var - r: TTypeRelation - arg = argSemantized - - let - c = m.c - a0 = if c.InTypeClass > 0: argType.skipTypes({tyTypeDesc}) - else: argType - a = if a0 != nil: a0.skipTypes({tyStatic}) else: a0 fMaybeStatic = f.skipTypes({tyDistinct}) + arg = argSemantized + c = m.c + argType = argType + if tfHasStatic in fMaybeStatic.flags: + # XXX: When implicit statics are the default + # this will be done earlier - we just have to + # make sure that static types enter here + var evaluated = c.semTryConstExpr(c, arg) + if evaluated != nil: + arg.typ = newTypeS(tyStatic, c) + arg.typ.sons = @[evaluated.typ] + arg.typ.n = evaluated + argType = arg.typ + + var + r: TTypeRelation + a = if c.InTypeClass > 0: argType.skipTypes({tyTypeDesc}) + else: argType + case fMaybeStatic.kind - of tyStatic: - if a.kind == tyStatic: - InternalAssert a.len > 0 - r = typeRel(m, f.lastSon, a.lastSon) - else: - r = typeRel(m, fMaybeStatic, a) - if r != isNone: - # XXX: Ideally, this should happen much earlier somewhere near - # semOpAux, but to do that, we need to be able to query the - # overload set to determine whether compile-time value is expected - # for the param before entering the full-blown sigmatch algorithm. - # This is related to the immediate pragma since querying the - # overload set could help there too. - var evaluated = c.semConstExpr(c, arg) - if evaluated != nil: - r = isGeneric - arg.typ = newTypeS(tyStatic, c) - arg.typ.sons = @[evaluated.typ] - arg.typ.n = evaluated - - if r == isGeneric: - put(m.bindings, f, arg.typ) - of tyTypeClass, tyParametricTypeClass: if fMaybeStatic.n != nil: let match = matchUserTypeClass(c, m, arg, fMaybeStatic, a) @@ -955,7 +951,7 @@ proc ParamTypesMatchAux(m: var TCandidate, f, argType: PType, r = typeRel(m, f, a) case r - of isConvertible: + of isConvertible: inc(m.convMatches) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) of isIntConv: diff --git a/compiler/types.nim b/compiler/types.nim index 872834810e..f6e426f396 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -434,10 +434,10 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = add(result, ']') of tyTypeDesc: if t.len == 0: result = "typedesc" - else: result = "typedesc[" & typeToString(t) & "]" + else: result = "typedesc[" & typeToString(t.sons[0]) & "]" of tyStatic: InternalAssert t.len > 0 - result = "static[" & typeToString(t) & "]" + result = "static[" & typeToString(t.sons[0]) & "]" of tyTypeClass: InternalAssert t.sym != nil and t.sym.owner != nil return t.sym.owner.name.s @@ -450,8 +450,8 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = of tyNot: result = "not " & typeToString(t.sons[0]) of tyExpr: - if t.len == 0: result = "expr" - else: result = "expr[" & typeToString(t) & "]" + InternalAssert t.len == 0 + result = "expr" of tyArray: if t.sons[0].kind == tyRange: result = "array[" & rangeToStr(t.sons[0].n) & ", " & From 86108be24b43d1b4b02a6549e75f64b7625233ac Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Wed, 25 Dec 2013 22:40:06 +0200 Subject: [PATCH 06/15] test case for semistatic --- tests/run/tsemistatic.nim | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/run/tsemistatic.nim diff --git a/tests/run/tsemistatic.nim b/tests/run/tsemistatic.nim new file mode 100644 index 0000000000..d187f153cc --- /dev/null +++ b/tests/run/tsemistatic.nim @@ -0,0 +1,24 @@ +discard """ + msg: "static 10\ndynamic\nstatic 20\n" + output: "s\nd\nd\ns" +""" + +proc foo(x: semistatic[int]) = + when isStatic(x): + static: echo "static ", x + echo "s" + else: + static: echo "dynamic" + echo "d" + +foo 10 + +var + x = 10 + y: int + +foo x +foo y + +foo 20 + From e2594f44bdc99e1c7930dfb66fa258a504ee4d79 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 26 Dec 2013 01:09:10 +0200 Subject: [PATCH 07/15] bugfix: in some contexts, newSeq[T](n) is incorrectly inferred to have a seq[typedesc[T]] type --- compiler/sigmatch.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index c3d197c3ba..6a68882237 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -93,8 +93,10 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, var typeParams = callee.ast[genericParamsPos] for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1): var formalTypeParam = typeParams.sons[i-1].typ - #debug(formalTypeParam) - put(c.bindings, formalTypeParam, binding[i].typ) + var bound = binding[i].typ + if formalTypeParam.kind != tyTypeDesc: + bound = bound.skipTypes({tyTypeDesc}) + put(c.bindings, formalTypeParam, bound) proc newCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1): TCandidate = From 4eea2f17d36b2cf239f0a987b5d9715a81b2b70f Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 27 Dec 2013 13:00:45 +0200 Subject: [PATCH 08/15] forgotten modification to the news files --- web/news.txt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/web/news.txt b/web/news.txt index f919089a53..78079ea163 100644 --- a/web/news.txt +++ b/web/news.txt @@ -30,6 +30,8 @@ Changes affecting backwards compatibility - ``os.parentDir`` now returns "" if there is no parent dir. - ``quoteIfContainsWhite`` now escapes argument in such way that it can be safely passed to shell, instead of just adding double quotes. +- ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``, + ``dumpTreeImm`` and ``dumpLispImm`` are now deprecated. Compiler Additions @@ -50,13 +52,13 @@ Language Additions - Arrays can now be declared with a single integer literal ``N`` instead of a range; the range is then ``0..N-1``. -- ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``, - ``dumpTreeImm`` and ``dumpLispImm`` are now deprecated. - Added ``requiresInit`` pragma to enforce explicit initialization. -- Added ``using statement`` for better authoring domain-specific languages and - OOP-like syntactic sugar. -- Added ``delegator pragma`` for handling calls to missing procs and fields at - compile-time. +- The ``using statement`` enables you to more easily author domain-specific + languages and libraries providing OOP-like syntactic sugar. +- Added a new ``delegator pragma`` for handling calls to missing procs and + fields at compile-time. +- The overload resolution now supports ``static[T]`` params that must be + evaluatable at compile-time. - Support for user-defined type classes have been added. From a27eb51535f9ff233b67e5bac80cc51b81c343c7 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 27 Dec 2013 18:34:28 +0200 Subject: [PATCH 09/15] towards support for composite type classes such as seq[Number] and SquareMatrix[T] --- compiler/ast.nim | 3 ++- compiler/procfind.nim | 2 +- compiler/seminst.nim | 6 +++-- compiler/semtypes.nim | 13 ++++++----- compiler/semtypinst.nim | 21 ++++++++++------- compiler/sigmatch.nim | 16 +++++++++++++ compiler/types.nim | 22 ++++++++++-------- tests/compile/tcompositetypeclasses.nim | 30 +++++++++++++++++++++++++ 8 files changed, 87 insertions(+), 26 deletions(-) create mode 100644 tests/compile/tcompositetypeclasses.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 130f5a5adc..45784bbcbf 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -340,6 +340,7 @@ type tyParametricTypeClass # structured similarly to tyGenericInst # lastSon is the body of the type class tyBuiltInTypeClass + tyCompositeTypeClass tyAnd tyOr tyNot @@ -350,7 +351,7 @@ const tyPureObject* = tyTuple GcTypeKinds* = {tyRef, tySequence, tyString} tyError* = tyProxy # as an errornous node should match everything - tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass, + tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass, tyCompositeTypeClass, tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything} type diff --git a/compiler/procfind.nim b/compiler/procfind.nim index aefccd140f..aef1c4edc9 100644 --- a/compiler/procfind.nim +++ b/compiler/procfind.nim @@ -25,7 +25,7 @@ proc equalGenericParams(procA, procB: PNode): bool = let a = procA.sons[i].sym let b = procB.sons[i].sym if a.name.id != b.name.id or - not sameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}): return + not sameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}): return if a.ast != nil and b.ast != nil: if not ExprStructuralEquivalent(a.ast, b.ast): return result = true diff --git a/compiler/seminst.nim b/compiler/seminst.nim index a76c673dad..250e53ed6f 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -47,7 +47,7 @@ proc sameInstantiation(a, b: TInstantiation): bool = if a.concreteTypes.len == b.concreteTypes.len: for i in 0..a.concreteTypes.high: if not compareTypes(a.concreteTypes[i], b.concreteTypes[i], - flags = {TypeDescExactMatch}): return + flags = {ExactTypeDescValues}): return result = true proc GenericCacheGet(genericSym: Psym, entry: TInstantiation): PSym = @@ -165,7 +165,8 @@ proc lateInstantiateGeneric(c: PContext, invocation: PType, info: TLineInfo): PT result.sons.add instantiated cacheTypeInst result -proc instGenericContainer(c: PContext, info: TLineInfo, header: PType): PType = +proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, + allowMetaTypes = false): PType = when oUseLateInstantiation: lateInstantiateGeneric(c, header, info) else: @@ -174,6 +175,7 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType): PType = InitIdTable(cl.typeMap) cl.info = info cl.c = c + cl.allowMetaTypes = allowMetaTypes result = ReplaceTypeVarsT(cl, header) proc instGenericContainer(c: PContext, n: PNode, header: PType): PType = diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 2016220162..d3a934c215 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -666,11 +666,14 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramType.sons[i] = lifted result = paramType of tyGenericBody: - # type Foo[T] = object - # proc x(a: Foo, b: Foo) - var typ = newTypeS(tyTypeClass, c) - typ.addSonSkipIntLit(paramType) - result = addImplicitGeneric(typ) + result = newTypeS(tyGenericInvokation, c) + result.rawAddSon(paramType) + for i in 0 .. paramType.sonsLen - 2: + result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true)) + result = instGenericContainer(c, paramType.sym.info, result, + allowMetaTypes = true) + result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) + result = addImplicitGeneric(result) of tyGenericInst: for i in 1 .. (paramType.sons.len - 2): var lifted = liftingWalk(paramType.sons[i]) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 69d91766b9..f7750171d6 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -50,9 +50,10 @@ proc searchInstTypes*(key: PType): PType = block MatchType: for j in 1 .. high(key.sons): # XXX sameType is not really correct for nested generics? - if not sameType(inst.sons[j], key.sons[j]): + if not compareTypes(inst.sons[j], key.sons[j], + flags = {ExactGenericParams}): break MatchType - + return inst proc cacheTypeInst*(inst: PType) = @@ -67,6 +68,8 @@ type typeMap*: TIdTable # map PType to PType symMap*: TIdTable # map PSym to PSym info*: TLineInfo + allowMetaTypes*: bool # allow types such as seq[Number] + # i.e. the result contains unresolved generics proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym @@ -132,9 +135,10 @@ proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType = result = PType(idTableGet(cl.typeMap, t)) if result == nil: + if cl.allowMetaTypes: return LocalError(t.sym.info, errCannotInstantiateX, typeToString(t)) result = errorType(cl.c) - elif result.kind == tyGenericParam: + elif result.kind == tyGenericParam and not cl.allowMetaTypes: InternalError(cl.info, "substitution with generic parameter") proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = @@ -150,11 +154,11 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = var x = t.sons[i] if x.kind == tyGenericParam: x = lookupTypeVar(cl, x) - if header == nil: header = copyType(t, t.owner, false) - header.sons[i] = x - propagateToOwner(header, x) - #idTablePut(cl.typeMap, body.sons[i-1], x) - + if x != nil: + if header == nil: header = copyType(t, t.owner, false) + header.sons[i] = x + propagateToOwner(header, x) + if header != nil: # search again after first pass: result = searchInstTypes(header) @@ -202,6 +206,7 @@ proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = of tyTypeClass: nil of tyGenericParam: result = lookupTypeVar(cl, t) + if result == nil: return t if result.kind == tyGenericInvokation: result = handleGenericInvokation(cl, result) of tyGenericInvokation: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 6a68882237..18020b95c4 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -647,6 +647,8 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation = result = typeRel(c, lastSon(f), a) of tyGenericBody: + if a.kind == tyGenericInst and a.sons[0] == f: + return isGeneric let ff = lastSon(f) if ff != nil: result = typeRel(c, ff, a) @@ -718,6 +720,17 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation = else: result = typeRel(c, prev, a) + of tyCompositeTypeClass: + var prev = PType(idTableGet(c.bindings, f)) + if prev == nil: + if typeRel(c, f.sons[1], a) != isNone: + put(c.bindings, f, a) + return isGeneric + else: + return isNone + else: + result = typeRel(c, prev, a) + of tyGenericParam, tyTypeClass: var x = PType(idTableGet(c.bindings, f)) if x == nil: @@ -780,10 +793,13 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation = let toMatch = if tfUnresolved in f.flags: a else: a.sons[0] result = typeRel(c, prev.sons[0], toMatch) + of tyExpr, tyStmt: result = isGeneric + of tyProxy: result = isEqual + else: internalError("typeRel: " & $f.kind) proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = diff --git a/compiler/types.nim b/compiler/types.nim index f6e426f396..1b25a396c3 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -409,7 +409,7 @@ const "uint", "uint8", "uint16", "uint32", "uint64", "bignum", "const ", "!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass", - "ParametricTypeClass", "BuiltInTypeClass", + "ParametricTypeClass", "BuiltInTypeClass", "CompositeTypeClass", "and", "or", "not", "any", "static"] proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = @@ -604,8 +604,9 @@ type dcEqOrDistinctOf ## a equals b or a is distinct of b TTypeCmpFlag* = enum - IgnoreTupleFields, - TypeDescExactMatch, + IgnoreTupleFields + ExactTypeDescValues + ExactGenericParams AllowCommonBase TTypeCmpFlags* = set[TTypeCmpFlag] @@ -646,7 +647,7 @@ proc SameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool = result = SameTypeAux(a, b, c) proc equalParam(a, b: PSym): TParamsEquality = - if SameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}) and + if SameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}) and ExprStructuralEquivalent(a.constraint, b.constraint): if a.ast == b.ast: result = paramsEqual @@ -682,7 +683,7 @@ proc equalParams(a, b: PNode): TParamsEquality = return paramsNotEqual # paramsIncompatible; # continue traversal! If not equal, we can return immediately; else # it stays incompatible - if not SameTypeOrNil(a.sons[0].typ, b.sons[0].typ, {TypeDescExactMatch}): + if not SameTypeOrNil(a.sons[0].typ, b.sons[0].typ, {ExactTypeDescValues}): if (a.sons[0].typ == nil) or (b.sons[0].typ == nil): result = paramsNotEqual # one proc has a result, the other not is OK else: @@ -749,9 +750,9 @@ template IfFastObjectTypeCheckFailed(a, b: PType, body: stmt) {.immediate.} = proc sameObjectTypes*(a, b: PType): bool = # specialized for efficiency (sigmatch uses it) - IfFastObjectTypeCheckFailed(a, b): + IfFastObjectTypeCheckFailed(a, b): var c = initSameTypeClosure() - result = sameTypeAux(a, b, c) + result = sameTypeAux(a, b, c) proc sameDistinctTypes*(a, b: PType): bool {.inline.} = result = sameObjectTypes(a, b) @@ -852,12 +853,15 @@ proc SameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = result = sameTypeAux(lastSon(a), lastSon(b), c) of tyTypeDesc: if c.cmp == dcEqIgnoreDistinct: result = false - elif TypeDescExactMatch in c.flags: + elif ExactTypeDescValues in c.flags: CycleCheck() result = sameChildrenAux(x, y, c) and sameFlags(a, b) else: result = sameFlags(a, b) - of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence, + of tyGenericParam: + result = if ExactGenericParams in c.flags: a.id == b.id + else: sameChildrenAux(a, b, c) and sameFlags(a, b) + of tyGenericInvokation, tyGenericBody, tySequence, tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter, tyOrdinal, tyTypeClasses: diff --git a/tests/compile/tcompositetypeclasses.nim b/tests/compile/tcompositetypeclasses.nim new file mode 100644 index 0000000000..ea966f1a9d --- /dev/null +++ b/tests/compile/tcompositetypeclasses.nim @@ -0,0 +1,30 @@ +template accept(e) = + static: assert(compiles(e)) + +template reject(e) = + static: assert(not compiles(e)) + +type + TFoo[T, U] = tuple + x: T + y: U + + TBar[K] = TFoo[K, K] + + TUserClass = int|string + + # TBaz = TBar[TUserClass] + +var + vfoo: TFoo[int, string] + vbar: TFoo[string, string] + +proc foo(x: TFoo) = echo "foo" +proc bar(x: TBar) = echo "bar" +# proc baz(x: TBaz) = echo "baz" + +accept(foo(vfoo)) +accept(bar(vbar)) +# baz vbar +reject(bar(vfoo)) + From a59f13b00dff570865201e860ea24d202b60c85a Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 28 Dec 2013 12:50:45 +0200 Subject: [PATCH 10/15] lift generic parameters from concrete composite type classes --- compiler/seminst.nim | 5 ++++- compiler/semtypes.nim | 17 ++++++++++------- compiler/semtypinst.nim | 9 +++++---- compiler/types.nim | 17 ++++++++++------- tests/compile/tcompositetypeclasses.nim | 17 +++++++++++------ 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 250e53ed6f..ba26635a1d 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -204,6 +204,8 @@ proc fixupProcType(c: PContext, genericType: PType, result = result.sons[0] of tyStatic: result = inst.concreteTypes[genericType.sym.position] + of tyGenericInst: + result = fixupProcType(c, result.lastSon, inst) of tyOpenArray, tyArray, tySet, tySequence, tyTuple, tyProc, tyPtr, tyVar, tyRef, tyOrdinal, tyRange, tyVarargs: if genericType.sons == nil: return @@ -234,7 +236,8 @@ proc fixupProcType(c: PContext, genericType: PType, continue result.sons[head] = changed - + result.size = 0 + if result.n != nil: if result.n.kind == nkRecList: for son in result.n.sons: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index d3a934c215..6f6d0c4c58 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -620,7 +620,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, s.position = genericParams.len genericParams.addSon(newSymNode(s)) result = typeClass - + # XXX: There are codegen errors if this is turned into a nested proc template liftingWalk(typ: PType, anonFlag = false): expr = liftParamType(c, procKind, genericParams, typ, paramName, info, anonFlag) @@ -665,6 +665,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if lifted != nil: paramType.sons[i] = lifted result = paramType + of tyGenericBody: result = newTypeS(tyGenericInvokation, c) result.rawAddSon(paramType) @@ -674,6 +675,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, allowMetaTypes = true) result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) result = addImplicitGeneric(result) + of tyGenericInst: for i in 1 .. (paramType.sons.len - 2): var lifted = liftingWalk(paramType.sons[i]) @@ -681,21 +683,22 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramType.sons[i] = lifted result = paramType - if result == nil: - result = liftingWalk(paramType.lastSon) - else: - result.kind = tyGenericInvokation - result.sons.setLen(result.sons.len - 1) + let liftBody = liftingWalk(paramType.lastSon) + if liftBody != nil: result = liftBody + of tyTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot: - result = addImplicitGeneric(copyType(paramType, getCurrOwner(), false)) + result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true)) + of tyExpr: result = addImplicitGeneric(newTypeS(tyGenericParam, c)) + of tyGenericParam: if tfGenericTypeParam in paramType.flags and false: if paramType.sonsLen > 0: result = liftingWalk(paramType.lastSon) else: result = addImplicitGeneric(newTypeS(tyGenericParam, c)) + else: nil # result = liftingWalk(paramType) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index f7750171d6..384ce3498c 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -17,14 +17,14 @@ proc checkPartialConstructedType(info: TLineInfo, t: PType) = elif t.kind == tyVar and t.sons[0].kind == tyVar: LocalError(info, errVarVarTypeNotAllowed) -proc checkConstructedType*(info: TLineInfo, typ: PType) = +proc checkConstructedType*(info: TLineInfo, typ: PType) = var t = typ.skipTypes({tyDistinct}) if t.kind in tyTypeClasses: nil elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject: LocalError(info, errInvalidPragmaX, "acyclic") elif t.kind == tyVar and t.sons[0].kind == tyVar: LocalError(info, errVarVarTypeNotAllowed) - elif computeSize(t) < 0: + elif computeSize(t) == szIllegalRecursion: LocalError(info, errIllegalRecursionInTypeX, typeToString(t)) when false: if t.kind == tyObject and t.sons[0] != nil: @@ -140,7 +140,7 @@ proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType = result = errorType(cl.c) elif result.kind == tyGenericParam and not cl.allowMetaTypes: InternalError(cl.info, "substitution with generic parameter") - + proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = # tyGenericInvokation[A, tyGenericInvokation[A, B]] # is difficult to handle: @@ -170,7 +170,8 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = # recursive instantions: result = newType(tyGenericInst, t.sons[0].owner) result.rawAddSon(header.sons[0]) - cacheTypeInst(result) + if not cl.allowMetaTypes: + cacheTypeInst(result) for i in countup(1, sonsLen(t) - 1): var x = replaceTypeVarsT(cl, t.sons[i]) diff --git a/compiler/types.nim b/compiler/types.nim index 1b25a396c3..d47015836d 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1098,18 +1098,22 @@ proc computeRecSizeAux(n: PNode, a, currOffset: var biggestInt): biggestInt = a = 1 result = - 1 -proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt = +const + szIllegalRecursion* = -2 + szUnknownSize* = -1 + +proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt = var res, maxAlign, length, currOffset: biggestInt - if typ.size == - 2: + if typ.size == szIllegalRecursion: # we are already computing the size of the type # --> illegal recursion in type - return - 2 - if typ.size >= 0: + return szIllegalRecursion + if typ.size >= 0: # size already computed result = typ.size a = typ.align return - typ.size = - 2 # mark as being computed + typ.size = szIllegalRecursion # mark as being computed case typ.kind of tyInt, tyUInt: result = IntSize @@ -1196,7 +1200,7 @@ proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt = of tyProxy: result = 1 else: #internalError("computeSizeAux()") - result = - 1 + result = szUnknownSize typ.size = result typ.align = int(a) @@ -1213,7 +1217,6 @@ proc getSize(typ: PType): biggestInt = result = computeSize(typ) if result < 0: InternalError("getSize: " & $typ.kind) - proc containsGenericTypeIter(t: PType, closure: PObject): bool = result = t.kind in GenericTypes diff --git a/tests/compile/tcompositetypeclasses.nim b/tests/compile/tcompositetypeclasses.nim index ea966f1a9d..4ba92fed1a 100644 --- a/tests/compile/tcompositetypeclasses.nim +++ b/tests/compile/tcompositetypeclasses.nim @@ -13,18 +13,23 @@ type TUserClass = int|string - # TBaz = TBar[TUserClass] + TBaz = TBar[TUserClass] var vfoo: TFoo[int, string] vbar: TFoo[string, string] + vbaz: TFoo[int, int] + vnotbaz: TFoo[TObject, TObject] proc foo(x: TFoo) = echo "foo" proc bar(x: TBar) = echo "bar" -# proc baz(x: TBaz) = echo "baz" +proc baz(x: TBaz) = echo "baz" -accept(foo(vfoo)) -accept(bar(vbar)) -# baz vbar -reject(bar(vfoo)) +accept foo(vfoo) +accept bar(vbar) +accept baz(vbar) +accept baz(vbaz) + +reject baz(vnotbaz) +reject bar(vfoo) From f34ca1a7d7fe22c762b1a4bc29f4f1e19d5da0ec Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 28 Dec 2013 13:26:41 +0200 Subject: [PATCH 11/15] fix illegal recursion checks --- compiler/types.nim | 11 +++++++---- tests/reject/tillrec.nim | 19 +++++++++---------- tests/reject/typredef.nim | 6 ++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/compiler/types.nim b/compiler/types.nim index d47015836d..a2869f0da5 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1144,8 +1144,10 @@ proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt = tyBigNum: result = ptrSize a = result - of tyArray, tyArrayConstr: - result = lengthOrd(typ.sons[0]) * computeSizeAux(typ.sons[1], a) + of tyArray, tyArrayConstr: + let elemSize = computeSizeAux(typ.sons[1], a) + if elemSize < 0: return elemSize + result = lengthOrd(typ.sons[0]) * elemSize of tyEnum: if firstOrd(typ) < 0: result = 4 # use signed int32 @@ -1196,8 +1198,9 @@ proc computeSizeAux(typ: PType, a: var biggestInt): biggestInt = of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter: result = computeSizeAux(lastSon(typ), a) of tyTypeDesc: - result = (if typ.len == 1: computeSizeAux(typ.sons[0], a) else: -1) - of tyProxy: result = 1 + result = if typ.len == 1: computeSizeAux(typ.sons[0], a) + else: szUnknownSize + of tyForward: return szIllegalRecursion else: #internalError("computeSizeAux()") result = szUnknownSize diff --git a/tests/reject/tillrec.nim b/tests/reject/tillrec.nim index 3f8fe60fca..1d1ec06229 100644 --- a/tests/reject/tillrec.nim +++ b/tests/reject/tillrec.nim @@ -3,15 +3,14 @@ discard """ line: 13 errormsg: "illegal recursion in type \'TIllegal\'" """ -# test illegal recursive types - -type - TLegal {.final.} = object - x: int - kids: seq[TLegal] - - TIllegal {.final.} = object #ERROR_MSG illegal recursion in type 'TIllegal' - y: Int - x: array[0..3, TIllegal] +# test illegal recursive types +type + TLegal {.final.} = object + x: int + kids: seq[TLegal] + + TIllegal {.final.} = object #ERROR_MSG illegal recursion in type 'TIllegal' + y: Int + x: array[0..3, TIllegal] diff --git a/tests/reject/typredef.nim b/tests/reject/typredef.nim index b2182d116d..0b6aed8750 100644 --- a/tests/reject/typredef.nim +++ b/tests/reject/typredef.nim @@ -3,8 +3,6 @@ discard """ line: 7 errormsg: "illegal recursion in type \'Uint8\'" """ -type - Uint8 = Uint8 #ERROR_MSG illegal recursion in type 'Uint8' - - +type + Uint8 = Uint8 #ERROR_MSG illegal recursion in type 'Uint8' From 5d75ce2f2eec8d4ec6f152105a144abcf73e4a37 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 28 Dec 2013 15:01:40 +0200 Subject: [PATCH 12/15] fix tclosure4 --- compiler/semtypes.nim | 1 + compiler/sigmatch.nim | 17 +++++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 6f6d0c4c58..1251a25c06 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -673,6 +673,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true)) result = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) + result.lastSon.flags.incl tfHasMeta result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) result = addImplicitGeneric(result) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 18020b95c4..43ca6e6669 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -303,21 +303,23 @@ proc minRel(a, b: TTypeRelation): TTypeRelation = if a <= b: result = a else: result = b -proc tupleRel(c: var TCandidate, f, a: PType): TTypeRelation = +proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation = result = isNone if sameType(f, a): result = isEqual elif sonsLen(a) == sonsLen(f): result = isEqual - for i in countup(0, sonsLen(f) - 1): + let firstField = if f.kind == tyTuple: 0 + else: 1 + for i in countup(firstField, sonsLen(f) - 1): var m = typeRel(c, f.sons[i], a.sons[i]) if m < isSubtype: return isNone result = minRel(result, m) if f.n != nil and a.n != nil: for i in countup(0, sonsLen(f.n) - 1): # check field names: - if f.n.sons[i].kind != nkSym: InternalError(f.n.info, "tupleRel") - elif a.n.sons[i].kind != nkSym: InternalError(a.n.info, "tupleRel") + if f.n.sons[i].kind != nkSym: InternalError(f.n.info, "recordRel") + elif a.n.sons[i].kind != nkSym: InternalError(a.n.info, "recordRel") else: var x = f.n.sons[i].sym var y = a.n.sons[i].sym @@ -408,11 +410,13 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation = result = isNone assert(f != nil) assert(a != nil) + if a.kind == tyGenericInst and skipTypes(f, {tyVar}).kind notin { tyGenericBody, tyGenericInvokation, - tyGenericParam, tyTypeClass}: + tyGenericParam} + tyTypeClasses: return typeRel(c, f, lastSon(a)) + if a.kind == tyVar and f.kind != tyVar: return typeRel(c, f, a.sons[0]) @@ -559,10 +563,11 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation = of tyNil: if a.kind == f.kind: result = isEqual of tyTuple: - if a.kind == tyTuple: result = tupleRel(c, f, a) + if a.kind == tyTuple: result = recordRel(c, f, a) of tyObject: if a.kind == tyObject: if sameObjectTypes(f, a): result = isEqual + elif tfHasMeta in f.flags: result = recordRel(c, f, a) else: var depth = isObjectSubtype(a, f) if depth > 0: From eb1d23c0c745c64225e8db22f62d8ebf596f4448 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 28 Dec 2013 23:53:48 +0200 Subject: [PATCH 13/15] fixes #787 --- lib/system.nim | 18 ++++++------- tests/run/tfailedassert.nim | 51 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 tests/run/tfailedassert.nim diff --git a/lib/system.nim b/lib/system.nim index d45137b9eb..e9719c7e1e 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2545,7 +2545,7 @@ proc raiseAssert*(msg: string) {.noinline.} = sysFatal(EAssertionFailed, msg) when true: - proc hiddenRaiseAssert(msg: string) {.raises: [], tags: [].} = + proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} = # trick the compiler to not list ``EAssertionFailed`` when called # by ``assert``. type THide = proc (msg: string) {.noinline, raises: [], noSideEffect, @@ -2558,11 +2558,11 @@ template assert*(cond: bool, msg = "") = ## raises an ``EAssertionFailure`` exception. However, the compiler may ## not generate any code at all for ``assert`` if it is advised to do so. ## Use ``assert`` for debugging purposes only. - bind instantiationInfo, hiddenRaiseAssert + bind instantiationInfo + mixin failedAssertImpl when compileOption("assertions"): {.line.}: - if not cond: - hiddenRaiseAssert(astToStr(cond) & ' ' & msg) + if not cond: failedAssertImpl(astToStr(cond) & ' ' & msg) template doAssert*(cond: bool, msg = "") = ## same as `assert` but is always turned on and not affected by the @@ -2575,9 +2575,9 @@ template doAssert*(cond: bool, msg = "") = when not defined(nimhygiene): {.pragma: inject.} -template onFailedAssert*(msg: expr, code: stmt): stmt = - ## Sets an assertion failure handler that will intercept any assert statements - ## following `onFailedAssert` in the current lexical scope. +template onFailedAssert*(msg: expr, code: stmt): stmt {.dirty, immediate.} = + ## Sets an assertion failure handler that will intercept any assert + ## statements following `onFailedAssert` in the current lexical scope. ## Can be defined multiple times in a single function. ## ## .. code-block:: nimrod @@ -2594,8 +2594,8 @@ template onFailedAssert*(msg: expr, code: stmt): stmt = ## ## assert(...) ## - template raiseAssert(msgIMPL: string): stmt = - let msg {.inject.} = msgIMPL + template failedAssertImpl(msgIMPL: string): stmt {.dirty, immediate.} = + let msg = msgIMPL code proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} = diff --git a/tests/run/tfailedassert.nim b/tests/run/tfailedassert.nim new file mode 100644 index 0000000000..0e536cffd4 --- /dev/null +++ b/tests/run/tfailedassert.nim @@ -0,0 +1,51 @@ +discard """ + output: ''' +WARNING: false first asseertion from bar +ERROR: false second assertion from bar +-1 +tests/run/tfailedassert.nim:40 false assertion from foo +''' +""" + +type + TLineInfo = tuple[filename: string, line: int] + + TMyError = object of E_Base + lineinfo: TLineInfo + + EMyError = ref TMyError + +# module-wide policy to change the failed assert +# exception type in order to include a lineinfo +onFailedAssert(msg): + var e = new(TMyError) + e.msg = msg + e.lineinfo = instantiationInfo(-2) + raise e + +proc foo = + assert(false, "assertion from foo") + +proc bar: int = + # local overrides that are active only + # in this proc + onFailedAssert(msg): echo "WARNING: " & msg + + assert(false, "first asseertion from bar") + + onFailedAssert(msg): + echo "ERROR: " & msg + return -1 + + assert(false, "second assertion from bar") + return 10 + +echo("") +echo(bar()) + +try: + foo() +except: + let e = EMyError(getCurrentException()) + echo e.lineinfo.filename, ":", e.lineinfo.line, " ", e.msg + From 66a255652572b48440b68878e99d7f5290e384b3 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 29 Dec 2013 00:00:37 +0200 Subject: [PATCH 14/15] make more tests green --- compiler/evals.nim | 10 ++- compiler/sem.nim | 17 +++-- compiler/semexprs.nim | 8 ++- compiler/semfold.nim | 5 +- compiler/seminst.nim | 19 ++++- compiler/semmagic.nim | 7 +- compiler/semstmts.nim | 8 ++- compiler/semtypes.nim | 33 +++++---- compiler/semtypinst.nim | 7 +- compiler/sigmatch.nim | 50 ++++++------- tests/compile/tloops.nim | 128 +++++++++++++++++----------------- tests/run/tmemoization.nim | 8 +-- tests/run/tstaticparams.nim | 12 ++-- tests/run/ttypetraits.nim | 2 +- tests/run/tusingstatement.nim | 24 ++----- 15 files changed, 176 insertions(+), 162 deletions(-) diff --git a/compiler/evals.nim b/compiler/evals.nim index f7d94e4c0f..151adf6909 100644 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -906,17 +906,15 @@ proc evalParseStmt(c: PEvalContext, n: PNode): PNode = result = parseString(code.getStrValue, code.info.toFilename, code.info.line.int) #result.typ = newType(tyStmt, c.module) - -proc evalTypeTrait*(trait, operand: PNode, context: PSym): PNode = - InternalAssert operand.kind == nkSym - let typ = operand.sym.typ.skipTypes({tyTypeDesc}) +proc evalTypeTrait*(trait, operand: PNode, context: PSym): PNode = + let typ = operand.typ.skipTypes({tyTypeDesc}) case trait.sym.name.s.normalize of "name": result = newStrNode(nkStrLit, typ.typeToString(preferName)) result.typ = newType(tyString, context) result.info = trait.info - of "arity": + of "arity": result = newIntNode(nkIntLit, typ.n.len-1) result.typ = newType(tyInt, context) result.info = trait.info @@ -1330,7 +1328,7 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = if gNestedEvals <= 0: stackTrace(c, n.info, errTooManyIterations) case n.kind of nkSym: result = evalSym(c, n, flags) - of nkType..nkNilLit: + of nkType..nkNilLit, nkTypeOfExpr: # nkStrLit is VERY common in the traces, so we should avoid # the 'copyNode' here. result = n #.copyNode diff --git a/compiler/sem.nim b/compiler/sem.nim index 67d400ac50..4b4a3da50c 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -158,16 +158,15 @@ proc IsOpImpl(c: PContext, n: PNode): PNode proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, semCheck: bool = true): PNode -when false: - proc symFromType(t: PType, info: TLineInfo): PSym = - if t.sym != nil: return t.sym - result = newSym(skType, getIdent"AnonType", t.owner, info) - result.flags.incl sfAnon - result.typ = t +proc symFromType(t: PType, info: TLineInfo): PSym = + if t.sym != nil: return t.sym + result = newSym(skType, getIdent"AnonType", t.owner, info) + result.flags.incl sfAnon + result.typ = t - proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode = - result = newSymNode(symFromType(t, info), info) - result.typ = makeTypeDesc(c, t) +proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode = + result = newSymNode(symFromType(t, info), info) + result.typ = makeTypeDesc(c, t) proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext = result = newEvalContext(c.module, mode) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index fde09400d9..6294fb3c90 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -301,10 +301,10 @@ proc semOf(c: PContext, n: PNode): PNode = proc isOpImpl(c: PContext, n: PNode): PNode = InternalAssert n.sonsLen == 3 and - n[1].kind == nkSym and n[1].sym.kind == skType and + n[1].typ != nil and n[1].typ.kind == tyTypeDesc and n[2].kind in {nkStrLit..nkTripleStrLit, nkType} - let t1 = n[1].sym.typ.skipTypes({tyTypeDesc}) + let t1 = n[1].typ.skipTypes({tyTypeDesc}) if n[2].kind in {nkStrLit..nkTripleStrLit}: case n[2].strVal.normalize @@ -1942,7 +1942,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # type parameters: partial generic specialization n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) result = explicitGenericInstantiation(c, n, s) - else: + elif s != nil and s.kind in {skType}: + result = symNodeFromType(c, semTypeNode(c, n, nil), n.info) + else: result = semArrayAccess(c, n, flags) of nkCurlyExpr: result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index ddbe3053c7..5e5403c280 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -588,8 +588,9 @@ proc getConstExpr(m: PSym, n: PNode): PNode = result = newSymNodeTypeDesc(s, n.info) of skGenericParam: if s.typ.kind == tyStatic: - result = s.typ.n - result.typ = s.typ.sons[0] + if s.typ.n != nil: + result = s.typ.n + result.typ = s.typ.sons[0] else: result = newSymNodeTypeDesc(s, n.info) else: nil diff --git a/compiler/seminst.nim b/compiler/seminst.nim index ba26635a1d..969ff2d590 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -198,14 +198,31 @@ proc fixupProcType(c: PContext, genericType: PType, case genericType.kind of tyGenericParam, tyTypeClasses: result = inst.concreteTypes[genericType.sym.position] + of tyTypeDesc: result = inst.concreteTypes[genericType.sym.position] if tfUnresolved in genericType.flags: result = result.sons[0] + of tyStatic: result = inst.concreteTypes[genericType.sym.position] + of tyGenericInst: result = fixupProcType(c, result.lastSon, inst) + + of tyObject: + var recList = genericType.n + for i in 0 .. 1000: InternalError(fn.ast.info, "nesting too deep") inc(c.InstCounter) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 4c667e27e9..b97cde63a9 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -34,10 +34,9 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode = proc semTypeTraits(c: PContext, n: PNode): PNode = checkMinSonsLen(n, 2) - internalAssert n.sons[1].kind == nkSym - let typArg = n.sons[1].sym - if typArg.kind == skType or - (typArg.kind == skParam and typArg.typ.sonsLen > 0): + internalAssert n.sons[1].typ.kind == tyTypeDesc + let typArg = n.sons[1].typ + if typArg.sonsLen > 0: # This is either a type known to sem or a typedesc # param to a regular proc (again, known at instantiation) result = evalTypeTrait(n[0], n[1], GetCurrOwner()) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index a1805fdec0..33e942844e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1039,8 +1039,12 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # for DLL generation it is annoying to check for sfImportc! if sfBorrow in s.flags: LocalError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s) - if n.sons[genericParamsPos].kind == nkEmpty: - ParamsTypeCheck(c, s.typ) + let usePseudoGenerics = kind in {skMacro, skTemplate} + # Macros and Templates can have generic parameters, but they are + # only used for overload resolution (there is no instantiation of + # the symbol, so we must process the body now) + if n.sons[genericParamsPos].kind == nkEmpty or usePseudoGenerics: + if not usePseudoGenerics: ParamsTypeCheck(c, s.typ) pushProcCon(c, s) maybeAddResult(c, s, n) if sfImportc notin s.flags: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 1251a25c06..29fad00599 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -594,12 +594,7 @@ let typedescId = getIdent"typedesc" proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramType: PType, paramName: string, info: TLineInfo, anon = false): PType = - if procKind in {skMacro, skTemplate}: - # generic param types in macros and templates affect overload - # resolution, but don't work as generic params when it comes - # to proc instantiation. We don't need to lift such params here. - return - + proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType = let finalTypId = if typId != nil: typId else: getIdent(paramName & ":type") @@ -620,7 +615,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, s.position = genericParams.len genericParams.addSon(newSymNode(s)) result = typeClass - + # XXX: There are codegen errors if this is turned into a nested proc template liftingWalk(typ: PType, anonFlag = false): expr = liftParamType(c, procKind, genericParams, typ, paramName, info, anonFlag) @@ -635,20 +630,25 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, case paramType.kind: of tyAnything: result = addImplicitGeneric(newTypeS(tyGenericParam, c)) + of tyStatic: # proc(a: expr{string}, b: expr{nkLambda}) # overload on compile time values and AST trees result = addImplicitGeneric(c.newTypeWithSons(tyStatic, paramType.sons)) + result.flags.incl tfHasStatic + of tyTypeDesc: if tfUnresolved notin paramType.flags: # naked typedescs are not bindOnce types if paramType.sonsLen == 0 and paramTypId != nil and paramTypId.id == typedescId.id: paramTypId = nil result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons)) + of tyDistinct: if paramType.sonsLen == 1: # disable the bindOnce behavior for the type class result = liftingWalk(paramType.sons[0], true) + of tySequence, tySet, tyArray, tyOpenArray: # XXX: this is a bit strange, but proc(s: seq) # produces tySequence(tyGenericParam, null). @@ -657,7 +657,8 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, # Maybe there is another better place to associate # the seq type class with the seq identifier. if paramType.lastSon == nil: - let typ = c.newTypeWithSons(tyTypeClass, @[newTypeS(paramType.kind, c)]) + let typ = c.newTypeWithSons(tyBuiltInTypeClass, + @[newTypeS(paramType.kind, c)]) result = addImplicitGeneric(typ) else: for i in 0 .. 0: argType.skipTypes({tyTypeDesc}) diff --git a/tests/compile/tloops.nim b/tests/compile/tloops.nim index 2b1765b007..f6f9397698 100644 --- a/tests/compile/tloops.nim +++ b/tests/compile/tloops.nim @@ -1,67 +1,67 @@ -# Test nested loops and some other things - -proc andTest() = - var a = 0 == 5 and 6 == 6 - -proc incx(x: var int) = # is built-in proc - x = x + 1 - -proc decx(x: var int) = - x = x - 1 - -proc First(y: var int) = - var x: int - i_ncx(x) - if x == 10: - y = 0 - else: - if x == 0: - incx(x) - else: - x=11 - -proc TestLoops() = - var i, j: int - while i >= 0: - if i mod 3 == 0: - break - i = i + 1 - while j == 13: - j = 13 - break - break - - while True: - break - - -proc Foo(n: int): int = - var - a, old: int - b, c: bool - F_irst(a) - if a == 10: - a = 30 - elif a == 11: - a = 22 - elif a == 12: - a = 23 - elif b: - old = 12 - else: - a = 40 - - # - b = false or 2 == 0 and 3 == 9 - a = 0 + 3 * 5 + 6 + 7 + +8 # 36 - while b: - a = a + 3 - a = a + 5 - write(stdout, "Hello!") - - -# We should come till here :-) -discard Foo(345) +# Test nested loops and some other things + +proc andTest() = + var a = 0 == 5 and 6 == 6 + +proc incx(x: var int) = # is built-in proc + x = x + 1 + +proc decx(x: var int) = + x = x - 1 + +proc First(y: var int) = + var x: int + i_ncx(x) + if x == 10: + y = 0 + else: + if x == 0: + incx(x) + else: + x=11 + +proc TestLoops() = + var i, j: int + while i >= 0: + if i mod 3 == 0: + break + i = i + 1 + while j == 13: + j = 13 + break + break + + while True: + break + + +proc Foo(n: int): int = + var + a, old: int + b, c: bool + F_irst(a) + if a == 10: + a = 30 + elif a == 11: + a = 22 + elif a == 12: + a = 23 + elif b: + old = 12 + else: + a = 40 + + # + b = false or 2 == 0 and 3 == 9 + a = 0 + 3 * 5 + 6 + 7 + +8 # 36 + while b: + a = a + 3 + a = a + 5 + write(stdout, "Hello!") + + +# We should come till here :-) +discard Foo(345) # test the new type symbol lookup feature: diff --git a/tests/run/tmemoization.nim b/tests/run/tmemoization.nim index b59ff44ea2..180acd89b6 100644 --- a/tests/run/tmemoization.nim +++ b/tests/run/tmemoization.nim @@ -1,6 +1,6 @@ discard """ - msg: "test 1\ntest 2" - output: "TEST 1\nTEST 2\nTEST 2" + msg: "test 1\ntest 2\ntest 3" + output: "TEST 1\nTEST 2\nTEST 3" """ import strutils @@ -10,8 +10,8 @@ proc foo(s: static[string]): string = const R = s.toUpper return R - + echo foo("test 1") echo foo("test 2") -echo foo("test " & $2) +echo foo("test " & $3) diff --git a/tests/run/tstaticparams.nim b/tests/run/tstaticparams.nim index 23d644bce3..b1377443bb 100644 --- a/tests/run/tstaticparams.nim +++ b/tests/run/tstaticparams.nim @@ -10,9 +10,9 @@ type TBar[T; I: static[int]] = object data: array[I, T] - #TA1[T; I: static[int]] = array[I, T] - #TA2[T; I: static[int]] = array[0..I, T] - TA3[T; I: static[int]] = array[I-1, T] + TA1[T; I: static[int]] = array[I, T] + # TA2[T; I: static[int]] = array[0..I, T] + # TA3[T; I: static[int]] = array[I-1, T] proc takeFoo(x: TFoo) = echo "abracadabra" @@ -25,7 +25,7 @@ var y: TBar[float, 4] echo high(y.data) var - t1: TA1 - t2: TA2 - t3: TA3 + t1: TA1[float, 1] + # t2: TA2[string, 4] + # t3: TA3[int, 10] diff --git a/tests/run/ttypetraits.nim b/tests/run/ttypetraits.nim index 9a4a7d0d30..4344855eb6 100644 --- a/tests/run/ttypetraits.nim +++ b/tests/run/ttypetraits.nim @@ -1,6 +1,6 @@ discard """ msg: "int\nstring\nTBar[int]" - output: "int\nstring\nTBar[int]\nint\nrange 0..2\nstring" + output: "int\nstring\nTBar[int]\nint\nrange 0..2(int)\nstring" """ import typetraits diff --git a/tests/run/tusingstatement.nim b/tests/run/tusingstatement.nim index b9d4663775..a33aced4c5 100644 --- a/tests/run/tusingstatement.nim +++ b/tests/run/tusingstatement.nim @@ -8,25 +8,11 @@ import # This macro mimics the using statement from C# # -# XXX: -# It doen't match the C# version exactly yet. -# In particular, it's not recursive, which prevents it from dealing -# with exceptions thrown from the variable initializers when multiple. -# variables are used. +# It's kept only as a test for the macro system +# Nimrod's destructors offer a mechanism for automatic +# disposal of resources. # -# Also, since nimrod relies less on exceptions in general, a more -# idiomatic definition could be: -# var x = init() -# if opened(x): -# try: -# body -# finally: -# close(x) -# -# `opened` here could be an overloaded proc which any type can define. -# A common practice can be returing an Optional[Resource] obj for which -# `opened` is defined to `optional.hasValue` -macro using(e: expr): stmt {.immediate.} = +macro autoClose(e: expr): stmt {.immediate.} = let e = callsite() if e.len != 3: error "Using statement: unexpected number of arguments. Got " & @@ -97,7 +83,7 @@ proc close(r: var TResource) = proc use(r: var TResource) = write(stdout, "Using " & r.field & ".") -using(r = openResource("test")): +autoClose(r = openResource("test")): use r From 72291875bf895e8e0d22ab3f375752417b07ed25 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 29 Dec 2013 16:08:33 +0200 Subject: [PATCH 15/15] integrate the logic of fixupProcType into ReplaceTypeVars --- compiler/ast.nim | 20 ++++++++--- compiler/semdata.nim | 14 ++++---- compiler/seminst.nim | 3 +- compiler/semtypes.nim | 19 +++++++---- compiler/semtypinst.nim | 42 ++++++++++++++--------- compiler/sigmatch.nim | 15 ++++++--- compiler/types.nim | 5 +-- tests/compile/tbindtypedesc.nim | 60 ++++++++++++++++----------------- tests/reject/tenummix.nim | 2 +- tests/run/tfailedassert.nim | 2 +- 10 files changed, 108 insertions(+), 74 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 45784bbcbf..92f3ce8d37 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -351,9 +351,12 @@ const tyPureObject* = tyTuple GcTypeKinds* = {tyRef, tySequence, tyString} tyError* = tyProxy # as an errornous node should match everything + tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass, tyCompositeTypeClass, tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything} + tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyStatic, tyExpr} + tyTypeClasses + type TTypeKinds* = set[TTypeKind] @@ -397,7 +400,8 @@ type tfNeedsInit, # type constains a "not nil" constraint somewhere or some # other type so that it requires inititalization tfHasShared, # type constains a "shared" constraint modifier somewhere - tfHasMeta, # type has "typedesc" or "expr" somewhere; or uses '|' + tfHasMeta, # type contains "wildcard" sub-types such as generic params + # or other type classes tfHasGCedMem, # type contains GC'ed memory tfGenericTypeParam tfHasStatic @@ -777,9 +781,11 @@ const GenericTypes*: TTypeKinds = {tyGenericInvokation, tyGenericBody, tyGenericParam} + StructuralEquivTypes*: TTypeKinds = {tyArrayConstr, tyNil, tyTuple, tyArray, tySet, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, tyOpenArray, tyVarargs} + ConcreteTypes*: TTypeKinds = { # types of the expr that may occur in:: # var x = expr tyBool, tyChar, tyEnum, tyArray, tyObject, @@ -1222,7 +1228,7 @@ proc newSons(father: PNode, length: int) = proc propagateToOwner*(owner, elem: PType) = const HaveTheirOwnEmpty = {tySequence, tySet} owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta, - tfHasGCedMem}) + tfHasStatic, tfHasGCedMem}) if tfNotNil in elem.flags: if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvokation}: owner.flags.incl tfNotNil @@ -1235,10 +1241,14 @@ proc propagateToOwner*(owner, elem: PType) = if tfShared in elem.flags: owner.flags.incl tfHasShared - - if elem.kind in {tyExpr, tyStatic, tyTypeDesc}: + + if elem.kind in tyMetaTypes: owner.flags.incl tfHasMeta - elif elem.kind in {tyString, tyRef, tySequence} or + + if elem.kind == tyStatic: + owner.flags.incl tfHasStatic + + if elem.kind in {tyString, tyRef, tySequence} or elem.kind == tyProc and elem.callConv == ccClosure: owner.flags.incl tfHasGCedMem diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 874e5dab46..687140ce98 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -217,23 +217,21 @@ proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = proc makeAndType*(c: PContext, t1, t2: PType): PType = result = newTypeS(tyAnd, c) result.sons = @[t1, t2] - result.flags.incl tfHasMeta - if tfHasStatic in t1.flags or tfHasStatic in t2.flags: - result.flags.incl tfHasStatic + propagateToOwner(result, t1) + propagateToOwner(result, t2) proc makeOrType*(c: PContext, t1, t2: PType): PType = result = newTypeS(tyOr, c) result.sons = @[t1, t2] - result.flags.incl tfHasMeta - if tfHasStatic in t1.flags or tfHasStatic in t2.flags: - result.flags.incl tfHasStatic + propagateToOwner(result, t1) + propagateToOwner(result, t2) proc makeNotType*(c: PContext, t1: PType): PType = result = newTypeS(tyNot, c) result.sons = @[t1] - result.flags.incl tfHasMeta + propagateToOwner(result, t1) -proc newTypeS(kind: TTypeKind, c: PContext): PType = +proc newTypeS(kind: TTypeKind, c: PContext): PType = result = newType(kind, getCurrOwner()) proc newTypeWithSons*(c: PContext, kind: TTypeKind, diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 969ff2d590..cfa099d3fe 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -310,7 +310,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, var entry = TInstantiation.new entry.sym = result instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[]) - result.typ = fixupProcType(c, fn.typ, entry[]) + # let t1 = fixupProcType(c, fn.typ, entry[]) + result.typ = generateTypeInstance(c, pt, info, fn.typ) n.sons[genericParamsPos] = ast.emptyNode var oldPrc = GenericCacheGet(fn, entry[]) if oldPrc == nil: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 29fad00599..0562509db4 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -591,6 +591,10 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = let typedescId = getIdent"typedesc" +template shouldHaveMeta(t) = + InternalAssert tfHasMeta in result.lastSon.flags + # result.lastSon.flags.incl tfHasMeta + proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramType: PType, paramName: string, info: TLineInfo, anon = false): PType = @@ -615,7 +619,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, s.position = genericParams.len genericParams.addSon(newSymNode(s)) result = typeClass - + # XXX: There are codegen errors if this is turned into a nested proc template liftingWalk(typ: PType, anonFlag = false): expr = liftParamType(c, procKind, genericParams, typ, paramName, info, anonFlag) @@ -674,24 +678,22 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true)) result = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) - result.lastSon.flags.incl tfHasMeta + result.lastSon.shouldHaveMeta result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) result = addImplicitGeneric(result) of tyGenericInst: - # XXX: It should be possible to set tfHasMeta in semtypinst, when the - # instance was generated for i in 1 .. (paramType.sons.len - 2): var lifted = liftingWalk(paramType.sons[i]) if lifted != nil: paramType.sons[i] = lifted result = paramType - paramType.lastSon.flags.incl tfHasMeta + result.lastSon.shouldHaveMeta let liftBody = liftingWalk(paramType.lastSon) if liftBody != nil: result = liftBody - result.flags.incl tfHasMeta + result.shouldHaveMeta of tyTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot: result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true)) @@ -884,7 +886,10 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = when oUseLateInstantiation: result = lateInstantiateGeneric(c, result, n.info) else: - result = instGenericContainer(c, n, result) + result = instGenericContainer(c, n.info, result, + allowMetaTypes = not isConcrete) + if not isConcrete and result.kind == tyGenericInst: + result.lastSon.shouldHaveMeta proc semTypeExpr(c: PContext, n: PNode): PType = var n = semExprWithType(c, n, {efDetermineType}) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 1bd6e23d5e..1a4bdd9e3e 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -135,7 +135,7 @@ proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType = result = PType(idTableGet(cl.typeMap, t)) if result == nil: - if cl.allowMetaTypes: return + if cl.allowMetaTypes or tfRetType in t.flags: return LocalError(t.sym.info, errCannotInstantiateX, typeToString(t)) result = errorType(cl.c) elif result.kind == tyGenericParam and not cl.allowMetaTypes: @@ -184,7 +184,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = # if one of the params is not concrete, we cannot do anything # but we already raised an error! rawAddSon(result, header.sons[i]) - + var newbody = ReplaceTypeVarsT(cl, lastSon(body)) newbody.flags = newbody.flags + t.flags + body.flags result.flags = result.flags + newbody.flags @@ -205,20 +205,29 @@ proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = return if s != nil: s else: t case t.kind - of tyTypeClass, tyBuiltInTypeClass: nil - of tyGenericParam, tyCompositeTypeClass: - result = lookupTypeVar(cl, t) - if result == nil: return t - if result.kind == tyGenericInvokation: - result = handleGenericInvokation(cl, result) - of tyGenericInvokation: + of tyGenericParam, tyTypeClasses: + let lookup = lookupTypeVar(cl, t) + if lookup != nil: + result = lookup + if result.kind == tyGenericInvokation: + result = handleGenericInvokation(cl, result) + of tyGenericInvokation: result = handleGenericInvokation(cl, t) of tyGenericBody: - InternalError(cl.info, "ReplaceTypeVarsT: tyGenericBody") + InternalError(cl.info, "ReplaceTypeVarsT: tyGenericBody" ) result = ReplaceTypeVarsT(cl, lastSon(t)) of tyInt: result = skipIntLit(t) # XXX now there are also float literals + of tyTypeDesc: + let lookup = PType(idTableGet(cl.typeMap, t)) # lookupTypeVar(cl, t) + if lookup != nil: + result = lookup + if tfUnresolved in t.flags: result = result.base + of tyGenericInst: + result = copyType(t, t.owner, true) + for i in 1 ..