/*
* #%~
* The VDM Type Checker
* %%
* Copyright (C) 2008 - 2014 Overture
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #~%
*/
package org.overture.typechecker.assistant.definition;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import org.overture.ast.analysis.AnalysisException;
import org.overture.ast.analysis.QuestionAnswerAdaptor;
import org.overture.ast.assistant.IAstAssistant;
import org.overture.ast.definitions.ABusClassDefinition;
import org.overture.ast.definitions.AClassInvariantDefinition;
import org.overture.ast.definitions.ACpuClassDefinition;
import org.overture.ast.definitions.AExplicitOperationDefinition;
import org.overture.ast.definitions.AInheritedDefinition;
import org.overture.ast.definitions.APerSyncDefinition;
import org.overture.ast.definitions.ASystemClassDefinition;
import org.overture.ast.definitions.AValueDefinition;
import org.overture.ast.definitions.PDefinition;
import org.overture.ast.definitions.SClassDefinition;
import org.overture.ast.factory.AstFactory;
import org.overture.ast.intf.lex.ILexLocation;
import org.overture.ast.intf.lex.ILexNameToken;
import org.overture.ast.lex.LexNameToken;
import org.overture.ast.patterns.PPattern;
import org.overture.ast.statements.PStm;
import org.overture.ast.typechecker.ClassDefinitionSettings;
import org.overture.ast.typechecker.NameScope;
import org.overture.ast.typechecker.Pass;
import org.overture.ast.types.AClassType;
import org.overture.ast.types.AOperationType;
import org.overture.ast.types.PType;
import org.overture.typechecker.Environment;
import org.overture.typechecker.FlatCheckedEnvironment;
import org.overture.typechecker.FlatEnvironment;
import org.overture.typechecker.TypeCheckInfo;
import org.overture.typechecker.TypeCheckerErrors;
import org.overture.typechecker.assistant.ITypeCheckerAssistantFactory;
public class SClassDefinitionAssistantTC implements IAstAssistant
{
protected static ITypeCheckerAssistantFactory af;
@SuppressWarnings("static-access")
public SClassDefinitionAssistantTC(ITypeCheckerAssistantFactory af)
{
this.af = af;
}
public boolean hasSupertype(SClassDefinition classDefinition, PType other)
{
if (af.createPTypeAssistant().equals(getType(classDefinition), other))
{
return true;
} else
{
for (PType type : classDefinition.getSupertypes())
{
AClassType sclass = (AClassType) type;
if (af.createAClassTypeAssistant().hasSupertype(sclass, other))
{
return true;
}
}
}
return false;
}
public boolean isAccessible(Environment env, PDefinition field,
boolean needStatic)
{
SClassDefinition self = env.findClassDefinition();
SClassDefinition target = field.getClassDefinition();
if (self == null) // Not called from within a class member
{
// We're outside, so just public access
return af.createPAccessSpecifierAssistant().isPublic(field.getAccess());
} else
{
AClassType selftype = (AClassType) getType(self);
AClassType targtype = (AClassType) getType(target);
if (!af.createPTypeAssistant().equals(selftype, targtype))
{
if (af.createAClassTypeAssistant().hasSupertype(selftype, targtype))
{
// We're a subclass, so see public or protected
return !af.createPAccessSpecifierAssistant().isPrivate(field.getAccess());
} else
{
// We're outside, so just public/static access
return af.createPAccessSpecifierAssistant().isPublic(field.getAccess())
&& (needStatic ? af.createPAccessSpecifierAssistant().isStatic(field.getAccess())
: true);
}
} else
{
// else same type, so anything goes
return true;
}
}
}
// public static PDefinition findType(SClassDefinition classdef,
// ILexNameToken sought, String fromModule)
// {
// // FIXME: This method is used and outside the TypeFinder visitor so I can't delete it!
// // It is used in this class "public class PrivateClassEnvironment"
// // How do I proceed in this case?
// if (!sought.getExplicit()
// && sought.getName().equals(classdef.getName().getName())
// || sought.equals(classdef.getName().getClassName()))
// {
// return classdef; // Class referred to as "A" or "CLASS`A"
// }
//
// PDefinition def = PDefinitionAssistantTC.findType(classdef.getDefinitions(), sought, null);
//
// if (def == null)
// {
// for (PDefinition d : classdef.getAllInheritedDefinitions())
// {
// PDefinition indef = PDefinitionAssistantTC.findType(d, sought, null);
//
// if (indef != null)
// {
// def = indef;
// break;
// }
// }
// }
//
// return def;
// }
public Set<PDefinition> findMatches(SClassDefinition classdef,
ILexNameToken sought)
{
Set<PDefinition> set = af.createPDefinitionListAssistant().findMatches(classdef.getDefinitions(), sought);
set.addAll(af.createPDefinitionListAssistant().findMatches(classdef.getAllInheritedDefinitions(), sought));
return set;
}
public PDefinition findName(List<SClassDefinition> classes,
ILexNameToken name, NameScope scope)
{
SClassDefinition d = get(classes, name.getModule());
if (d != null)
{
PDefinition def = af.createPDefinitionAssistant().findName(d, name, scope);
if (def != null)
{
return def;
}
}
return null;
}
private SClassDefinition get(List<SClassDefinition> classes, String module)
{
for (SClassDefinition sClassDefinition : classes)
{
if (sClassDefinition.getName().getName().equals(module))
{
return sClassDefinition;
}
}
return null;
}
public PDefinition findType(List<SClassDefinition> classes,
ILexNameToken name)
{
for (SClassDefinition d : classes)
{
PDefinition def = af.createPDefinitionAssistant().findType(d, name, null);
if (def != null)
{
return def;
}
}
return null;
}
public Set<PDefinition> findMatches(List<SClassDefinition> classes,
ILexNameToken name)
{
Set<PDefinition> set = new HashSet<PDefinition>();
for (SClassDefinition d : classes)
{
set.addAll(af.createSClassDefinitionAssistant().findMatches(d, name));
}
return set;
}
public void unusedCheck(List<SClassDefinition> classes)
{
for (SClassDefinition d : classes)
{
af.createPDefinitionAssistant().unusedCheck(d);
}
}
// public static List<PDefinition> getLocalDefinitions(
// SClassDefinition classDefinition)
// {
//
// List<PDefinition> all = new Vector<PDefinition>();
//
// all.addAll(classDefinition.getLocalInheritedDefinitions());
// all.addAll(PDefinitionListAssistantTC.singleDefinitions(classDefinition.getDefinitions()));
//
// return all;
// }
public void implicitDefinitions(SClassDefinition d,
Environment publicClasses)
{
if (d instanceof ASystemClassDefinition)
{
af.createPDefinitionAssistant().implicitDefinitions(d, publicClasses);
// ASystemClassDefinitionAssistantTC.implicitDefinitions((ASystemClassDefinition) d, );
} else
{
implicitDefinitionsBase(d, publicClasses);
}
}
public void implicitDefinitionsBase(SClassDefinition d,
Environment publicClasses)
{
setInherited(d, publicClasses);
setInheritedDefinitions(d);
AExplicitOperationDefinition invariant = getInvDefinition(d);
d.setInvariant(invariant);
if (invariant != null)
{
af.createPDefinitionAssistant().setClassDefinition(invariant, d);
}
}
private AExplicitOperationDefinition getInvDefinition(SClassDefinition d)
{
List<PDefinition> invdefs = getInvDefs(d);
if (invdefs.isEmpty())
{
return null;
}
// Location of last local invariant
ILexLocation invloc = invdefs.get(invdefs.size() - 1).getLocation();
AOperationType type = AstFactory.newAOperationType(invloc, new Vector<PType>(), AstFactory.newABooleanBasicType(invloc));
type.setPure(true);
LexNameToken invname = new LexNameToken(d.getName().getName(), "inv_"
+ d.getName().getName(), invloc);
PStm body = AstFactory.newAClassInvariantStm(invname, invdefs);
return AstFactory.newAExplicitOperationDefinition(invname, type, new Vector<PPattern>(), null, null, body);
}
public List<PDefinition> getInvDefs(SClassDefinition def)
{
List<PDefinition> invdefs = new Vector<PDefinition>();
if (def.getGettingInvDefs())
{
// reported elsewhere
return invdefs;
}
def.setGettingInvDefs(true);
for (SClassDefinition d : def.getSuperDefs())
{
invdefs.addAll(getInvDefs(d));
}
for (PDefinition d : def.getDefinitions())
{
if (d instanceof AClassInvariantDefinition)
{
invdefs.add(d);
}
}
def.setGettingInvDefs(false);
return invdefs;
}
private void setInheritedDefinitions(SClassDefinition definition)
{
List<PDefinition> indefs = new Vector<PDefinition>();
for (SClassDefinition sclass : definition.getSuperDefs())
{
indefs.addAll(getInheritable(sclass));
}
// The inherited definitions are ordered such that the
// definitions, taken in order, will consider the overriding
// members before others.
List<PDefinition> superInheritedDefinitions = new Vector<PDefinition>();
for (PDefinition d : indefs)
{
superInheritedDefinitions.add(d);
ILexNameToken localname = d.getName().getModifiedName(definition.getName().getName());
if (af.createPDefinitionListAssistant().findName(definition.getDefinitions(), localname, NameScope.NAMESANDSTATE) == null ||
af.createPDefinitionAssistant().isSubclassResponsibility(d))
{
AInheritedDefinition local = AstFactory.newAInheritedDefinition(localname, d);
definition.getLocalInheritedDefinitions().add(local);
}
}
definition.setSuperInheritedDefinitions(superInheritedDefinitions);
definition.setAllInheritedDefinitions(new Vector<PDefinition>());
definition.getAllInheritedDefinitions().addAll(superInheritedDefinitions);
definition.getAllInheritedDefinitions().addAll(definition.getLocalInheritedDefinitions());
}
private List<PDefinition> getInheritable(SClassDefinition def)
{
List<PDefinition> defs = new Vector<PDefinition>();
if (def.getGettingInheritable())
{
TypeCheckerErrors.report(3009, "Circular class hierarchy detected: "
+ def.getName(), def.getLocation(), def);
return defs;
}
def.setGettingInheritable(true);
// The inherited definitions are ordered such that the
// definitions, taken in order, will consider the overriding
// members before others. So we add the local definitions
// before the inherited ones.
List<PDefinition> singles = af.createPDefinitionListAssistant().singleDefinitions(def.getDefinitions());
for (PDefinition d : singles)
{
if (!af.createPAccessSpecifierAssistant().isPrivate(d.getAccess()))
{
defs.add(d);
}
}
for (SClassDefinition sclass : def.getSuperDefs())
{
List<PDefinition> sdefs = getInheritable(sclass);
for (PDefinition d : sdefs)
{
defs.add(d);
ILexNameToken localname = d.getName().getModifiedName(def.getName().getName());
if (af.createPDefinitionListAssistant().findName(defs, localname, NameScope.NAMESANDSTATE) == null)
{
AInheritedDefinition local = AstFactory.newAInheritedDefinition(localname, d);
defs.add(local);
}
}
}
def.setGettingInheritable(false);
return defs;
}
private void setInherited(SClassDefinition d, Environment base)
{
switch (d.getSettingHierarchy())
{
case UNSET:
d.setSettingHierarchy(ClassDefinitionSettings.INPROGRESS);
break;
case INPROGRESS:
TypeCheckerErrors.report(3002, "Circular class hierarchy detected: "
+ d.getName(), d.getLocation(), d);
return;
case DONE:
return;
}
af.createPDefinitionListAssistant().implicitDefinitions(d.getDefinitions(), base);
for (ILexNameToken supername : d.getSupernames())
{
PDefinition def = base.findType(supername, null);
if (def == null)
{
TypeCheckerErrors.report(3003, "Undefined superclass: "
+ supername, d.getLocation(), d);
} else if (def instanceof ACpuClassDefinition)
{
TypeCheckerErrors.report(3298, "Cannot inherit from CPU", d.getLocation(), d);
} else if (def instanceof ABusClassDefinition)
{
TypeCheckerErrors.report(3299, "Cannot inherit from BUS", d.getLocation(), d);
} else if (def instanceof ASystemClassDefinition)
{
TypeCheckerErrors.report(3278, "Cannot inherit from system class "
+ supername, d.getLocation(), d);
} else if (def instanceof SClassDefinition)
{
SClassDefinition superdef = (SClassDefinition) def;
setInherited(superdef, base);
d.getSuperDefs().add(superdef);
d.getSupertypes().add(af.createPDefinitionAssistant().getType(superdef));
} else
{
TypeCheckerErrors.report(3004, "Superclass name is not a class: "
+ supername, d.getLocation(), d);
}
}
d.setSettingHierarchy(ClassDefinitionSettings.DONE);
return;
}
public void typeResolve(SClassDefinition d,
QuestionAnswerAdaptor<TypeCheckInfo, PType> rootVisitor,
TypeCheckInfo question) throws AnalysisException
{
Environment cenv = new FlatEnvironment(question.assistantFactory, d.getDefinitions(), question.env);
af.createPDefinitionListAssistant().typeResolve(d.getDefinitions(), rootVisitor, new TypeCheckInfo(question.assistantFactory, cenv));
}
public PDefinition findThread(SClassDefinition d)
{
// return SClassDefinitionAssistantTC.findName(d, d.getName().getThreadName(), NameScope.NAMES);
return af.createPDefinitionAssistant().findName(d, d.getName().getThreadName(), NameScope.NAMES);
}
public PDefinition findConstructor(SClassDefinition classdef,
List<PType> argtypes)
{
LexNameToken constructor = getCtorName(classdef, argtypes);
return af.createPDefinitionAssistant().findName(classdef, constructor, NameScope.NAMES);
}
public static LexNameToken getCtorName(SClassDefinition classdef,
List<PType> argtypes)
{
ILexNameToken name = classdef.getName();
LexNameToken cname = new LexNameToken(name.getName(), name.getName(), classdef.getLocation());
cname.setTypeQualifier(argtypes);
return cname;
}
public PType getType(SClassDefinition def)
{
if (def.getClasstype() == null)
{
def.setClasstype(AstFactory.newAClassType(def.getLocation(), def));
}
return def.getClasstype();
}
public void checkOver(SClassDefinition c)
{
int inheritedThreads = 0;
af.createSClassDefinitionAssistant().checkOverloads(c);
List<List<PDefinition>> superlist = new Vector<List<PDefinition>>();
for (PDefinition def : c.getSuperDefs())
{
SClassDefinition superdef = (SClassDefinition) def;
List<PDefinition> inheritable = af.createSClassDefinitionAssistant().getInheritable(superdef);
superlist.add(inheritable);
if (checkOverrides(c, inheritable))
{
inheritedThreads++;
}
}
if (inheritedThreads > 1)
{
TypeCheckerErrors.report(3001, "Class inherits thread definition from multiple supertypes", c.getLocation(), c);
}
checkAmbiguities(c, superlist);
}
private void checkAmbiguities(SClassDefinition c,
List<List<PDefinition>> superlist)
{
int count = superlist.size();
for (int i = 0; i < count; i++)
{
List<PDefinition> defs = superlist.get(i);
for (int j = i + 1; j < count; j++)
{
List<PDefinition> defs2 = superlist.get(j);
checkAmbiguities(c, defs, defs2);
}
}
}
private void checkAmbiguities(SClassDefinition c, List<PDefinition> defs,
List<PDefinition> defs2)
{
for (PDefinition indef : defs)
{
ILexNameToken localName = indef.getName().getModifiedName(c.getName().getName());
for (PDefinition indef2 : defs2)
{
if (!indef.getLocation().equals(indef2.getLocation())
&& af.createPDefinitionAssistant().kind(indef).equals(af.createPDefinitionAssistant().kind(indef2)))
{
ILexNameToken localName2 = indef2.getName().getModifiedName(c.getName().getName());
if (af.getLexNameTokenAssistant().isEqual(localName, localName2))
{
PDefinition override = af.createPDefinitionListAssistant().findName(c.getDefinitions(), localName, NameScope.NAMESANDSTATE);
if (override == null) // OK if we override the ambiguity
{
TypeCheckerErrors.report(3276, "Ambiguous definitions inherited by "
+ c.getName().getName(), c.getLocation(), c);
TypeCheckerErrors.detail("1", indef.getName() + " "
+ indef.getLocation());
TypeCheckerErrors.detail("2", indef2.getName()
+ " " + indef2.getLocation());
}
}
}
}
}
}
private boolean checkOverrides(SClassDefinition c,
List<PDefinition> inheritable)
{
boolean inheritedThread = false;
for (PDefinition indef : inheritable)
{
if (indef.getName().getName().equals("thread"))
{
inheritedThread = true;
continue; // No other checks needed for threads
}
ILexNameToken localName = indef.getName().getModifiedName(c.getName().getName());
PDefinition override = af.createPDefinitionListAssistant().findName(c.getDefinitions(), localName, NameScope.NAMESANDSTATE);
if (override == null)
{
override = af.createPDefinitionListAssistant().findType(c.getDefinitions(), localName, null);
}
if (override != null)
{
if (!af.createPDefinitionAssistant().kind(indef).equals(af.createPDefinitionAssistant().kind(override)))
{
TypeCheckerErrors.report(3005, "Overriding a superclass member of a different kind: "
+ override.getName(), override.getName().getLocation(), override);
TypeCheckerErrors.detail2("This", af.createPDefinitionAssistant().kind(override), "Super", af.createPDefinitionAssistant().kind(indef));
} else if (af.createPAccessSpecifierAssistant().narrowerThan(override.getAccess(), indef.getAccess()))
{
TypeCheckerErrors.report(3006, "Overriding definition reduces visibility", override.getName().getLocation(),override);
TypeCheckerErrors.detail2("This", override.getAccess().getAccess()+" "+ override.getName(), "Super",indef.getAccess().getAccess()+" "+ indef.getName());
}
else if (override.getAccess().getPure() != indef.getAccess().getPure())
{
TypeCheckerErrors.report(3341, "Overriding definition must " + (override.getAccess().getPure() ? "not" : "also") + " be pure", override.getName().getLocation(),override);
} else
{
PType to = af.createPDefinitionAssistant().getType(indef);
PType from = af.createPDefinitionAssistant().getType(override);
// Note this uses the "parameters only" comparator option
if (!af.getTypeComparator().compatible(to, from, true))
{
TypeCheckerErrors.report(3007, "Overriding member incompatible type: "
+ override.getName().getName(), override.getLocation(), override);
TypeCheckerErrors.detail2("This", override.getType(), "Super", indef.getType());
}
}
}
}
return inheritedThread;
}
private void checkOverloads(SClassDefinition c)
{
List<String> done = new Vector<String>();
List<PDefinition> singles = af.createPDefinitionListAssistant().singleDefinitions(c.getDefinitions());
for (PDefinition def1 : singles)
{
for (PDefinition def2 : singles)
{
if (def1 != def2
&& def1.getName() != null
&& def2.getName() != null
&& def1.getName().getName().equals(def2.getName().getName())
&& !done.contains(def1.getName().getName()))
{
if (af.createPDefinitionAssistant().isFunction(def1)
&& af.createPDefinitionAssistant().isFunction(def2)
|| af.createPDefinitionAssistant().isOperation(def1)
&& af.createPDefinitionAssistant().isOperation(def2))
{
PType to = def1.getType();
PType from = def2.getType();
// Note this uses the "parameters only" comparator option
if (af.getTypeComparator().compatible(to, from, true))
{
TypeCheckerErrors.report(3008, "Overloaded members indistinguishable: "
+ def1.getName().getName(), def1.getLocation(), def1);
TypeCheckerErrors.detail2(def1.getName().getName(), def1.getType(), def2.getName().getName(), def2.getType());
done.add(def1.getName().getName());
}
} else
{
// Class invariants can duplicate if there are several
// "inv" clauses in one class...
if (!(def1 instanceof AClassInvariantDefinition)
&& !(def2 instanceof AClassInvariantDefinition)
&& !(def1 instanceof APerSyncDefinition)
&& !(def2 instanceof APerSyncDefinition))
{
TypeCheckerErrors.report(3017, "Duplicate definitions for "
+ def1.getName().getName(), def1.getName().getLocation(), def1);
TypeCheckerErrors.detail2(def1.getName().getName(), def1.getLocation().getFile().getName()
+ " " + def1.getLocation().toShortString(), def2.getName().getName(), def2.getLocation().getFile().getName()
+ " " + def2.getLocation().toShortString());
done.add(def1.getName().getName());
}
}
}
}
}
}
public void typeCheckPass(SClassDefinition c, Pass p, Environment base,
QuestionAnswerAdaptor<TypeCheckInfo, PType> tc)
throws AnalysisException
{
if (c.getTypeChecked())
{
return;
}
if (p == Pass.TYPES) // First one
{
PDefinitionListAssistantTC assistant = af.createPDefinitionListAssistant();
assistant.removeDuplicates(c.getLocalInheritedDefinitions());
List<PDefinition> localDefs = new LinkedList<PDefinition>();
localDefs.addAll(c.getDefinitions());
localDefs.addAll(c.getLocalInheritedDefinitions());
c.setIsAbstract(assistant.hasSubclassResponsibilities(assistant.removeAbstracts(localDefs)));
}
for (PDefinition d : c.getDefinitions())
{
if (d.getPass() == p)
{
Environment env = base;
if (d instanceof AValueDefinition)
{
// ValueDefinition body always a static context
FlatCheckedEnvironment checked = new FlatCheckedEnvironment(af, new Vector<PDefinition>(), base, NameScope.NAMES);
checked.setStatic(true);
env = checked;
}
d.apply(tc, new TypeCheckInfo(af, env, NameScope.NAMES));
}
}
if (c.getInvariant() != null && c.getInvariant().getPass() == p)
{
c.getInvariant().apply(tc, new TypeCheckInfo(af, base, NameScope.NAMES));
}
}
public void initializedCheck(SClassDefinition c)
{
af.createPDefinitionListAssistant().initializedCheck(c.getDefinitions());
}
}