package com.redhat.ceylon.eclipse.code.correct;
import static com.redhat.ceylon.model.typechecker.model.ModelUtil.intersectionType;
import static com.redhat.ceylon.model.typechecker.model.ModelUtil.isTypeUnknown;
import static com.redhat.ceylon.model.typechecker.model.ModelUtil.unionType;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Interface;
import com.redhat.ceylon.model.typechecker.model.Parameter;
import com.redhat.ceylon.model.typechecker.model.Reference;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypedDeclaration;
import com.redhat.ceylon.model.typechecker.model.Unit;
class InferredType {
private Unit unit;
InferredType(Unit unit) {
this.unit = unit;
}
Type inferredType;
Type generalizedType;
void intersect(Type pt) {
if (!isTypeUnknown(pt)) {
if (generalizedType==null) {
generalizedType = pt;
}
else {
Type it =
intersectionType(generalizedType,
pt, unit);
if (!it.isNothing()) {
generalizedType = it;
}
}
}
}
void union(Type pt) {
if (!isTypeUnknown(pt)) {
if (inferredType==null) {
inferredType = pt;
}
else {
inferredType =
unionType(inferredType,
unit.denotableType(pt),
unit);
}
}
}
}
class InferTypeVisitor extends Visitor {
Unit unit;
Declaration dec;
InferredType result;
InferTypeVisitor(Unit unit) {
this.unit = unit;
result = new InferredType(unit);
}
@Override public void visit(Tree.AttributeDeclaration that) {
super.visit(that);
//TODO: an assignment to something with an inferred
// type doesn't _directly_ constrain the type
// ... but _indirectly_ it can!
// if (!(that.getType() instanceof Tree.LocalModifier)) {
Tree.SpecifierOrInitializerExpression sie =
that.getSpecifierOrInitializerExpression();
Tree.Term term = sie==null ?
null : sie.getExpression().getTerm();
if (term instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme =
(Tree.BaseMemberExpression) term;
Declaration d = bme.getDeclaration();
if (d!=null && d.equals(dec)) {
Type t =
that.getType().getTypeModel();
result.intersect(t);
}
}
else if (term!=null) {
if (that.getDeclarationModel().equals(dec)) {
result.union(term.getTypeModel());
}
}
// }
}
@Override public void visit(Tree.MethodDeclaration that) {
super.visit(that);
//TODO: an assignment to something with an inferred
// type doesn't _directly_ constrain the type
// ... but _indirectly_ it can!
// if (!(that.getType() instanceof Tree.LocalModifier)) {
Tree.SpecifierExpression se =
that.getSpecifierExpression();
Tree.Term term = se==null ?
null : se.getExpression().getTerm();
if (term instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme =
(Tree.BaseMemberExpression) term;
Declaration d = bme.getDeclaration();
if (d!=null && d.equals(dec)) {
Type t =
that.getType().getTypeModel();
result.intersect(t);
}
}
else if (term!=null) {
if (that.getDeclarationModel().equals(dec)) {
result.union(term.getTypeModel());
}
}
// }
}
@Override public void visit(Tree.SpecifierStatement that) {
super.visit(that);
Tree.Term bme = that.getBaseMemberExpression();
Tree.SpecifierExpression se =
that.getSpecifierExpression();
Tree.Term term = se==null ?
null : se.getExpression().getTerm();
if (bme instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression ibme =
(Tree.BaseMemberExpression) bme;
Declaration d = ibme.getDeclaration();
if (d!=null && d.equals(dec)) {
if (term!=null)
result.union(term.getTypeModel());
}
}
if (term instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression ibme =
(Tree.BaseMemberExpression) term;
Declaration d = ibme.getDeclaration();
if (d!=null && d.equals(dec)) {
if (bme!=null)
result.intersect(bme.getTypeModel());
}
}
}
@Override public void visit(Tree.AssignmentOp that) {
super.visit(that);
Tree.Term rt = that.getRightTerm();
Tree.Term lt = that.getLeftTerm();
if (lt instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme =
(Tree.BaseMemberExpression) lt;
if (bme.getDeclaration().equals(dec)) {
if (rt!=null)
result.union(rt.getTypeModel());
}
}
if (rt instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme =
(Tree.BaseMemberExpression) rt;
if (bme.getDeclaration().equals(dec)) {
if (lt!=null)
result.intersect(lt.getTypeModel());
}
}
}
private Reference pr;
@Override public void visit(Tree.InvocationExpression that) {
Reference opr=null;
Tree.Primary primary = that.getPrimary();
if (primary!=null) {
if (primary instanceof Tree.MemberOrTypeExpression) {
Tree.MemberOrTypeExpression mte =
(Tree.MemberOrTypeExpression) primary;
pr = mte.getTarget();
}
}
super.visit(that);
pr = opr;
}
@Override public void visit(Tree.ListedArgument that) {
super.visit(that);
Tree.Term t = that.getExpression().getTerm();
if (t instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme =
(Tree.BaseMemberExpression) t;
Declaration d = bme.getDeclaration();
if (d!=null && d.equals(dec)) {
Parameter p = that.getParameter();
if (p!=null && pr!=null) {
Type ft =
pr.getTypedParameter(p)
.getFullType();
if (p.isSequenced()) {
ft = unit.getIteratedType(ft);
}
result.intersect(ft);
}
}
}
}
@Override public void visit(Tree.SpreadArgument that) {
super.visit(that);
Tree.Term t = that.getExpression().getTerm();
if (t instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme =
(Tree.BaseMemberExpression) t;
Declaration d = bme.getDeclaration();
if (d!=null && d.equals(dec)) {
Parameter p = that.getParameter();
if (p!=null && pr!=null) {
//TODO: is this correct?
Type ft =
pr.getTypedParameter(p)
.getFullType();
Type et =
unit.getIteratedType(ft);
Type it =
unit.getIterableType(et);
result.intersect(it);
}
}
}
}
@Override public void visit(Tree.SpecifiedArgument that) {
super.visit(that);
Tree.Term t =
that.getSpecifierExpression()
.getExpression().getTerm();
if (t instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme =
(Tree.BaseMemberExpression) t;
Declaration d = bme.getDeclaration();
if (d!=null && d.equals(dec)) {
Parameter p = that.getParameter();
if (p!=null && pr!=null) {
Type ft =
pr.getTypedParameter(p)
.getFullType();
result.intersect(ft);
}
}
}
}
@Override public void visit(Tree.Return that) {
super.visit(that);
Tree.Term bme = that.getExpression().getTerm();
if (bme instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression ibme =
(Tree.BaseMemberExpression) bme;
Declaration bmed = ibme.getDeclaration();
if (bmed!=null && bmed.equals(dec)) {
Declaration d = that.getDeclaration();
if (d instanceof TypedDeclaration) {
TypedDeclaration td =
(TypedDeclaration) d;
result.intersect(td.getType());
}
}
}
else if (bme!=null) {
if (that.getDeclaration().equals(dec)) {
result.union(bme.getTypeModel());
}
}
}
@Override
public void visit(Tree.QualifiedMemberOrTypeExpression that) {
super.visit(that);
Tree.Primary primary = that.getPrimary();
if (primary instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme =
(Tree.BaseMemberExpression) primary;
Declaration bmed = bme.getDeclaration();
if (bmed!=null && bmed.equals(dec)) {
TypeDeclaration td = (TypeDeclaration)
that.getDeclaration()
.getRefinedDeclaration()
.getContainer();
Type st =
that.getTarget()
.getQualifyingType()
.getSupertype(td);
result.intersect(st);
}
}
}
/*@Override
public void visit(Tree.PatternIterator that) {
super.visit(that);
Tree.Term primary = that.getSpecifierExpression().getExpression().getTerm();
if (primary instanceof Tree.BaseMemberExpression) {
Declaration bmed = ((Tree.BaseMemberExpression) primary).getDeclaration();
if (bmed!=null && bmed.equals(dec)) {
Type kt = that.getKeyVariable().getType().getTypeModel();
Type vt = that.getValueVariable().getType().getTypeModel();
result.intersect(that.getUnit().getIterableType(that.getUnit().getEntryType(kt, vt)));
}
}
}*/
@Override
public void visit(Tree.ValueIterator that) {
super.visit(that);
Tree.Term primary =
that.getSpecifierExpression()
.getExpression().getTerm();
if (primary instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme =
(Tree.BaseMemberExpression) primary;
Declaration bmed = bme.getDeclaration();
if (bmed!=null && bmed.equals(dec)) {
Type vt =
that.getVariable().getType()
.getTypeModel();
Type it = unit.getIterableType(vt);
result.intersect(it);
}
}
}
@Override
public void visit(Tree.BooleanCondition that) {
super.visit(that);
Tree.Term primary = that.getExpression().getTerm();
if (primary instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme =
(Tree.BaseMemberExpression) primary;
Declaration bmed = bme.getDeclaration();
if (bmed!=null && bmed.equals(dec)) {
Type bt =
unit.getBooleanDeclaration()
.getType();
result.intersect(bt);
}
}
}
@Override
public void visit(Tree.NonemptyCondition that) {
super.visit(that);
Tree.Statement s = that.getVariable();
if (s instanceof Tree.Variable) {
Tree.Variable var = (Tree.Variable) s;
Tree.Term primary =
var.getSpecifierExpression()
.getExpression().getTerm();
if (primary instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme =
(Tree.BaseMemberExpression) primary;
Declaration bmed = bme.getDeclaration();
if (bmed!=null && bmed.equals(dec)) {
Type vt =
var.getType().getTypeModel();
Type et =
unit.getSequentialElementType(vt);
Type st =
unit.getSequentialType(et);
result.intersect(st);
}
}
}
}
@Override
public void visit(Tree.ArithmeticOp that) {
super.visit(that);
Interface sd = getArithmeticDeclaration(that);
genericOperatorTerm(sd, that.getLeftTerm());
genericOperatorTerm(sd, that.getRightTerm());
}
@Override
public void visit(Tree.NegativeOp that) {
super.visit(that);
Interface sd = unit.getInvertableDeclaration();
genericOperatorTerm(sd, that.getTerm());
}
@Override
public void visit(Tree.PrefixOperatorExpression that) {
super.visit(that);
Interface sd = unit.getOrdinalDeclaration();
genericOperatorTerm(sd, that.getTerm());
}
@Override
public void visit(Tree.PostfixOperatorExpression that) {
super.visit(that);
Interface sd = unit.getOrdinalDeclaration();
genericOperatorTerm(sd, that.getTerm());
}
@Override
public void visit(Tree.BitwiseOp that) {
super.visit(that);
Interface sd = unit.getSetDeclaration();
genericOperatorTerm(sd, that.getLeftTerm());
genericOperatorTerm(sd, that.getRightTerm());
}
@Override
public void visit(Tree.ComparisonOp that) {
super.visit(that);
Interface sd = unit.getComparableDeclaration();
genericOperatorTerm(sd, that.getLeftTerm());
genericOperatorTerm(sd, that.getRightTerm());
}
@Override
public void visit(Tree.CompareOp that) {
super.visit(that);
Interface sd = unit.getComparableDeclaration();
genericOperatorTerm(sd, that.getLeftTerm());
genericOperatorTerm(sd, that.getRightTerm());
}
@Override
public void visit(Tree.LogicalOp that) {
super.visit(that);
TypeDeclaration sd = unit.getBooleanDeclaration();
operatorTerm(sd, that.getLeftTerm());
operatorTerm(sd, that.getRightTerm());
}
@Override
public void visit(Tree.NotOp that) {
super.visit(that);
TypeDeclaration sd = unit.getBooleanDeclaration();
operatorTerm(sd, that.getTerm());
}
@Override
public void visit(Tree.EntryOp that) {
super.visit(that);
TypeDeclaration sd = unit.getObjectDeclaration();
operatorTerm(sd, that.getLeftTerm());
operatorTerm(sd, that.getRightTerm());
}
private Interface getArithmeticDeclaration(Tree.ArithmeticOp that) {
if (that instanceof Tree.PowerOp) {
return unit.getExponentiableDeclaration();
}
else if (that instanceof Tree.SumOp) {
return unit.getSummableDeclaration();
}
else if (that instanceof Tree.DifferenceOp) {
return unit.getInvertableDeclaration();
}
else if (that instanceof Tree.RemainderOp) {
return unit.getIntegralDeclaration();
}
else {
return unit.getNumericDeclaration();
}
}
public void operatorTerm(TypeDeclaration sd, Tree.Term lhs) {
if (lhs instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme =
(Tree.BaseMemberExpression) lhs;
Declaration bmed = bme.getDeclaration();
if (bmed!=null && bmed.equals(dec)) {
result.intersect(sd.getType());
}
}
}
public void genericOperatorTerm(TypeDeclaration sd, Tree.Term lhs) {
if (lhs instanceof Tree.BaseMemberExpression) {
Tree.BaseMemberExpression bme =
(Tree.BaseMemberExpression) lhs;
Declaration bmed = bme.getDeclaration();
if (bmed!=null && bmed.equals(dec)) {
Type st =
lhs.getTypeModel().getSupertype(sd);
Type at =
st.getTypeArguments().get(0);
result.intersect(at);
}
}
}
//TODO: more operator expressions!
}