diff options
author | Zahary Karadjov <zahary@gmail.com> | 2017-05-13 14:38:07 +0300 |
---|---|---|
committer | Zahary Karadjov <zahary@gmail.com> | 2017-05-13 14:38:07 +0300 |
commit | 0aede22e87926304ca1eea0e96805e4e58fb5d7b (patch) | |
tree | 79e24991cd7a7cca4dbe49e5c659e65a24d041e7 /compiler | |
parent | 7e0c66ffe79f1616c1c821ede5f50f03406feabe (diff) | |
download | Nim-0aede22e87926304ca1eea0e96805e4e58fb5d7b.tar.gz |
enforce the covariance rules for user-defined generic types
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 14 | ||||
-rw-r--r-- | compiler/msgs.nim | 2 | ||||
-rw-r--r-- | compiler/semstmts.nim | 88 | ||||
-rw-r--r-- | compiler/semtypes.nim | 10 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 8 |
5 files changed, 105 insertions, 17 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 4bd76e41e..ee6391919 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -270,8 +270,8 @@ type sfDiscardable, # returned value may be discarded implicitly sfOverriden, # proc is overriden sfGenSym # symbol is 'gensym'ed; do not add to symbol table - sfCovariant # covariant generic param mimicing seq/array type - sfStrongCovariant # covariant generic param mimicing ptr type + sfCovariant # covariant generic param mimicing a ptr type + sfWeakCovariant # covariant generic param mimicing a seq/array type sfContravariant # contravariant generic param TSymFlags* = set[TSymFlag] @@ -1009,15 +1009,17 @@ proc add*(father, son: PNode) = if isNil(father.sons): father.sons = @[] add(father.sons, son) -proc `[]`*(n: PNode, i: int): PNode {.inline.} = - result = n.sons[i] +type Indexable = PNode | PType + +template `[]`*(n: Indexable, i: int): Indexable = + n.sons[i] template `-|`*(b, s: untyped): untyped = (if b >= 0: b else: s.len + b) # son access operators with support for negative indices -template `{}`*(n: PNode, i: int): untyped = n[i -| n] -template `{}=`*(n: PNode, i: int, s: PNode) = +template `{}`*(n: Indexable, i: int): untyped = n[i -| n] +template `{}=`*(n: Indexable, i: int, s: Indexable) = n.sons[i -| n] = s when defined(useNodeIds): diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 1842ce1de..e416a3826 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -381,7 +381,7 @@ const "of the generic paramers can be inferred from the expected signature.", errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.", errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", - errInOutFlagNotExtern: "`in` and `out` flags can be used only with imported types", + errInOutFlagNotExtern: "The `$1` modifier can be used only with imported types", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\'", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index e52a1b5cc..4c1fbede3 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -730,7 +730,9 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = var s: PSym if name.kind == nkDotExpr: s = qualifiedLookUp(c, name, {checkUndeclared, checkModule}) - if s.kind != skType or s.typ.skipTypes(abstractPtrs).kind != tyObject or tfPartial notin s.typ.skipTypes(abstractPtrs).flags: + if s.kind != skType or + s.typ.skipTypes(abstractPtrs).kind != tyObject or + tfPartial notin s.typ.skipTypes(abstractPtrs).flags: localError(name.info, "only .partial objects can be extended") else: s = semIdentDef(c, name, skType) @@ -742,6 +744,87 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = if sfGenSym notin s.flags: addInterfaceDecl(c, s) a.sons[0] = newSymNode(s) +proc checkCovariantParamsUsages(genericType: PType) = + var body = genericType{-1} + + proc traverseSubTypes(t: PType): bool = + template error(msg) = localError(genericType.sym.info, msg) + + result = false + + template subresult(r) = + let sub = r + result = result or sub + + case t.kind + of tyGenericParam: + t.sym.flags.incl sfWeakCovariant + return true + + of tyObject: + for field in t.n: + subresult traverseSubTypes(field.typ) + + of tyArray: + return traverseSubTypes(t[1]) + + of tyProc: + for subType in t.sons: + if subType != nil: + subresult traverseSubTypes(subType) + if result: + error("non-invariant type param used in a proc type: " & $t) + + of tySequence: + return traverseSubTypes(t[0]) + + of tyGenericInvocation: + let targetBody = t[0] + for i in 1 .. <t.len: + let param = t[i] + if param.kind == tyGenericParam: + if sfCovariant in param.sym.flags: + let formalFlags = targetBody[i-1].sym.flags + if sfCovariant notin formalFlags: + error("covariant param '" & param.sym.name.s & + "' used in a non-covariant position") + elif sfWeakCovariant in formalFlags: + param.sym.flags.incl sfWeakCovariant + result = true + elif sfContravariant in param.sym.flags: + let formalParam = targetBody[i-1].sym + if sfContravariant notin formalParam.flags: + error("contravariant param '" & param.sym.name.s & + "' used in a non-contravariant position") + result = true + else: + subresult traverseSubTypes(param) + + of tyAnd, tyOr, tyNot, tyStatic, tyBuiltInTypeClass, tyCompositeTypeClass: + error("non-invariant type parameters cannot be used with types such '" & $t & "'") + + of tyUserTypeClass, tyUserTypeClassInst: + error("non-invariant type parameters are not supported in concepts") + + of tyTuple: + for fieldType in t.sons: + subresult traverseSubTypes(fieldType) + + of tyPtr, tyRef, tyVar: + if t.base.kind == tyGenericParam: return true + return traverseSubTypes(t.base) + + of tyDistinct, tyAlias: + return traverseSubTypes(t.lastSon) + + of tyGenericInst: + internalAssert false + + else: + discard + + discard traverseSubTypes(body) + proc typeSectionRightSidePass(c: PContext, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] @@ -782,6 +865,9 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = body.sym = s body.size = -1 # could not be computed properly s.typ.sons[sonsLen(s.typ) - 1] = body + if sfCovariant in s.flags: + checkCovariantParamsUsages(s.typ) + popOwner(c) closeScope(c) elif a.sons[2].kind != nkEmpty: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index ad7e53a4d..e0dedf135 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -136,9 +136,6 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = let n = if n[0].kind == nkBracket: n[0] else: n checkMinSonsLen(n, 1) var t = semTypeNode(c, n.lastSon, nil) - if c.inGenericContext > 0: - if t.sym != nil and sfCovariant in t.sym.flags: - t.sym.flags.incl sfStrongCovariant if t.kind == tyTypeDesc and tfUnresolved notin t.flags: t = t.base result = newOrPrevType(kind, prev, c) @@ -1591,10 +1588,13 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = var covarianceFlag = sfPure if paramName.kind in {nkInTy, nkOutTy}: - if father == nil or sfImportc notin father.sym.flags: - localError(paramName.info, errInOutFlagNotExtern) + if not nimEnableCovariance or paramName.kind == nkInTy: + if father == nil or sfImportc notin father.sym.flags: + localError(paramName.info, errInOutFlagNotExtern, + if paramName.kind == nkInTy: "in" else: "out") covarianceFlag = if paramName.kind == nkInTy: sfContravariant else: sfCovariant + if father != nil: father.sym.flags.incl sfCovariant paramName = paramName[0] var s = if finalType.kind == tyStatic or tfWildcard in typ.flags: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index c2a9b78bf..36975c3db 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -883,7 +883,7 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool = let body = f.base return body == a.base and a.sonsLen == 3 and - sfStrongCovariant in body.sons[0].sym.flags and + sfWeakCovariant notin body.sons[0].sym.flags and baseTypesCheck(f.sons[1], a.sons[1]) else: return false @@ -1305,10 +1305,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let paramFlags = rootf.base.sons[i-1].sym.flags hasCovariance = if sfCovariant in paramFlags: - if sfStrongCovariant in paramFlags: - ff.kind notin {tyRef, tyPtr} and result == isSubtype - else: + if sfWeakCovariant in paramFlags: isCovariantPtr(c, ff, aa) + else: + ff.kind notin {tyRef, tyPtr} and result == isSubtype else: sfContravariant in paramFlags and typeRel(c, aa, ff) == isSubtype |