From a802d724525628879dd7eda0c97436ccb4084952 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 12 May 2017 12:46:09 +0300 Subject: [PATCH] doh, forgot to add all files in the previous commit --- compiler/ast.nim | 3 +- compiler/semcall.nim | 2 +- compiler/semtypes.nim | 5 +++- compiler/sigmatch.nim | 70 +++++++++++++++++++++++++++++++++++-------- 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index d4ffbcfa4f..4bd76e41eb 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -270,7 +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 + sfCovariant # covariant generic param mimicing seq/array type + sfStrongCovariant # covariant generic param mimicing ptr type sfContravariant # contravariant generic param TSymFlags* = set[TSymFlag] diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 1089ab7db8..4fa4f7f322 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -429,7 +429,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = for i in 1..sonsLen(n)-1: let formal = s.ast.sons[genericParamsPos].sons[i-1].typ let arg = n[i].typ - let tm = typeRel(m, formal, arg, true) + let tm = typeRel(m, formal, arg) if tm in {isNone, isConvertible}: return nil var newInst = generateInstance(c, s, m.bindings, n.info) newInst.typ.flags.excl tfUnresolved diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 5cc5a1ecfa..ad7e53a4d8 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -136,6 +136,9 @@ 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) @@ -1590,9 +1593,9 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = if paramName.kind in {nkInTy, nkOutTy}: if father == nil or sfImportc notin father.sym.flags: localError(paramName.info, errInOutFlagNotExtern) - paramName = paramName[0] covarianceFlag = if paramName.kind == nkInTy: sfContravariant else: sfCovariant + paramName = paramName[0] var s = if finalType.kind == tyStatic or tfWildcard in typ.flags: newSymG(skGenericParam, paramName, c).linkTo(finalType) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index db3775cca4..d68fe6a41b 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -69,6 +69,12 @@ type mutabilityProblem*: uint8 # tyVar mismatch inheritancePenalty: int # to prefer closest father object type + TTypeRelFlag* = enum + trDontBind + trNoCovariance + + TTypeRelFlags* = set[TTypeRelFlag] + TTypeRelation* = enum # order is important! isNone, isConvertible, isIntConv, @@ -296,7 +302,9 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1; add(result, argTypeToString(arg, prefer)) if i != sonsLen(n) - 1: add(result, ", ") -proc typeRel*(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation +proc typeRel*(c: var TCandidate, f, aOrig: PType, + flags: TTypeRelFlags = {}): TTypeRelation + proc concreteType(c: TCandidate, t: PType): PType = case t.kind of tyNil: @@ -860,7 +868,28 @@ template subtypeCheck() = if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar}: result = isNone -proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = +proc isCovariantPtr(c: var TCandidate, f, a: PType): bool = + # this proc is always called for a pair of matching types + assert f.kind == a.kind + + template baseTypesCheck(lhs, rhs: PType): bool = + lhs.kind notin {tyPtr, tyRef, tyVar} and + typeRel(c, lhs, rhs, {trNoCovariance}) == isSubtype + + case f.kind + of tyRef, tyPtr: + return baseTypesCheck(f.base, a.base) + of tyGenericInst: + let body = f.base + return body == a.base and + a.sonsLen == 3 and + sfStrongCovariant in body.sons[0].sym.flags and + baseTypesCheck(f.sons[1], a.sons[1]) + else: + return false + +proc typeRel(c: var TCandidate, f, aOrig: PType, + flags: TTypeRelFlags = {}): TTypeRelation = # typeRel can be used to establish various relationships between types: # # 1) When used with concrete types, it will check for type equivalence @@ -927,6 +956,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isEqual return + template doBind: bool = trDontBind notin flags + # var and static arguments match regular modifier-free types var a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym) # XXX: Theoretically, maybeSkipDistinct could be called before we even @@ -963,7 +994,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219): result = isGeneric for branch in a.sons: - let x = typeRel(c, f, branch, false) + let x = typeRel(c, f, branch, flags + {trDontBind}) if x == isNone: return isNone if x < result: result = x return @@ -974,7 +1005,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # seq[Sortable and Iterable] vs seq[Sortable] # only one match is enough for branch in a.sons: - let x = typeRel(c, f, branch, false) + let x = typeRel(c, f, branch, flags + {trDontBind}) if x != isNone: return if x >= isGeneric: isGeneric else: x return isNone @@ -1001,7 +1032,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyUserTypeClass, tyUserTypeClassInst: # consider this: 'var g: Node' *within* a concept where 'Node' # is a concept too (tgraph) - let x = typeRel(c, a, f, false) + let x = typeRel(c, a, f, flags + {trDontBind}) if x >= isGeneric: return isGeneric else: discard @@ -1047,7 +1078,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyFloat128: result = handleFloatRange(f, a) of tyVar: if aOrig.kind == tyVar: result = typeRel(c, f.base, aOrig.base) - else: result = typeRel(c, f.base, aOrig) + else: result = typeRel(c, f.base, aOrig, flags + {trNoCovariance}) subtypeCheck() of tyArray: case a.kind @@ -1163,7 +1194,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = if a.len < f.len: return isNone for i in 0..f.len-2: if typeRel(c, f.sons[i], a.sons[i]) == isNone: return isNone - result = typeRel(c, f.lastSon, a.lastSon) + result = typeRel(c, f.lastSon, a.lastSon, flags + {trNoCovariance}) subtypeCheck() if result <= isConvertible: result = isNone elif tfNotNil in f.flags and tfNotNil notin a.flags: @@ -1235,15 +1266,30 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = var m = c if a.kind == tyGenericInst: if roota.base == rootf.base: + let nextFlags = flags + {trNoCovariance} + var hasCovariance = false for i in 1 .. rootf.sonsLen-2: let ff = rootf.sons[i] let aa = roota.sons[i] - result = typeRel(c, ff, aa) - if result notin {isEqual, isGeneric}: return isNone - # if ff.kind == tyRange and result != isEqual: return isNone + result = typeRel(c, ff, aa, nextFlags) + if result notin {isEqual, isGeneric}: + if trNoCovariance notin flags and ff.kind == aa.kind: + 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: + isCovariantPtr(c, ff, aa) + else: + sfContravariant in paramFlags and + typeRel(c, aa, ff) == isSubtype + if hasCovariance: + continue + return isNone if prev == nil: put(c, f, a) - result = isGeneric + result = if hasCovariance: isGeneric else: isGeneric else: let fKind = rootf.lastSon.kind if fKind in {tyAnd, tyOr}: @@ -1455,7 +1501,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isNone else: if f.sonsLen > 0 and f.sons[0].kind != tyNone: - result = typeRel(c, f.lastSon, a, false) + result = typeRel(c, f.lastSon, a, flags + {trDontBind}) if doBind and result notin {isNone, isGeneric}: let concrete = concreteType(c, a) if concrete == nil: return isNone