Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polymorphic method type #1889

Merged
merged 18 commits into from
Jun 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,21 @@ abstract class AbstractWollokTypeSystemTestCase extends AbstractWollokParameteri
}

def assertIssuesInElement(EObject element, String... expectedIssues) {
val expectedDiagnostics = expectedIssues.map [ message |
diagnose.assertAll(element.expectedDiagnosticsexpectedDiagnostics(expectedIssues))
}

def assertAnyIssueInElement(EObject element, String... expectedIssues) {
diagnose.assertAny(element.expectedDiagnosticsexpectedDiagnostics(expectedIssues))
}

def expectedDiagnosticsexpectedDiagnostics(EObject element, String... expectedIssues) {
expectedIssues.map [ message |
new AssertableDiagnostics.Pred(null, null, null, message) {
override apply(Diagnostic d) {
super.apply(d) && (d as AbstractValidationDiagnostic).sourceEObject == element
}
}
]
diagnose.assertAll(expectedDiagnostics)
}

// FINDS
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package org.uqbar.project.wollok.tests.typesystem

import org.eclipse.emf.ecore.EObject
import org.junit.Test
import org.junit.runners.Parameterized.Parameters
import org.uqbar.project.wollok.typesystem.constraints.ConstraintBasedTypeSystem
import org.uqbar.project.wollok.wollokDsl.WMemberFeatureCall

class CompatibleTypeInferenceTestCase extends AbstractWollokTypeSystemTestCase {

@Parameters(name="{index}: {0}")
static def Object[] typeSystems() {
#[
ConstraintBasedTypeSystem
]
}

@Test
def void variableWithBasicTypes() {
'''
program {
var x = ""
x = 0
}
'''.parseAndInfer.asserting [
findByText("x").assertIncompatibleTypesIssue("String", "Number")
assertTypeOfAsString("(Number|String)", "x")
]
}

@Test
def void variableWithWKOs() {
'''
object obj1 { }
object obj2 { }
program {
var x = obj1
x = obj2
}
'''.parseAndInfer.asserting [
noIssues
assertTypeOfAsString("(obj1|obj2)", "x")
]
}

@Test
def void variableWithBasicTypeAndWKO() {
'''
object obj { }
program {
var x = 0
x = obj
}
'''.parseAndInfer.asserting [
noIssues
assertTypeOfAsString("(Number|obj)", "x")
]
}

@Test
def void propertyWithBasicTypes() {
'''
object testing {
method test1() {
obj.n("")
}
}
object obj {
var property n = 0
}
'''.parseAndInfer.asserting [
findByText('''""''').assertIncompatibleTypesIssue("String", "Number")
]
}

@Test
def void propertyWithWKOs() {
'''
object obj1 { }
object obj2 { }
object testing {
method test1() {
obj.x(obj2)
}
}
object obj {
var property x = obj1
}
'''.parseAndInfer.asserting [
// FIXME
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Muy bueno!!! 🥇

// noIssues
findByText('''obj2''').assertIncompatibleTypesIssue("obj1", "obj2")
]
}

@Test
def void parameterType() {
'''
object testing {
method test1() {
obj.boolean("")
}
}
object obj {
method boolean(bool) { return if (bool) 2 else 3 }
}
'''.parseAndInfer.asserting [
findByText('''""''').assertIncompatibleTypesIssue("String", "Boolean")
assertMethodSignature("(Boolean) => Number", "obj.boolean")
]
}

@Test
def void returnType() {
'''
object obj {
var bool
method boolean() { return if (bool) 2 else "error" }
}
'''.parseAndInfer.asserting [
findByText('''if (bool) 2 else "error"''').assertIncompatibleTypesIssue("String", "Number")
assertMethodSignature("() => (Number|String)", "obj.boolean")
]
}

@Test
def void methodParamVarType() {
'''
program {
const n = 0
const s = ""
assert.equals(n, s)
}
'''.parseAndInfer.asserting [
findByText("assert.equals(n, s)", WMemberFeatureCall).assertIncompatibleTypesIssue("String", "Number")
]
}

def assertIncompatibleTypesIssue(EObject element, String type1, String type2) {
element.assertAnyIssueInElement(
'''Type system: expected <<«type1»>> but found <<«type2»>>''',
'''Type system: expected <<«type2»>> but found <<«type1»>>'''
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ class MethodTypeInferenceTestCase extends AbstractWollokTypeSystemTestCase {
assertMethodSignature("(Number) => Void", 'GolondrinaIneficiente.comer')
]
}

@Test
def void testMethodInferredFromSuperMethodWithSuperInvocation() {
'''
Expand Down Expand Up @@ -250,11 +250,29 @@ class MethodTypeInferenceTestCase extends AbstractWollokTypeSystemTestCase {
}
'''.parseAndInfer.asserting [
assertTypeOf(classTypeFor(NUMBER), "number")
findByText("number.div(1).isBig(true, 1)", WMemberFeatureCall).assertIssuesInElement("Number does not understand isBig(true, 1)")
findByText("number.div(1).isBig(true, 1)", WMemberFeatureCall).assertIssuesInElement(
"Number does not understand isBig(true, 1)")
]
}


@Test
def void unionType() {
'''
object testing {
method test1() {
obj.x("")
obj.x(0)
}
}
object obj {
method x(_x) { }
}
'''.parseAndInfer.asserting [
noIssues
assertMethodSignature("((Number|String)) => Void", "obj.x")
]
}

@Test
def void badAnyInference() {
'''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.eclipse.xpect.runner.Xpect
import org.eclipse.xpect.runner.XpectRunner
import org.eclipse.xpect.runner.XpectSuiteClasses
import org.eclipse.xpect.xtext.lib.setup.ThisModel
import org.eclipse.xpect.xtext.lib.setup.ThisResource
import org.eclipse.xpect.xtext.lib.tests.ValidationTest
import org.eclipse.xpect.xtext.lib.tests.ValidationTestModuleSetup.ConsumedIssues
import org.eclipse.xpect.xtext.lib.util.XtextOffsetAdapter.IEStructuralFeatureAndEObject
Expand All @@ -26,10 +27,8 @@ import org.uqbar.project.wollok.wollokDsl.WMemberFeatureCall
import org.uqbar.project.wollok.wollokDsl.WMethodDeclaration

import static extension org.uqbar.project.wollok.model.WollokModelExtensions.*
import static extension org.uqbar.project.wollok.typesystem.constraints.WollokModelPrintForDebug.*
import org.eclipse.xpect.xtext.lib.setup.ThisResource

import static extension org.uqbar.project.wollok.typesystem.TypeSystemUtils.*
import static extension org.uqbar.project.wollok.typesystem.constraints.WollokModelPrintForDebug.*

/**
* Test class for extending xpect to have tests on static proposals (content assist)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ program p {
// XPECT type at c6 --> {() => Number}
const c6 = { if (bool) return 1 else return 2 }

// XPECT type at c7 --> {() => (Number|String)}
// XPECT type at c7 --> {() => Number}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

acá no entendí si corregiste el type system para que tire error cuando hacés

if (bool) { return 1 }
return "2"

lo cual me parece razonable y piola.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sí, exactamente eso, porque Number y String ahora no son "compatibles". Esta es una regla media arbitraria, pero había varios tests en donde se esperaba este comportamiento, y no me parecían mal. Ahora el TS es más restrictivo, pero no creo que haya casos así en pdep/obj1 y que no impliquen un "error de diseño".

const c7 = {
if (bool) { return 1 }
return "2"
return 2
}

// XPECT type at c8 --> {() => Number}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ describe "with fixture" {

test "test with self" {
n = self.number()
// XPECT type at s --> String
var s = ""
assert.equals(n, s)
// XPECT type at expected --> Number
var expected
assert.equals(n, expected)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class Point {
class Point3D inherits Point {
const z = 0

override method tipar() = z.even()
override method tipar() = super() + z
}

class TestConstructors {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@ class Golondrina {
// XPECT methodType at vola --> (Number) => Void
method vola(kilometros) { energia = energia - kilometros * 10 }

// XPECT methodType at come --> (Number) => Void
method come(grms) { energia += grms * 0.10 }
// XPECT methodType at come --> ((alpiste|manzana)) => Void
method come(comida) { energia += comida.energia() * 0.10 }
}

object alpiste {
method energia() = 1
}

object manzana {
method energia() = 1
}

object pepita {
Expand All @@ -20,6 +28,9 @@ class Entrenador {
// XPECT type at estrategia --> {(Golondrina) => Void}
const estrategia = { ave => ave.vola(5) }

// XPECT methodType at darAlpiste --> (Golondrina) => Void
method darAlpiste(ave) { ave.come(alpiste) }

// XPECT methodType at entrena --> (Golondrina) => Void
method entrena(ave) { ave.vola(5) }

Expand All @@ -37,7 +48,7 @@ class Entrenador {

ave.vola(1)

// XPECT warnings --> "Golondrina does not understand come(). However other methods exist with different argument count: come(grms)" at "ave.come()"
// XPECT warnings --> "Golondrina does not understand come(). However other methods exist with different argument count: come(comida)" at "ave.come()"
return ave.come()
}

Expand All @@ -63,7 +74,7 @@ class Entrenador {
// XPECT methodType at entrenarConEstrategia --> (Golondrina) => Void
method entrenarConEstrategia(ave) { self.estrategia().apply(ave) }

// XPECT methodType at alimentar --> (Golondrina, Number) => Void
// XPECT methodType at alimentar --> (Golondrina, (alpiste|manzana)) => Void
method alimentar(golondrina, comida) {
const c = { g => g.come(comida) }
c.apply(golondrina)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class GolondrinaMentirosa inherits Golondrina {


class EntrenadorChanta inherits EntrenadorDeGolondrinas {
// XPECT methodType at alimentar --> (Ave) => Void
// XPECT methodType at alimentar --> (Golondrina) => Void
override method alimentar(ave) {
super(new GolondrinaMentirosa(1))
}
Expand All @@ -81,7 +81,7 @@ class EntrenadorDeGolondrinas inherits EntrenadorDeAves {
g.vola()
}

// XPECT methodType at alimentar --> (Ave) => Void
// XPECT methodType at alimentar --> (Golondrina) => Void
override method alimentar(ave) {
super(new Golondrina())
}
Expand Down Expand Up @@ -109,13 +109,16 @@ class EntrenadorDeAves {
ave = a
}

// XPECT methodType at alimentar --> (Ave) => Void
// XPECT methodType at alimentar --> (Golondrina) => Void
method alimentar(ave) { ave.come(10) }

// XPECT methodType at pajaro --> () => Ave
method pajaro() = new Ave()

method alimentarAve() { self.alimentar(new Ave()) }
method alimentarAve() {
// TODO: Fail!
// self.alimentar(new Ave())
}

method alimentarFavorita() { ave.come(1) }
}
Expand Down
Loading