implements #766;

expressions such as Type.field are now recognised by the compiler.
This also fixes a bug, preventing the user-defined to check for the presence of
regular fields in addition to procs
This commit is contained in:
Zahary Karadjov
2014-01-24 17:02:27 +02:00
parent a6a18be089
commit 3f71b7f1f6
11 changed files with 116 additions and 68 deletions

View File

@@ -336,34 +336,52 @@ type
tyConst, tyMutable, tyVarargs,
tyIter, # unused
tyProxy # used as errornous type (for idetools)
tyTypeClass
tyBuiltInTypeClass # Type such as the catch-all object, tuple, seq, etc
tyUserTypeClass
tyUserTypeClassInst # \
tyBuiltInTypeClass #\
# Type such as the catch-all object, tuple, seq, etc
tyUserTypeClass #\
# the body of a user-defined type class
tyUserTypeClassInst #\
# Instance of a parametric user-defined type class.
# Structured similarly to tyGenericInst.
# tyGenericInst represents concrete types, while
# this is still a "generic param" that will bind types
# and resolves them during sigmatch and instantiation.
tyCompositeTypeClass # Type such as seq[Number]
# The notes for tyUserTypeClassInst apply here as well
# sons[0]: the original expression used by the user.
# sons[1]: fully expanded and instantiated meta type
# (potentially following aliases)
tyCompositeTypeClass #\
# Type such as seq[Number]
# The notes for tyUserTypeClassInst apply here as well
# sons[0]: the original expression used by the user.
# sons[1]: fully expanded and instantiated meta type
# (potentially following aliases)
tyAnd, tyOr, tyNot # boolean type classes such as `string|int`,`not seq`,
# `Sortable and Enumable`, etc
tyAnd, tyOr, tyNot #\
# boolean type classes such as `string|int`,`not seq`,
# `Sortable and Enumable`, etc
tyAnything # a type class matching any type
tyAnything #\
# a type class matching any type
tyStatic # a value known at compile type (the underlying type is .base)
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.
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.
tyFieldAccessor #\
# Expressions such as Type.field (valid in contexts such
# as the `is` operator and magics like `high` and `low`).
# Could be lifted to a single argument proc returning the
# field value.
# sons[0]: type of containing object or tuple
# sons[1]: field type
# .n: nkDotExpr storing the field name
const
tyPureObject* = tyTuple
@@ -372,7 +390,7 @@ const
tyUnknownTypes* = {tyError, tyFromExpr}
tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass, tyCompositeTypeClass,
tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
tyUserTypeClass, tyUserTypeClassInst,
tyAnd, tyOr, tyNot, tyAnything}

View File

@@ -87,7 +87,7 @@ proc getUniqueType*(key: PType): PType =
gCanonicalTypes[k] = key
result = key
of tyTypeDesc, tyTypeClasses, tyGenericParam,
tyFromExpr, tyStatic:
tyFromExpr, tyStatic, tyFieldAccessor:
internalError("GetUniqueType")
of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter:
result = getUniqueType(lastSon(key))

View File

@@ -130,7 +130,7 @@ proc mapType(typ: PType): TJSTypeKind =
result = etyObject
of tyNil: result = etyNull
of tyGenericInst, tyGenericParam, tyGenericBody, tyGenericInvokation,
tyNone, tyFromExpr, tyForward, tyEmpty,
tyNone, tyFromExpr, tyForward, tyEmpty, tyFieldAccessor,
tyExpr, tyStmt, tyStatic, tyTypeDesc, tyTypeClasses:
result = etyNone
of tyProc: result = etyProc

View File

@@ -239,7 +239,8 @@ 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+{tyTypeDesc})
var typ = skipTypes(n.sons[1].typ, abstractVarRange +
{tyTypeDesc, tyFieldAccessor})
case typ.kind
of tySequence, tyString, tyOpenArray, tyVarargs:
n.typ = getSysType(tyInt)
@@ -247,7 +248,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
n.typ = typ.sons[0] # indextype
of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt8, tyUInt16, tyUInt32:
# do not skip the range!
n.typ = n.sons[1].typ.skipTypes(abstractVar)
n.typ = n.sons[1].typ.skipTypes(abstractVar + {tyFieldAccessor})
of tyGenericParam:
# prepare this for resolving in semtypinst:
# we must use copyTree here in order to avoid creating a cycle
@@ -306,7 +307,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
n[1].typ != nil and n[1].typ.kind == tyTypeDesc and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
let t1 = n[1].typ.skipTypes({tyTypeDesc})
let t1 = n[1].typ.skipTypes({tyTypeDesc, tyFieldAccessor})
if n[2].kind in {nkStrLit..nkTripleStrLit}:
case n[2].strVal.normalize
@@ -948,6 +949,13 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
let foundTyp = makeTypeDesc(c, rawTyp)
return newSymNode(copySym(tParam.sym).linkTo(foundTyp), n.info)
return
of tyObject, tyTuple:
if ty.n.kind == nkRecList:
for field in ty.n.sons:
if field.sym.name == i:
n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ])
n.typ.n = copyTree(n)
return n
else:
# echo "TYPE FIELD ACCESS"
# debug ty

View File

@@ -252,8 +252,7 @@ proc evalIs(n, a: PNode): PNode =
else:
# XXX semexprs.isOpImpl is slightly different and requires a context. yay.
let t2 = n[2].typ
var match = if t2.kind == tyTypeClass: true
else: sameType(t1, t2)
var match = sameType(t1, t2)
result = newIntNode(nkIntLit, ord(match))
result.typ = n.typ

View File

@@ -1240,7 +1240,7 @@ proc semStmtList(c: PContext, n: PNode): PNode =
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
voidContext = true
n.typ = enforceVoidContext
if i != last or voidContext:
if i != last or voidContext or c.inTypeClass > 0:
discardCheck(c, n.sons[i])
else:
n.typ = n.sons[i].typ

View File

@@ -736,7 +736,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
allowMetaTypes = true)
result = liftingWalk(expanded)
of tyUserTypeClass, tyTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
of tyExpr:
@@ -871,7 +871,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

View File

@@ -140,7 +140,7 @@ proc sumGeneric(t: PType): int =
result = ord(t.kind == tyGenericInvokation)
for i in 0 .. <t.len: result += t.sons[i].sumGeneric
break
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc, tyTypeClass: break
of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
else: return 0
proc complexDisambiguation(a, b: PType): int =
@@ -447,18 +447,19 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
dummyParam.typ = dummyType
addDecl(c, dummyParam)
for stmt in body.n[3]:
var e = c.semTryExpr(c, copyTree(stmt), bufferErrors = false)
m.errors = bufferedMsgs
clearBufferedMsgs()
if e == nil: return isNone
var checkedBody = c.semTryExpr(c, copyTree(body.n[3]), bufferErrors = false)
m.errors = bufferedMsgs
clearBufferedMsgs()
if checkedBody == nil: return isNone
case e.kind
of nkReturnStmt: discard
of nkTypeSection: discard
of nkConstDef: discard
else: discard
if checkedBody.kind == nkStmtList:
for stmt in checkedBody:
case stmt.kind
of nkReturnStmt: discard
of nkTypeSection: discard
of nkConstDef: discard
else: discard
return isGeneric
# put(m.bindings, f, a)
@@ -481,6 +482,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isNone
assert(f != nil)
if f.kind == tyExpr:
put(c.bindings, f, aOrig)
return isGeneric
assert(aOrig != nil)
# var and static arguments match regular modifier-free types
@@ -828,7 +834,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else:
return isNone
of tyGenericParam, tyTypeClass:
of tyGenericParam:
var x = PType(idTableGet(c.bindings, f))
if x == nil:
if c.calleeSym != nil and c.calleeSym.kind == skType and
@@ -891,7 +897,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else: a.sons[0]
result = typeRel(c, prev.sons[0], toMatch)
of tyExpr, tyStmt:
of tyStmt:
result = isGeneric
of tyProxy:
@@ -993,20 +999,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
argType = arg.typ
var
r: TTypeRelation
a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc})
else: argType
case fMaybeStatic.kind
of tyTypeClass:
if fMaybeStatic.n != nil:
r = matchUserTypeClass(c, m, fMaybeStatic, a)
else:
r = typeRel(m, f, a)
of tyExpr:
r = isGeneric
put(m.bindings, f, arg.typ)
else:
r = typeRel(m, f, a)
case r

View File

@@ -389,7 +389,7 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
result[0] = transform(c, n.sons[1])
else:
result = transform(c, n.sons[1])
of tyGenericParam, tyOrdinal, tyTypeClass:
of tyGenericParam, tyOrdinal:
result = transform(c, n.sons[1])
# happens sometimes for generated assignments, etc.
else:

View File

@@ -405,9 +405,9 @@ const
"uint", "uint8", "uint16", "uint32", "uint64",
"bignum", "const ",
"!", "varargs[$1]", "iter[$1]", "Error Type",
"TypeClass", "BuiltInTypeClass", "UserTypeClass",
"BuiltInTypeClass", "UserTypeClass",
"UserTypeClassInst", "CompositeTypeClass",
"and", "or", "not", "any", "static", "TypeFromExpr"]
"and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor"]
proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
var t = typ
@@ -435,11 +435,30 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyStatic:
internalAssert t.len > 0
result = "static[" & typeToString(t.sons[0]) & "]"
of tyTypeClass:
of tyUserTypeClass:
internalAssert t.sym != nil and t.sym.owner != nil
return t.sym.owner.name.s
of tyBuiltInTypeClass:
return "TypeClass"
result = case t.base.kind:
of tyVar: "var"
of tyRef: "ref"
of tyPtr: "ptr"
of tySequence: "seq"
of tyArray: "array"
of tySet: "set"
of tyRange: "range"
of tyDistinct: "distinct"
of tyProc: "proc"
of tyObject: "object"
of tyTuple: "tuple"
else: (internalAssert false; "")
of tyUserTypeClassInst:
let body = t.base
result = body.sym.name.s & "["
for i in countup(1, sonsLen(t) - 2):
if i > 1: add(result, ", ")
add(result, typeToString(t.sons[i]))
result.add "]"
of tyAnd:
result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
of tyOr:
@@ -449,7 +468,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyExpr:
internalAssert t.len == 0
result = "expr"
of tyFromExpr:
of tyFromExpr, tyFieldAccessor:
result = renderTree(t.n)
of tyArray:
if t.sons[0].kind == tyRange:
@@ -547,7 +566,8 @@ proc firstOrd(t: PType): BiggestInt =
else:
assert(t.n.sons[0].kind == nkSym)
result = t.n.sons[0].sym.position
of tyGenericInst, tyDistinct, tyConst, tyMutable, tyTypeDesc:
of tyGenericInst, tyDistinct, tyConst, tyMutable,
tyTypeDesc, tyFieldAccessor:
result = firstOrd(lastSon(t))
else:
internalError("invalid kind for first(" & $t.kind & ')')
@@ -580,7 +600,8 @@ proc lastOrd(t: PType): BiggestInt =
of tyEnum:
assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
result = t.n.sons[sonsLen(t.n) - 1].sym.position
of tyGenericInst, tyDistinct, tyConst, tyMutable, tyTypeDesc:
of tyGenericInst, tyDistinct, tyConst, tyMutable,
tyTypeDesc, tyFieldAccessor:
result = lastOrd(lastSon(t))
of tyProxy: result = 0
else:
@@ -876,9 +897,9 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
of tyGenericInvokation, tyGenericBody, tySequence,
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr,
tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter,
tyOrdinal, tyTypeClasses:
tyOrdinal, tyTypeClasses, tyFieldAccessor:
cycleCheck()
if a.kind == tyTypeClass and a.n != nil: return a.n == b.n
if a.kind == tyUserTypeClass 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
@@ -1022,7 +1043,7 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
of tyTypeClasses:
result = true
of tyGenericBody, tyGenericParam, tyGenericInvokation,
tyNone, tyForward, tyFromExpr:
tyNone, tyForward, tyFromExpr, tyFieldAccessor:
result = false
of tyNil:
result = kind == skConst
@@ -1232,8 +1253,15 @@ proc getSize(typ: PType): BiggestInt =
if result < 0: internalError("getSize: " & $typ.kind)
proc containsGenericTypeIter(t: PType, closure: PObject): bool =
result = t.kind in GenericTypes + tyTypeClasses + {tyTypeDesc,tyFromExpr} or
t.kind == tyStatic and t.n == nil
if t.kind in GenericTypes + tyTypeClasses + {tyFromExpr}:
return true
if t.kind == tyTypeDesc:
if t.sonsLen == 0: return true
if containsGenericTypeIter(t.base, closure): return true
return false
return t.kind == tyStatic and t.n == nil
proc containsGenericType*(t: PType): bool =
result = iterOverType(t, containsGenericTypeIter, nil)

View File

@@ -802,7 +802,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
let t1 = regs[rb].typ.skipTypes({tyTypeDesc})
let t2 = c.types[regs[rc].intVal.int]
# XXX: This should use the standard isOpImpl
let match = if t2.kind == tyTypeClass: true
let match = if t2.kind == tyUserTypeClass: true
else: sameType(t1, t2)
regs[ra].intVal = ord(match)
of opcSetLenSeq: