From 027f30610e44633b661befcca1b5dd39e9eaa283 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 19 Dec 2013 01:06:38 +0200 Subject: [PATCH 01/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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/30] 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 .. Date: Sun, 29 Dec 2013 19:26:52 +0200 Subject: [PATCH 16/30] fix the filtering of void params in procs' signatures --- compiler/semtypinst.nim | 24 ++++++++++++++++++++---- tests/reject/mbind4.nim | 9 --------- tests/reject/tactiontable2.nim | 14 +++++++------- tests/reject/tbind4.nim | 13 ------------- tests/reject/teffects1.nim | 2 +- 5 files changed, 28 insertions(+), 34 deletions(-) delete mode 100644 tests/reject/mbind4.nim delete mode 100644 tests/reject/tbind4.nim diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 0bb27946f1..04d2577075 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -189,14 +189,32 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = newbody.flags = newbody.flags + t.flags + body.flags result.flags = result.flags + newbody.flags newbody.callConv = body.callConv - newbody.n = replaceTypeVarsN(cl, lastSon(body).n) # This type may be a generic alias and we want to resolve it here. # One step is enough, because the recursive nature of # handleGenericInvokation will handle the alias-to-alias-to-alias case if newbody.isGenericAlias: newbody = newbody.skipGenericAlias rawAddSon(result, newbody) checkPartialConstructedType(cl.info, newbody) + +proc normalizeProcType(t: PType) = + if t.sons[0] != nil and t.sons[0].kind == tyEmpty: + t.sons[0] = nil + for i in 1 .. Date: Mon, 30 Dec 2013 00:03:57 +0200 Subject: [PATCH 17/30] restore return type inference --- compiler/ast.nim | 3 +++ compiler/semexprs.nim | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 6f30522a20..22b17f673a 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -905,6 +905,9 @@ template `{}=`*(n: PNode, i: int, s: PNode): stmt = var emptyNode* = newNode(nkEmpty) # There is a single empty node that is shared! Do not overwrite it! +proc isMetaType*(t: PType): bool = + return t.kind in tyMetaTypes or tfHasMeta in t.flags + proc linkTo*(t: PType, s: PSym): PType {.discardable.} = t.sym = s s.typ = t diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 4e53a5389c..14f611c5cd 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1164,7 +1164,7 @@ proc semAsgn(c: PContext, n: PNode): PNode = if lhsIsResult: {efAllowDestructor} else: {}) if lhsIsResult: n.typ = enforceVoidContext - if lhs.sym.typ.kind == tyGenericParam: + if lhs.sym.typ.isMetaType: if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric: internalAssert c.p.resultSym != nil lhs.typ = rhs.typ From e3f53409f64a74e2f672a1624de17efc84be55ed Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 30 Dec 2013 02:13:50 +0200 Subject: [PATCH 18/30] proc redefinition search based on the type system instead of on sloppy AST matching This will work the same for procs/templates/macros/etc, having arbitrary mix of implicit and explicit generics (as long as the symbols are equivalent for the purposes of overload resolution, they will be detected as redefinitions) fixes tgeneric --- compiler/procfind.nim | 26 +++++++++++++++++++++++++- compiler/semtypes.nim | 16 ---------------- compiler/types.nim | 36 +++++++++++++++++++++++------------- 3 files changed, 48 insertions(+), 30 deletions(-) diff --git a/compiler/procfind.nim b/compiler/procfind.nim index f02e7aed40..eb0cbca3f6 100644 --- a/compiler/procfind.nim +++ b/compiler/procfind.nim @@ -30,7 +30,7 @@ proc equalGenericParams(procA, procB: PNode): bool = if not exprStructuralEquivalent(a.ast, b.ast): return result = true -proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = +proc searchForProcOld*(c: PContext, scope: PScope, fn: PSym): PSym = # Searchs for a forward declaration or a "twin" symbol of fn # in the symbol table. If the parameter lists are exactly # the same the sym in the symbol table is returned, else nil. @@ -63,6 +63,30 @@ proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = nil result = nextIdentIter(it, scope.symbols) +proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym = + const flags = {ExactGenericParams, ExactTypeDescValues, + ExactConstraints, IgnoreCC} + + var it: TIdentIter + result = initIdentIter(it, scope.symbols, fn.name) + while result != nil: + if result.kind in skProcKinds and + sameType(result.typ, fn.typ, flags): return + + result = nextIdentIter(it, scope.symbols) + + return nil + +proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = + result = searchForProcNew(c, scope, fn) + when false: + let old = searchForProcOld(c, scope, fn) + if old != result: + echo "Mismatch in searchForProc: ", fn.info + debug fn.typ + debug if result != nil: result.typ else: nil + debug if old != nil: old.typ else: nil + when false: proc paramsFitBorrow(child, parent: PNode): bool = var length = sonsLen(child) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 03d8b7095b..175901057d 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -827,22 +827,6 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType = dec(c.p.nestedBlockCounter) proc semGenericParamInInvokation(c: PContext, n: PNode): PType = - # XXX hack 1022 for generics ... would have been nice if the compiler had - # been designed with them in mind from start ... - when false: - if n.kind == nkSym: - # for generics we need to lookup the type var again: - var s = searchInScopes(c, n.sym.name) - if s != nil: - if s.kind == skType and s.typ != nil: - var t = n.sym.typ - echo "came here" - return t - else: - echo "s is crap:" - debug(s) - else: - echo "s is nil!!!!" result = semTypeNode(c, n, nil) proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = diff --git a/compiler/types.nim b/compiler/types.nim index 024cf45491..7403e29f9d 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -606,8 +606,10 @@ type TTypeCmpFlag* = enum IgnoreTupleFields + IgnoreCC ExactTypeDescValues ExactGenericParams + ExactConstraints AllowCommonBase TTypeCmpFlags* = set[TTypeCmpFlag] @@ -637,15 +639,17 @@ proc sameTypeOrNilAux(a, b: PType, c: var TSameTypeClosure): bool = if a == nil or b == nil: result = false else: result = sameTypeAux(a, b, c) +proc sameType*(a, b: PType, flags: TTypeCmpFlags = {}): bool = + var c = initSameTypeClosure() + c.flags = flags + result = sameTypeAux(a, b, c) + proc sameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool = if a == b: result = true - else: + else: if a == nil or b == nil: result = false - else: - var c = initSameTypeClosure() - c.flags = flags - result = sameTypeAux(a, b, c) + else: result = sameType(a, b, flags) proc equalParam(a, b: PSym): TParamsEquality = if sameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}) and @@ -661,7 +665,15 @@ proc equalParam(a, b: PSym): TParamsEquality = result = paramsIncompatible else: result = paramsNotEqual - + +proc sameConstraints(a, b: PNode): bool = + internalAssert a.len == b.len + for i in 1 .. Date: Mon, 30 Dec 2013 11:02:48 +0200 Subject: [PATCH 19/30] Introduce a PreMain proc in the C codegen The rationale here is that it has become too hard to step into a program when #line directives are enabled. You have to skip over many lines of init code that doesn't have corresponding lines in the nimrod program. Now, you can just step-out of PreMain and go straight to the useful code in NimMain. --- compiler/cgen.nim | 98 ++++++++++++++++++++++++++----------------- compiler/cgendata.nim | 3 +- 2 files changed, 62 insertions(+), 39 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 3aef60fa6e..b08647512a 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -943,44 +943,60 @@ proc genFilenames(m: BModule): PRope = for i in 0.. Date: Mon, 30 Dec 2013 14:25:05 +0200 Subject: [PATCH 20/30] add incl/excl for sets accepting accepting other sets --- lib/system.nim | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/system.nim b/lib/system.nim index 978fae1a6e..241b0bd034 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -422,10 +422,18 @@ proc incl*[T](x: var set[T], y: T) {.magic: "Incl", noSideEffect.} ## includes element ``y`` to the set ``x``. This is the same as ## ``x = x + {y}``, but it might be more efficient. +template incl*[T](s: var set[T], flags: set[T]) = + ## includes the set of flags to the set ``x``. + s = s + flags + proc excl*[T](x: var set[T], y: T) {.magic: "Excl", noSideEffect.} ## excludes element ``y`` to the set ``x``. This is the same as ## ``x = x - {y}``, but it might be more efficient. +template excl*[T](s: var set[T], flags: set[T]) = + ## excludes the set of flags to ``x``. + s = s - flags + proc card*[T](x: set[T]): int {.magic: "Card", noSideEffect.} ## returns the cardinality of the set ``x``, i.e. the number of elements ## in the set. From 7e24cf26dec34a85d7551946f546f32e30b27f42 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 30 Dec 2013 14:30:36 +0200 Subject: [PATCH 21/30] handle recursive types during the instantiation of meta types; propagate tfHasMeta more carefully --- compiler/ast.nim | 8 +++- compiler/seminst.nim | 1 + compiler/semtypinst.nim | 98 +++++++++++++++++++++++++++++++++-------- compiler/types.nim | 5 --- 4 files changed, 86 insertions(+), 26 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 22b17f673a..8699d17151 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -404,8 +404,8 @@ type tfHasMeta, # type contains "wildcard" sub-types such as generic params # or other type classes tfHasGCedMem, # type contains GC'ed memory - tfGenericTypeParam tfHasStatic + tfGenericTypeParam TTypeFlags* = set[TTypeFlag] @@ -1229,6 +1229,10 @@ proc newSons(father: PNode, length: int) = else: setLen(father.sons, length) +proc skipTypes*(t: PType, kinds: TTypeKinds): PType = + result = t + while result.kind in kinds: result = lastSon(result) + proc propagateToOwner*(owner, elem: PType) = const HaveTheirOwnEmpty = {tySequence, tySet} owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta, @@ -1245,7 +1249,7 @@ proc propagateToOwner*(owner, elem: PType) = if tfShared in elem.flags: owner.flags.incl tfHasShared - + if elem.kind in tyMetaTypes: owner.flags.incl tfHasMeta diff --git a/compiler/seminst.nim b/compiler/seminst.nim index f7f836644c..cec3567e27 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -173,6 +173,7 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, var cl: TReplTypeVars initIdTable(cl.symMap) initIdTable(cl.typeMap) + initIdTable(cl.localCache) cl.info = info cl.c = c cl.allowMetaTypes = allowMetaTypes diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 04d2577075..0c185b09ab 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -11,6 +11,9 @@ import ast, astalgo, msgs, types, magicsys, semdata, renderer +const + tfInstClearedFlags = {tfHasMeta} + proc checkPartialConstructedType(info: TLineInfo, t: PType) = if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject: localError(info, errInvalidPragmaX, "acyclic") @@ -67,14 +70,30 @@ type c*: PContext typeMap*: TIdTable # map PType to PType symMap*: TIdTable # map PSym to PSym + localCache*: TIdTable # local cache for remembering alraedy replaced + # types during instantiation of meta types + # (they are not stored in the global cache) 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 replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode +template checkMetaInvariants(cl: TReplTypeVars, t: PType) = + when false: + if t != nil and tfHasMeta in t.flags and + cl.allowMetaTypes == false: + echo "UNEXPECTED META ", t.id, " ", instantiationInfo(-1) + debug t + writeStackTrace() + quit 1 + +proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = + result = replaceTypeVarsTAux(cl, t) + checkMetaInvariants(cl, result) + proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = result = copyNode(n) result.typ = replaceTypeVarsT(cl, n.typ) @@ -87,7 +106,9 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode = if n == nil: return result = copyNode(n) - result.typ = replaceTypeVarsT(cl, n.typ) + if n.typ != nil: + result.typ = replaceTypeVarsT(cl, n.typ) + checkMetaInvariants(cl, result.typ) case n.kind of nkNone..pred(nkSym), succ(nkSym)..nkNilLit: discard @@ -140,7 +161,12 @@ 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 instCopyType(t: PType): PType = + result = copyType(t, t.owner, false) + result.flags.incl tfFromGeneric + result.flags.excl tfInstClearedFlags + proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = # tyGenericInvokation[A, tyGenericInvokation[A, B]] # is difficult to handle: @@ -148,14 +174,17 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = if body.kind != tyGenericBody: internalError(cl.info, "no generic body") var header: PType = nil # search for some instantiation here: - result = searchInstTypes(t) + if cl.allowMetaTypes: + result = PType(idTableGet(cl.localCache, t)) + else: + result = searchInstTypes(t) if result != nil: return for i in countup(1, sonsLen(t) - 1): var x = t.sons[i] if x.kind == tyGenericParam: x = lookupTypeVar(cl, x) if x != nil: - if header == nil: header = copyType(t, t.owner, false) + if header == nil: header = instCopyType(t) header.sons[i] = x propagateToOwner(header, x) @@ -164,14 +193,18 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = result = searchInstTypes(header) if result != nil: return else: - header = copyType(t, t.owner, false) + header = instCopyType(t) + + result = newType(tyGenericInst, t.sons[0].owner) + # be careful not to propagate unnecessary flags here (don't use rawAddSon) + result.sons = @[header.sons[0]] # ugh need another pass for deeply recursive generic types (e.g. PActor) # we need to add the candidate here, before it's fully instantiated for # recursive instantions: - result = newType(tyGenericInst, t.sons[0].owner) - result.rawAddSon(header.sons[0]) if not cl.allowMetaTypes: cacheTypeInst(result) + else: + idTablePut(cl.localCache, t, result) for i in countup(1, sonsLen(t) - 1): var x = replaceTypeVarsT(cl, t.sons[i]) @@ -180,13 +213,13 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = propagateToOwner(header, x) idTablePut(cl.typeMap, body.sons[i-1], x) - for i in countup(1, sonsLen(t) - 1): + for i in countup(1, sonsLen(t) - 1): # 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 + newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags) result.flags = result.flags + newbody.flags newbody.callConv = body.callConv # This type may be a generic alias and we want to resolve it here. @@ -215,9 +248,22 @@ proc normalizeProcType(t: PType) = setLen t.n.sons, pos return -proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = +proc propagateFieldFlags(t: PType, n: PNode) = + # This is meant for objects and tuples + # The type must be fully instantiated! + internalAssert n.kind != nkRecWhen + case n.kind + of nkSym: + propagateToOwner(t, n.sym.typ) + of nkRecList, nkRecCase, nkOfBranch, nkElse: + for son in n.sons: + propagateFieldFlags(t, son) + else: discard + +proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = t if t == nil: return + if t.kind == tyStatic and t.sym != nil and t.sym.kind == skGenericParam: let s = lookupTypeVar(cl, t) return if s != nil: s else: t @@ -243,9 +289,10 @@ proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType = result = lookup if tfUnresolved in t.flags: result = result.base of tyGenericInst: - result = copyType(t, t.owner, true) + result = instCopyType(t) for i in 1 .. Date: Mon, 30 Dec 2013 17:07:49 +0200 Subject: [PATCH 22/30] properly remove intLiterals from proc signatures; fixes trettypeinference --- compiler/semtypinst.nim | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 0c185b09ab..029bf6c8c6 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -229,15 +229,13 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = rawAddSon(result, newbody) checkPartialConstructedType(cl.info, newbody) -proc normalizeProcType(t: PType) = +proc eraseVoidParams(t: PType) = if t.sons[0] != nil and t.sons[0].kind == tyEmpty: t.sons[0] = nil for i in 1 .. 0: t.n.sons[i].sym.typ = skipped + proc propagateFieldFlags(t: PType, n: PNode) = # This is meant for objects and tuples # The type must be fully instantiated! @@ -320,7 +327,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = of tyObject, tyTuple: propagateFieldFlags(result, result.n) of tyProc: - normalizeProcType(result) + eraseVoidParams(result) + skipIntLiteralParams(result) else: discard proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, From 754e2ef1db61610cba1b96752fa37c6e923e2808 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 30 Dec 2013 19:11:42 +0200 Subject: [PATCH 23/30] migrate the static param handling to ReplaceTypeVars; fix tgenericvariant --- compiler/sem.nim | 11 +++++++---- compiler/semtypes.nim | 23 +++++++++++++++++------ compiler/semtypinst.nim | 21 ++++++++++----------- compiler/sigmatch.nim | 3 +-- compiler/types.nim | 4 ++-- tests/reject/teffects1.nim | 2 +- 6 files changed, 38 insertions(+), 26 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index df37a6384f..e9c2de6572 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -204,12 +204,15 @@ proc tryConstExpr(c: PContext, n: PNode): PNode = result = getConstExpr(c.module, e) if result != nil: return - result = evalConstExpr(c.module, e) - if result == nil or result.kind == nkEmpty: + try: + result = evalConstExpr(c.module, e) + if result == nil or result.kind == nkEmpty: + return nil + + result = fixupTypeAfterEval(c, result, e) + except: return nil - result = fixupTypeAfterEval(c, result, e) - proc semConstExpr(c: PContext, n: PNode): PNode = var e = semExprWithType(c, n) if e == nil: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 175901057d..fb05469615 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -594,7 +594,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = let typedescId = getIdent"typedesc" template shouldHaveMeta(t) = - InternalAssert tfHasMeta in result.lastSon.flags + InternalAssert tfHasMeta in t.flags # result.lastSon.flags.incl tfHasMeta proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, @@ -677,7 +677,8 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result = newTypeS(tyGenericInvokation, c) result.rawAddSon(paramType) for i in 0 .. paramType.sonsLen - 2: - result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true)) + result.rawAddSon newTypeS(tyAnything, c) + # result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true)) result = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) result.lastSon.shouldHaveMeta @@ -696,20 +697,29 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if liftBody != nil: result = liftBody result.shouldHaveMeta - + + of tyGenericInvokation: + for i in 1 .. 0: result = liftingWalk(paramType.lastSon) else: - result = addImplicitGeneric(newTypeS(tyGenericParam, c)) + result = addImplicitGeneric(newTypeS(tyAnything, c)) else: nil @@ -860,7 +870,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = return newOrPrevType(tyError, prev, c) var isConcrete = true - + for i in 1 .. Date: Tue, 31 Dec 2013 03:58:31 +0200 Subject: [PATCH 24/30] Templates will pick the candidate in the nearest scope when symbols are mixed-in --- compiler/ast.nim | 4 ++++ compiler/semexprs.nim | 2 +- compiler/semtempl.nim | 1 + compiler/semtypinst.nim | 15 ++++++++------- compiler/sigmatch.nim | 23 ++++++++++++++++------- compiler/types.nim | 3 ++- 6 files changed, 32 insertions(+), 16 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 8699d17151..e25f10a669 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1430,6 +1430,10 @@ proc skipGenericOwner*(s: PSym): PSym = result = if sfFromGeneric in s.flags: s.owner.owner else: s.owner +proc originatingModule*(s: PSym): PSym = + result = s.owner + while result.kind != skModule: result = result.owner + proc isRoutine*(s: PSym): bool {.inline.} = result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod, skConverter} diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 14f611c5cd..00c0e0f78b 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1164,7 +1164,7 @@ proc semAsgn(c: PContext, n: PNode): PNode = if lhsIsResult: {efAllowDestructor} else: {}) if lhsIsResult: n.typ = enforceVoidContext - if lhs.sym.typ.isMetaType: + if lhs.sym.typ.isMetaType and lhs.sym.typ.kind != tyTypeDesc: if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric: internalAssert c.p.resultSym != nil lhs.typ = rhs.typ diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index da38f8625a..569d92d33f 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -421,6 +421,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = else: s = semIdentVis(c, skTemplate, n.sons[0], {}) # check parameter list: + s.scope = c.currentScope pushOwner(s) openScope(c) n.sons[namePos] = newSymNode(s, n.sons[namePos].info) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index fb7be3a385..ec56f75e41 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -162,8 +162,9 @@ proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType = elif result.kind == tyGenericParam and not cl.allowMetaTypes: internalError(cl.info, "substitution with generic parameter") -proc instCopyType(t: PType): PType = - result = copyType(t, t.owner, false) +proc instCopyType(cl: var TReplTypeVars, t: PType): PType = + # XXX: relying on allowMetaTypes is a kludge + result = copyType(t, t.owner, cl.allowMetaTypes) result.flags.incl tfFromGeneric result.flags.excl tfInstClearedFlags @@ -184,7 +185,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = if x.kind == tyGenericParam: x = lookupTypeVar(cl, x) if x != nil: - if header == nil: header = instCopyType(t) + if header == nil: header = instCopyType(cl, t) header.sons[i] = x propagateToOwner(header, x) @@ -193,7 +194,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = result = searchInstTypes(header) if result != nil: return else: - header = instCopyType(t) + header = instCopyType(cl, t) result = newType(tyGenericInst, t.sons[0].owner) # be careful not to propagate unnecessary flags here (don't use rawAddSon) @@ -279,7 +280,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses: let lookup = PType(idTableGet(cl.typeMap, t)) if lookup != nil: return lookup - + case t.kind of tyGenericInvokation: result = handleGenericInvokation(cl, t) @@ -295,7 +296,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = lookup if tfUnresolved in t.flags: result = result.base of tyGenericInst: - result = instCopyType(t) + result = instCopyType(cl, t) for i in 1 .. 0: diff --git a/compiler/types.nim b/compiler/types.nim index 1bf18fbe29..68e816c13c 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -874,7 +874,8 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter, tyOrdinal, tyTypeClasses: - cycleCheck() + cycleCheck() + if a.kind == tyTypeClass and a.n != nil: return a.n == b.n result = sameChildrenAux(a, b, c) and sameFlags(a, b) if result and a.kind == tyProc: result = ((IgnoreCC in c.flags) or a.callConv == b.callConv) and From ed3ab6539dde803eef0c91d5b8edd58408ba86d8 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 31 Dec 2013 04:13:51 +0200 Subject: [PATCH 25/30] close #517 --- tests/compile/tcompositetypeclasses.nim | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/compile/tcompositetypeclasses.nim b/tests/compile/tcompositetypeclasses.nim index 4ba92fed1a..3e6f0d6ae1 100644 --- a/tests/compile/tcompositetypeclasses.nim +++ b/tests/compile/tcompositetypeclasses.nim @@ -33,3 +33,13 @@ accept baz(vbaz) reject baz(vnotbaz) reject bar(vfoo) +# https://github.com/Araq/Nimrod/issues/517 +type + TVecT*[T] = array[0..1, T]|array[0..2, T]|array[0..3, T] + TVec2* = array[0..1, float32] + +proc f[T](a: TVecT[T], b: TVecT[T]): T = discard + +var x: float = f([0.0'f32, 0.0'f32], [0.0'f32, 0.0'f32]) +var y = f(TVec2([0.0'f32, 0.0'f32]), TVec2([0.0'f32, 0.0'f32])) + From 8e0941576fc30643a4cdcb532e180d0ca973ed85 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 31 Dec 2013 12:39:43 +0200 Subject: [PATCH 26/30] clean-up some obsolete code; close #602 --- compiler/options.nim | 2 - compiler/seminst.nim | 154 ++---------------------- compiler/semstmts.nim | 16 +-- compiler/semtypes.nim | 22 ++-- compiler/sigmatch.nim | 70 +++++------ tests/compile/tcompositetypeclasses.nim | 14 +++ 6 files changed, 73 insertions(+), 205 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index f184deb698..9b0cbf0c9c 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -158,8 +158,6 @@ var const oKeepVariableNames* = true -const oUseLateInstantiation* = false - proc mainCommandArg*: string = ## This is intended for commands like check or parse ## which will work on the main project file unless diff --git a/compiler/seminst.nim b/compiler/seminst.nim index cec3567e27..370d59b107 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -131,157 +131,20 @@ proc sideEffectsCheck(c: PContext, s: PSym) = s.ast.sons[genericParamsPos].kind == nkEmpty: c.threadEntries.add(s) -proc lateInstantiateGeneric(c: PContext, invocation: PType, info: TLineInfo): PType = - internalAssert invocation.kind == tyGenericInvokation - - let cacheHit = searchInstTypes(invocation) - if cacheHit != nil: - result = cacheHit - else: - let s = invocation.sons[0].sym - let oldScope = c.currentScope - c.currentScope = s.typScope - openScope(c) - pushInfoContext(info) - for i in 0 .. Date: Thu, 2 Jan 2014 17:55:18 +0200 Subject: [PATCH 27/30] fixed #597 --- compiler/astalgo.nim | 2 + compiler/msgs.nim | 3 ++ compiler/semdestruct.nim | 80 ++++++++++++++++++++++++++------------- compiler/semstmts.nim | 2 +- tests/run/tdestructor.nim | 76 ++++++++++++++++++++++++++++++++----- 5 files changed, 126 insertions(+), 37 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 4b73485667..110ee191f9 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -432,6 +432,8 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int): PRope = proc debug(n: PSym) = if n == nil: writeln(stdout, "null") + elif n.kind == skUnknown: + writeln(stdout, "skUnknown") else: #writeln(stdout, ropeToStr(symToYaml(n, 0, 1))) writeln(stdout, ropeToStr(ropef("$1_$2: $3, $4", [ diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 1b4d8e47a1..d7944a1823 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -93,6 +93,7 @@ type errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely, errOnlyACallOpCanBeDelegator, errUsingNoSymbol, + errDestructorNotGenericEnough, errXExpectsTwoArguments, errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, @@ -322,6 +323,8 @@ const errInstantiateXExplicitely: "instantiate '$1' explicitely", errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator", errUsingNoSymbol: "'$1' is not a variable, constant or a proc name", + errDestructorNotGenericEnough: "Destructor signarue is too specific. " & + "A destructor must be associated will all instantiations of a generic type", errXExpectsTwoArguments: "\'$1\' expects two arguments", errXExpectsObjectTypes: "\'$1\' expects object types", errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype", diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim index 6965b36bc9..9dbbf29402 100644 --- a/compiler/semdestruct.nim +++ b/compiler/semdestruct.nim @@ -9,6 +9,7 @@ ## This module implements destructors. +# included from sem.nim # special marker values that indicates that we are # 1) AnalyzingDestructor: currently analyzing the type for destructor @@ -25,10 +26,22 @@ var destructorPragma = newIdentNode(getIdent"destructor", unknownLineInfo()) rangeDestructorProc*: PSym -proc instantiateDestructor(c: PContext, typ: PType): bool +proc instantiateDestructor(c: PContext, typ: PType): PType proc doDestructorStuff(c: PContext, s: PSym, n: PNode) = - let t = s.typ.sons[1].skipTypes({tyVar}) + var t = s.typ.sons[1].skipTypes({tyVar}) + if t.kind == tyGenericInvokation: + for i in 1 .. Date: Sat, 4 Jan 2014 12:28:25 +0200 Subject: [PATCH 28/30] introduce tyFromExpr; fixes #618 --- compiler/ast.nim | 28 +++++++++++++++------- compiler/ccgutils.nim | 5 ++-- compiler/jsgen.nim | 5 ++-- compiler/sem.nim | 8 +++++++ compiler/semdata.nim | 4 ++++ compiler/semgnrc.nim | 7 ------ compiler/semtypes.nim | 6 +++++ compiler/semtypinst.nim | 4 ++++ compiler/sigmatch.nim | 10 +++++--- compiler/types.nim | 11 +++++---- tests/compile/compilehelpers.nim | 6 +++++ tests/compile/tmatrix3.nim | 41 ++++++++++++++++++++++++++++++++ tests/run/tdestructor.nim | 2 +- 13 files changed, 109 insertions(+), 28 deletions(-) create mode 100644 tests/compile/compilehelpers.nim create mode 100644 tests/compile/tmatrix3.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index e25f10a669..2f6b6fc9f1 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -339,19 +339,31 @@ type tyTypeClass tyParametricTypeClass # structured similarly to tyGenericInst # lastSon is the body of the type class - tyBuiltInTypeClass - tyCompositeTypeClass - tyAnd - tyOr - tyNot - tyAnything - tyStatic + + tyBuiltInTypeClass # Type such as the catch-all object, tuple, seq, etc + + tyCompositeTypeClass # + + tyAnd, tyOr, tyNot # boolean type classes such as `string|int`,`not seq`, + # `Sortable and Enumable`, etc + + tyAnything # a type class matching any type + + tyStatic # a value known at compile type (the underlying type is .base) + + tyFromExpr # This is a type representing an expression that depends + # on generic parameters (the exprsesion is stored in t.n) + # It will be converted to a real type only during generic + # instantiation and prior to this it has the potential to + # be any type. const tyPureObject* = tyTuple GcTypeKinds* = {tyRef, tySequence, tyString} tyError* = tyProxy # as an errornous node should match everything - + + tyUnknownTypes* = {tyError, tyFromExpr} + tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass, tyCompositeTypeClass, tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything} diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index b07047ec43..fe349174fd 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -86,9 +86,8 @@ proc getUniqueType*(key: PType): PType = if result == nil: gCanonicalTypes[k] = key result = key - of tyTypeDesc, tyTypeClasses: - internalError("value expected, but got a type") - of tyGenericParam, tyStatic: + of tyTypeDesc, tyTypeClasses, tyGenericParam, + tyFromExpr, tyStatic: internalError("GetUniqueType") of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter: result = getUniqueType(lastSon(key)) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index ddfc189dda..b4e696d0a5 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -129,8 +129,9 @@ proc mapType(typ: PType): TJSTypeKind = tyVarargs: result = etyObject of tyNil: result = etyNull - of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvokation, tyNone, - tyForward, tyEmpty, tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses: + of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvokation, + tyNone, tyFromExpr, tyForward, tyEmpty, + tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses: result = etyNone of tyProc: result = etyProc of tyCString: result = etyString diff --git a/compiler/sem.nim b/compiler/sem.nim index e9c2de6572..b53e7335c8 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -293,6 +293,14 @@ proc semConstBoolExpr(c: PContext, n: PNode): PNode = localError(n.info, errConstExprExpected) result = nn +type + TSemGenericFlag = enum + withinBind, withinTypeDesc, withinMixin + TSemGenericFlags = set[TSemGenericFlag] + +proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, + ctx: var TIntSet): PNode + include semtypes, semtempl, semgnrc, semstmts, semexprs proc addCodeForGenerics(c: PContext, n: PNode) = diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 3020a6af16..2e920d9cff 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -214,6 +214,10 @@ proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc) return newSymNode(sym, info) +proc makeTypeFromExpr*(c: PContext, n: PNode): PType = + result = newTypeS(tyFromExpr, c) + result.n = n + proc makeAndType*(c: PContext, t1, t2: PType): PType = result = newTypeS(tyAnd, c) result.sons = @[t1, t2] diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 9f477492c6..b40e86cbfc 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -17,11 +17,6 @@ # included from sem.nim -type - TSemGenericFlag = enum - withinBind, withinTypeDesc, withinMixin - TSemGenericFlags = set[TSemGenericFlag] - proc getIdentNode(n: PNode): PNode = case n.kind of nkPostfix: result = getIdentNode(n.sons[1]) @@ -31,8 +26,6 @@ proc getIdentNode(n: PNode): PNode = illFormedAst(n) result = n -proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, - ctx: var TIntSet): PNode proc semGenericStmtScope(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var TIntSet): PNode = diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 3ce504d2cb..79147ab825 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -988,6 +988,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result.rawAddSon(semTypeNode(c, n.sons[i], nil)) else: result = semGeneric(c, n, s, prev) of nkIdent, nkDotExpr, nkAccQuoted: + if n.kind == nkDotExpr: + let head = qualifiedLookUp(c, n[0], {checkAmbiguity, checkUndeclared}) + if head.kind in {skType}: + var toBind = initIntSet() + var preprocessed = semGenericStmt(c, n, {}, toBind) + return makeTypeFromExpr(c, preprocessed) var s = semTypeIdent(c, n) if s.typ == nil: if s.kind != skError: localError(n.info, errTypeExpected) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index ec56f75e41..75d2666795 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -287,6 +287,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = of tyGenericBody: internalError(cl.info, "ReplaceTypeVarsT: tyGenericBody" ) result = replaceTypeVarsT(cl, lastSon(t)) + of tyFromExpr: + var n = prepareNode(cl, t.n) + n = cl.c.semExpr(cl.c, n, {}) + result = n.typ.skipTypes({tyTypeDesc}) of tyInt: result = skipIntLit(t) # XXX now there are also float literals diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 2e314d115e..43f1cded92 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -665,10 +665,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyGenericInst: let roota = a.skipGenericAlias let rootf = f.skipGenericAlias - if a.kind == tyGenericInst and roota.base == rootf.base: + if a.kind == tyGenericInst and roota.base == rootf.base : for i in 1 .. rootf.sonsLen-2: - result = typeRel(c, rootf.sons[i], roota.sons[i]) - if result == isNone: return + let ff = rootf.sons[i] + let aa = roota.sons[i] + result = typeRel(c, ff, aa) + if result == isNone: return + if ff.kind == tyRange and result != isEqual: return isNone + result = isGeneric else: result = typeRel(c, lastSon(f), a) diff --git a/compiler/types.nim b/compiler/types.nim index 68e816c13c..dd808915e0 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -10,7 +10,7 @@ # this module contains routines for accessing and iterating over types import - intsets, ast, astalgo, trees, msgs, strutils, platform + intsets, ast, astalgo, trees, msgs, strutils, platform, renderer proc firstOrd*(t: PType): BiggestInt proc lastOrd*(t: PType): BiggestInt @@ -406,7 +406,7 @@ const "bignum", "const ", "!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass", "ParametricTypeClass", "BuiltInTypeClass", "CompositeTypeClass", - "and", "or", "not", "any", "static"] + "and", "or", "not", "any", "static", "TypeFromExpr"] proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = var t = typ @@ -448,6 +448,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = of tyExpr: InternalAssert t.len == 0 result = "expr" + of tyFromExpr: + result = renderTree(t.n) of tyArray: if t.sons[0].kind == tyRange: result = "array[" & rangeToStr(t.sons[0].n) & ", " & @@ -837,7 +839,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString, tyInt..tyBigNum, tyStmt, tyExpr: result = sameFlags(a, b) - of tyStatic: + of tyStatic, tyFromExpr: result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b) of tyObject: ifFastObjectTypeCheckFailed(a, b): @@ -1018,7 +1020,8 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind, result = taField in flags of tyTypeClasses: result = true - of tyGenericBody, tyGenericParam, tyForward, tyNone, tyGenericInvokation: + of tyGenericBody, tyGenericParam, tyGenericInvokation, + tyNone, tyForward, tyFromExpr: result = false of tyNil: result = kind == skConst diff --git a/tests/compile/compilehelpers.nim b/tests/compile/compilehelpers.nim new file mode 100644 index 0000000000..cb26ca5b5a --- /dev/null +++ b/tests/compile/compilehelpers.nim @@ -0,0 +1,6 @@ +template accept(e: expr) = + static: assert(compiles(e)) + +template reject(e: expr) = + static: assert(not compiles(e)) + diff --git a/tests/compile/tmatrix3.nim b/tests/compile/tmatrix3.nim new file mode 100644 index 0000000000..9004045249 --- /dev/null +++ b/tests/compile/tmatrix3.nim @@ -0,0 +1,41 @@ +discard """ + output: '''0.0000000000000000e+00 +0.0000000000000000e+00 +0 +0 +0 +''' +""" + +include compilehelpers + +type + Matrix*[M, N, T] = object + aij*: array[M, array[N, T]] + + Matrix2*[T] = Matrix[range[0..1], range[0..1], T] + + Matrix3*[T] = Matrix[range[0..2], range[0..2], T] + +proc mn(x: Matrix): Matrix.T = x.aij[0][0] + +proc m2(x: Matrix2): Matrix2.T = x.aij[0][0] + +proc m3(x: Matrix3): auto = x.aij[0][0] + +var + matn: Matrix[range[0..3], range[0..2], int] + mat2: Matrix2[int] + mat3: Matrix3[float] + +echo m3(mat3) +echo mn(mat3) +echo m2(mat2) +echo mn(mat2) +echo mn(matn) + +reject m3(mat2) +reject m3(matn) +reject m2(mat3) +reject m2(matn) + diff --git a/tests/run/tdestructor.nim b/tests/run/tdestructor.nim index 9b90995c74..bb1410d92d 100644 --- a/tests/run/tdestructor.nim +++ b/tests/run/tdestructor.nim @@ -1,7 +1,7 @@ discard """ output: '''---- myobj constructed -myobj destructed +myobj destroyed ---- mygeneric1 constructed mygeneric1 destroyed From 1ffae7cbafd63ed5d8546dcda1a0e5ec883fd00b Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 6 Jan 2014 00:15:55 +0200 Subject: [PATCH 29/30] progress towards fixing tgenericshardcases --- compiler/ast.nim | 1 + compiler/semdata.nim | 15 ++-- compiler/semexprs.nim | 10 +-- compiler/seminst.nim | 3 +- compiler/semtypes.nim | 28 +++++++- compiler/semtypinst.nim | 104 ++++++++++++++++++++++----- compiler/types.nim | 2 +- lib/system.nim | 2 +- tests/compile/tgenericshardcases.nim | 24 ++++--- 9 files changed, 146 insertions(+), 43 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 2f6b6fc9f1..1d356b6d89 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -418,6 +418,7 @@ type tfHasGCedMem, # type contains GC'ed memory tfHasStatic tfGenericTypeParam + tfImplicitTypeParam TTypeFlags* = set[TTypeFlag] diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 2e920d9cff..980abb865e 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -218,6 +218,16 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType = result = newTypeS(tyFromExpr, c) result.n = n +proc newTypeWithSons*(c: PContext, kind: TTypeKind, + sons: seq[PType]): PType = + result = newType(kind, getCurrOwner()) + result.sons = sons + +proc makeStaticExpr*(c: PContext, n: PNode): PNode = + result = newNodeI(nkStaticExpr, n.info) + result.sons = @[n] + result.typ = newTypeWithSons(c, tyStatic, @[n.typ]) + proc makeAndType*(c: PContext, t1, t2: PType): PType = result = newTypeS(tyAnd, c) result.sons = @[t1, t2] @@ -238,11 +248,6 @@ proc makeNotType*(c: PContext, t1: PType): PType = proc newTypeS(kind: TTypeKind, c: PContext): PType = result = newType(kind, getCurrOwner()) -proc newTypeWithSons*(c: PContext, kind: TTypeKind, - sons: seq[PType]): PType = - result = newType(kind, getCurrOwner()) - result.sons = sons - proc errorType*(c: PContext): PType = ## creates a type representing an error state result = newTypeS(tyError, c) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 00c0e0f78b..1f7803c95d 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -231,7 +231,7 @@ proc semCast(c: PContext, n: PNode): PNode = if not isCastable(result.typ, result.sons[1].typ): localError(result.info, errExprCannotBeCastedToX, typeToString(result.typ)) - + proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = const opToStr: array[mLow..mHigh, string] = ["low", "high"] @@ -239,7 +239,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = localError(n.info, errXExpectsTypeOrValue, opToStr[m]) else: n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) - var typ = skipTypes(n.sons[1].typ, abstractVarRange) + var typ = skipTypes(n.sons[1].typ, abstractVarRange+{tyTypeDesc}) case typ.kind of tySequence, tyString, tyOpenArray, tyVarargs: n.typ = getSysType(tyInt) @@ -249,8 +249,10 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = # do not skip the range! n.typ = n.sons[1].typ.skipTypes(abstractVar) of tyGenericParam: - # leave it for now, it will be resolved in semtypinst - n.typ = getSysType(tyInt) + # prepare this for resolving in semtypinst: + # we must use copyTree here in order to avoid creating a cycle + # that could easily turn into an infinite recursion in semtypinst + n.typ = makeTypeFromExpr(c, n.copyTree) else: localError(n.info, errInvalidArgForX, opToStr[m]) result = n diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 370d59b107..8faf1d21a5 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -167,8 +167,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, result.ast = n pushOwner(result) openScope(c) - if n.sons[genericParamsPos].kind == nkEmpty: - internalError(n.info, "generateInstance") + internalAssert n.sons[genericParamsPos].kind != nkEmpty n.sons[namePos] = newSymNode(result) pushInfoContext(info) var entry = TInstantiation.new diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 79147ab825..64c8f81e26 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -186,6 +186,11 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType = localError(n.info, errXExpectsOneTypeParam, "range") result = newOrPrevType(tyError, prev, c) +proc nMinusOne(n: PNode): PNode = + result = newNode(nkCall, n.info, @[ + newSymNode(getSysMagic("<", mUnaryLt)), + n]) + proc semArray(c: PContext, n: PNode, prev: PType): PType = var indx, base: PType result = newOrPrevType(tyArray, prev, c) @@ -194,7 +199,9 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = if isRange(n[1]): indx = semRangeAux(c, n[1], nil) else: let e = semExprWithType(c, n.sons[1], {efDetermineType}) - if e.kind in {nkIntLit..nkUInt64Lit}: + if e.typ.kind == tyFromExpr: + indx = e.typ + elif e.kind in {nkIntLit..nkUInt64Lit}: indx = makeRangeType(c, 0, e.intVal-1, n.info, e.typ) elif e.kind == nkSym and e.typ.kind == tyStatic: if e.sym.ast != nil: return semArray(c, e.sym.ast, nil) @@ -202,11 +209,25 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = if not isOrdinalType(e.typ.lastSon): localError(n[1].info, errOrdinalTypeExpected) indx = e.typ + elif e.kind in nkCallKinds and hasGenericArguments(e): + if not isOrdinalType(e.typ): + localError(n[1].info, errOrdinalTypeExpected) + # This is an int returning call, depending on an + # yet unknown generic param (see tgenericshardcases). + # We are going to construct a range type that will be + # properly filled-out in semtypinst (see how tyStaticExpr + # is handled there). + let intType = getSysType(tyInt) + indx = newTypeS(tyRange, c) + indx.sons = @[intType] + indx.n = newNode(nkRange, n.info, @[ + newIntTypeNode(nkIntLit, 0, intType), + makeStaticExpr(c, e.nMinusOne)]) else: indx = e.typ.skipTypes({tyTypeDesc}) addSonSkipIntLit(result, indx) if indx.kind == tyGenericInst: indx = lastSon(indx) - if indx.kind notin {tyGenericParam, tyStatic}: + if indx.kind notin {tyGenericParam, tyStatic, tyFromExpr}: if not isOrdinalType(indx): localError(n.sons[1].info, errOrdinalTypeExpected) elif enumHasHoles(indx): @@ -619,6 +640,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, var s = newSym(skType, finalTypId, owner, info) if typId == nil: s.flags.incl(sfAnon) s.linkTo(typeClass) + typeClass.flags.incl tfImplicitTypeParam s.position = genericParams.len genericParams.addSon(newSymNode(s)) result = typeClass @@ -844,7 +866,7 @@ proc semGenericParamInInvokation(c: PContext, n: PNode): PType = proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = result = newOrPrevType(tyGenericInvokation, prev, c) addSonSkipIntLit(result, s.typ) - + template addToResult(typ) = if typ.isNil: internalAssert false diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 75d2666795..b08119d4a7 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -98,11 +98,50 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = result = copyNode(n) result.typ = replaceTypeVarsT(cl, n.typ) if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym) - for i in 0 .. safeLen(n)-1: - # XXX HACK: ``f(a, b)``, avoid to instantiate `f` - if i == 0: result.add(n[i]) + let isCall = result.kind in nkCallKinds + for i in 0 .. 0: @@ -273,45 +316,58 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = t if t == nil: return - #if t.kind == tyStatic and t.sym != nil and t.sym.kind == skGenericParam: - # let s = lookupTypeVar(cl, t) - # return if s != nil: s else: t - if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses: let lookup = PType(idTableGet(cl.typeMap, t)) if lookup != nil: return lookup - + case t.kind of tyGenericInvokation: result = handleGenericInvokation(cl, t) + of tyGenericBody: internalError(cl.info, "ReplaceTypeVarsT: tyGenericBody" ) result = replaceTypeVarsT(cl, lastSon(t)) + of tyFromExpr: var n = prepareNode(cl, t.n) - n = cl.c.semExpr(cl.c, n, {}) - result = n.typ.skipTypes({tyTypeDesc}) + n = cl.c.semConstExpr(cl.c, n) + if n.typ.kind == tyTypeDesc: + # XXX: sometimes, chained typedescs enter here. + # It may be worth investigating why this is happening, + # because it may cause other bugs elsewhere. + result = n.typ.skipTypes({tyTypeDesc}) + # result = n.typ.base + else: + if n.typ.kind != tyStatic: + # XXX: In the future, semConstExpr should + # return tyStatic values to let anyone make + # use of this knowledge. The patching here + # won't be necessary then. + result = newTypeS(tyStatic, cl.c) + result.sons = @[n.typ] + result.n = n + else: + result = n.typ + 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 + elif t.sonsLen > 0: + result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0])) + of tyGenericInst: result = instCopyType(cl, t) for i in 1 .. Date: Mon, 6 Jan 2014 00:16:16 +0200 Subject: [PATCH 30/30] fix bootstrapping on windows --- compiler/sigmatch.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 43f1cded92..7066c5afdb 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -430,7 +430,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = return typeRel(c, f, lastSon(a)) template bindingRet(res) = - when res == isGeneric: put(c.bindings, f, aOrig) + when res == isGeneric: + let bound = aOrig.skipTypes({tyRange}).skipIntLit + put(c.bindings, f, bound) return res template considerPreviousT(body: stmt) {.immediate.} =