Skip to content

Commit

Permalink
Merge pull request #967 from shamanas/cross_templates
Browse files Browse the repository at this point in the history
Cross referencing type templates now possible
  • Loading branch information
alexnask committed Dec 9, 2015
2 parents 97baef0 + 64dc149 commit 4de448a
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 3 deletions.
78 changes: 76 additions & 2 deletions source/rock/middle/BaseType.ooc
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import tinker/[Response, Resolver, Trail, Errors]

import Type, Declaration, VariableAccess, VariableDecl, TypeDecl,
InterfaceDecl, Node, ClassDecl, CoverDecl, Cast, FuncType,
FunctionCall, Module, NamespaceDecl, Match
FunctionCall, Module, NamespaceDecl, Match, TemplateDef

/**
* BaseType(s) are types that are neither pointers, nor references.
Expand Down Expand Up @@ -208,7 +208,20 @@ BaseType: class extends Type {
return
}

numDeclTypeArgs := tDecl getTypeArgs() getSize()

templateSize := match (tDecl template) {
case null => match (tDecl templateParent) {
case null => 0
case => match (name != tDecl name) {
case true => tDecl templateArgs getSize()
case => 0
}
}
case => tDecl template typeArgs size
}

numDeclTypeArgs := tDecl getTypeArgs() getSize() + templateSize

numTypeArgs := typeArgs == null ? 0 : typeArgs size

if (numTypeArgs != numDeclTypeArgs) {
Expand Down Expand Up @@ -329,6 +342,67 @@ BaseType: class extends Type {
lhsTypeArgs := getTypeArgs()
rhsTypeArgs := other getTypeArgs()

// If both are template instances then we are lenient about typeArgs.
// We allow the difference of typeArgs to be exactly as many template args
// as there exist.

match ourRef {
case ourTD: TypeDecl =>
match hisRef {
case hisTD: TypeDecl =>
if (ourTD templateParent && hisTD templateParent && ourTD == hisTD) {
// If both have template parents and the same ref, these are of the same instance.
// All we need to do is compare the generics, if any.

ourTArgSize := match lhsTypeArgs {
case null => 0
case => lhsTypeArgs size
}

hisTArgSize := match rhsTypeArgs {
case null => 0
case => rhsTypeArgs size
}

templateCount := ourTD templateParent template typeArgs size
typeArgDiff := (ourTArgSize - hisTArgSize) abs()

weSmaller? := (ourTArgSize - hisTArgSize) < 0

if (typeArgDiff != templateCount && typeArgDiff != 0) {
raise("Internal error: Two types of the same template instance with typeArg count difference #{typeArgDiff} (template count is #{templateCount})")
}

genSize := ourTArgSize - match weSmaller? {
case true => 0
case => typeArgDiff
}

// assert (ourGenSize == hisGenSize)
for (i in 0 .. genSize) {
ourGen := lhsTypeArgs[i]
hisGen := rhsTypeArgs[i]

if (ourGen == null || hisGen == null) {
return -1
}

innerScore := ourGen getScore(hisGen)
if (innerScore < 0) {
if (superType) {
return superType getScore(other)
} else {
return innerScore
}
}
}

// We passed everything with positive scores, let's just call it a perfect match :) (TODO: this should take generic scores into account)
return scoreSeed
}
}
}

// one has, other doesn't? no luck
if ((lhsTypeArgs == null) != (rhsTypeArgs == null)) {
// if (debugCondition()) { token printMessage("one has, other doesn't") }
Expand Down
19 changes: 18 additions & 1 deletion source/rock/middle/TypeDecl.ooc
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,24 @@ TypeDecl: abstract class extends Declaration {
if (theirs inner isGeneric()) {
buffer append(ours getName())
} else {
buffer append(theirs getName())
// So, when we want to pass our template parameters to another type template
// 'theirs' ends up with a ref to a template, but it's name still is 'T' or something similar.
// We want to check that the ref of 'theirs' exists and is a TypeDecl and if it is we use its name.
name := match (theirs inner) {
case bt: BaseType =>
match (bt ref) {
case null =>
bt name
case tDecl: TypeDecl =>
tDecl name
case =>
bt name
}
case =>
theirs getName()
}

buffer append(name)
}
}

Expand Down
10 changes: 10 additions & 0 deletions test/compiler/classes/generic-template-scoring.ooc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//! shouldfail
Foo: class <U> template <T> {
val: U
init: func (=val)
}

takeAFoo: func (foo: Foo<Int, String>) {}

foo := Foo<String, String> new("hi there!")
takeAFoo(foo)
22 changes: 22 additions & 0 deletions test/compiler/covers/cover-template-cross-reference.ooc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

Foo: cover template <T> {
val: T

init: func@ (=val)

bar: func@ -> Bar<T> {
Bar<T> new(this&)
}
}

Bar: cover template <T> {
ref: Foo<T>*

init: func@ (=ref)
}

describe("we should be able to create cross-referencing cover templates", ||
foo := Foo<Int> new(42)

expect(foo&, foo bar() ref)
)

0 comments on commit 4de448a

Please sign in to comment.