/* * #%~ * 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.utilities.type; import java.util.ArrayList; import java.util.List; import java.util.Vector; import org.overture.ast.analysis.AnalysisException; import org.overture.ast.analysis.QuestionAnswerAdaptor; import org.overture.ast.analysis.intf.IQuestionAnswer; import org.overture.ast.definitions.ABusClassDefinition; import org.overture.ast.definitions.ACpuClassDefinition; import org.overture.ast.definitions.AExplicitFunctionDefinition; import org.overture.ast.definitions.AImportedDefinition; import org.overture.ast.definitions.AInheritedDefinition; import org.overture.ast.definitions.ARenamedDefinition; import org.overture.ast.definitions.AStateDefinition; import org.overture.ast.definitions.ATypeDefinition; import org.overture.ast.definitions.PDefinition; import org.overture.ast.definitions.SClassDefinition; import org.overture.ast.factory.AstFactory; import org.overture.ast.node.INode; import org.overture.ast.typechecker.NameScope; import org.overture.ast.types.ABracketType; import org.overture.ast.types.AClassType; import org.overture.ast.types.AFieldField; import org.overture.ast.types.AFunctionType; import org.overture.ast.types.ANamedInvariantType; import org.overture.ast.types.AOperationType; import org.overture.ast.types.AOptionalType; import org.overture.ast.types.AParameterType; import org.overture.ast.types.AProductType; import org.overture.ast.types.ARecordInvariantType; import org.overture.ast.types.SSetType; import org.overture.ast.types.AUnionType; import org.overture.ast.types.AUnresolvedType; import org.overture.ast.types.PType; import org.overture.ast.types.SInvariantType; import org.overture.ast.types.SMapType; import org.overture.ast.types.SSeqType; import org.overture.ast.util.PTypeSet; import org.overture.typechecker.Environment; import org.overture.typechecker.PrivateClassEnvironment; import org.overture.typechecker.TypeCheckException; import org.overture.typechecker.TypeCheckInfo; import org.overture.typechecker.TypeCheckerErrors; import org.overture.typechecker.assistant.ITypeCheckerAssistantFactory; /** * This class implements a way to resolve types from general PType class. * * @author kel */ public class PTypeResolver extends QuestionAnswerAdaptor<PTypeResolver.Newquestion, PType> { public static class Newquestion { ATypeDefinition root; IQuestionAnswer<TypeCheckInfo, PType> rootVisitor; TypeCheckInfo question; public Newquestion(ATypeDefinition root, IQuestionAnswer<TypeCheckInfo, PType> rootVisitor, TypeCheckInfo question) { this.question = question; this.root = root; this.rootVisitor = rootVisitor; } } protected ITypeCheckerAssistantFactory af; public PTypeResolver(ITypeCheckerAssistantFactory af) { this.af = af; } @Override public PType caseABracketType(ABracketType type, Newquestion question) throws AnalysisException { if (type.getResolved()) { return type; } else { type.setResolved(true); } PType tmp = type; try { do { tmp = af.createPTypeAssistant().typeResolve(((ABracketType)tmp).getType(), question.root, question.rootVisitor, question.question); } while (tmp instanceof ABracketType); tmp = af.createPTypeAssistant().typeResolve(tmp, question.root, question.rootVisitor, question.question); tmp.parent(type.parent());// re-link tree after bracket removal return tmp; } catch (TypeCheckException e) { af.createPTypeAssistant().unResolve(type); throw e; } } @Override public PType caseAClassType(AClassType type, Newquestion question) throws AnalysisException { if (type.getResolved()) { return type; } else { type.setResolved(true); } try { // We have to add a private class environment here because the // one passed in may be from a class that contains a reference // to this class. We need the private environment to see all // the definitions that are available to us while resolving... Environment self = new PrivateClassEnvironment(question.question.assistantFactory, type.getClassdef(), question.question.env); for (PDefinition d : type.getClassdef().getDefinitions()) { // There is a problem resolving ParameterTypes via a FunctionType // when this is not being done via ExplicitFunctionDefinition // which extends the environment with the type names that // are in scope. So we skip these here. if (d instanceof AExplicitFunctionDefinition) { AExplicitFunctionDefinition fd = (AExplicitFunctionDefinition) d; if (fd.getTypeParams() != null) { continue; // Skip polymorphic functions } } question.question = new TypeCheckInfo(question.question.assistantFactory, self, question.question.scope, question.question.qualifiers); af.createPTypeAssistant().typeResolve(question.question.assistantFactory.createPDefinitionAssistant().getType(d), question.root, question.rootVisitor, question.question); } return type; } catch (TypeCheckException e) { af.createPTypeAssistant().unResolve(type); throw e; } } @Override public PType caseAFunctionType(AFunctionType type, Newquestion question) throws AnalysisException { if (type.getResolved()) { return type; } else { type.setResolved(true); } List<PType> fixed = new ArrayList<PType>(); TypeCheckException problem = null; for (PType ft : type.getParameters()) { try { fixed.add(af.createPTypeAssistant().typeResolve(ft, question.root, question.rootVisitor, question.question)); } catch (TypeCheckException e) { if (problem == null) { problem = e; } else { problem.addExtra(e); } fixed.add(AstFactory.newAUnknownType(type.getLocation())); } } try { type.setParameters(fixed); type.setResult(af.createPTypeAssistant().typeResolve(type.getResult(), question.root, question.rootVisitor, question.question)); } catch (TypeCheckException e) { if (problem == null) { problem = e; } else { problem.addExtra(e); } fixed.add(AstFactory.newAUnknownType(type.getLocation())); } if (problem != null) { type.apply(af.getTypeUnresolver()); throw problem; } return type; } @Override public PType caseANamedInvariantType(ANamedInvariantType type, Newquestion question) throws AnalysisException { if (type.getResolved()) { return type; } else { type.setResolved(true); } try { type.setType(af.createPTypeAssistant().typeResolve(type.getType(), question.root, question.rootVisitor, question.question)); return type; } catch (TypeCheckException e) { af.createPTypeAssistant().unResolve(type); throw e; } } @Override public PType caseARecordInvariantType(ARecordInvariantType type, Newquestion question) throws AnalysisException { if (type.getResolved()) { return type; } else { type.setResolved(true); type.setInfinite(false); } TypeCheckException problem = null; for (AFieldField f : type.getFields()) { if (question.root != null) { question.root.setInfinite(false); } try { f.apply(THIS, question); } catch (TypeCheckException e) { if (problem == null) { problem = e; } else { problem.addExtra(e); } } if (question.root != null) { type.setInfinite(type.getInfinite() || question.root.getInfinite()); } } if (problem != null) { type.apply(af.getTypeUnresolver()); throw problem; } if (question.root != null) { question.root.setInfinite(type.getInfinite()); } return type; } @Override public PType caseAFieldField(AFieldField f, Newquestion question) throws AnalysisException { // Recursion defence done by the type f.setType(af.createPTypeAssistant().typeResolve(f.getType(), question.root, question.rootVisitor, question.question)); if (question.question.env.isVDMPP()) { if (f.getType() instanceof AFunctionType) { f.getTagname().setTypeQualifier(((AFunctionType) f.getType()).getParameters()); } else if (f.getType() instanceof AOperationType) { f.getTagname().setTypeQualifier(((AOperationType) f.getType()).getParameters()); } } return f.getType(); } @Override public PType defaultSInvariantType(SInvariantType type, Newquestion question) throws AnalysisException { type.setResolved(true); return type; } @Override public PType defaultSMapType(SMapType type, Newquestion question) throws AnalysisException { if (type.getResolved()) { return type; } else { type.setResolved(true); } try { if (!type.getEmpty()) { type.setFrom(af.createPTypeAssistant().typeResolve(type.getFrom(), question.root, question.rootVisitor, question.question)); type.setTo(af.createPTypeAssistant().typeResolve(type.getTo(), question.root, question.rootVisitor, question.question)); } return type; } catch (TypeCheckException e) { type.apply(af.getTypeUnresolver()); throw e; } } @Override public PType caseAOperationType(AOperationType type, Newquestion question) throws AnalysisException { if (type.getResolved()) { return type; } else { type.setResolved(true); } List<PType> fixed = new ArrayList<PType>(); TypeCheckException problem = null; for (PType ot : type.getParameters()) { try { fixed.add(af.createPTypeAssistant().typeResolve(ot, question.root, question.rootVisitor, question.question)); } catch (TypeCheckException e) { if (problem == null) { problem = e; } else { problem.addExtra(e); } fixed.add(AstFactory.newAUnknownType(type.getLocation())); } } try { type.setParameters(fixed); type.setResult(af.createPTypeAssistant().typeResolve(type.getResult(), question.root, question.rootVisitor, question.question)); } catch (TypeCheckException e) { if (problem == null) { problem = e; } else { problem.addExtra(e); } fixed.add(AstFactory.newAUnknownType(type.getLocation())); } if (problem != null) { type.apply(af.getTypeUnresolver()); throw problem; } return type; } @Override public PType caseAOptionalType(AOptionalType type, Newquestion question) throws AnalysisException { if (type.getResolved()) { return type; } else { type.setResolved(true); } type.setType(af.createPTypeAssistant().typeResolve(type.getType(), question.root, question.rootVisitor, question.question)); if (question.root != null) { question.root.setInfinite(false); // Could be nil } return type; } @Override public PType caseAParameterType(AParameterType type, Newquestion question) throws AnalysisException { if (type.getResolved()) { return type; } else { type.setResolved(true); } PDefinition p = question.question.env.findName(type.getName(), NameScope.NAMES); if (p == null || !(question.question.assistantFactory.createPDefinitionAssistant().getType(p) instanceof AParameterType)) { TypeCheckerErrors.report(3433, "Parameter type @" + type.getName() + " not defined", type.getLocation(), type); } return type; } @Override public PType caseAProductType(AProductType type, Newquestion question) throws AnalysisException { if (type.getResolved()) { return type; } else { type.setResolved(true); } List<PType> fixed = new Vector<PType>(); TypeCheckException problem = null; for (PType t : type.getTypes()) { try { PType rt = af.createPTypeAssistant().typeResolve(t, question.root, question.rootVisitor, question.question); fixed.add(rt); } catch (TypeCheckException e) { if (problem == null) { problem = e; } else { problem.addExtra(e); } } } if (problem != null) { type.apply(af.getTypeUnresolver()); throw problem; } type.setTypes(fixed); return type; } @Override public PType defaultSSeqType(SSeqType type, Newquestion question) throws AnalysisException { if (type.getResolved()) { return type; } else { type.setResolved(true); } try { type.setSeqof(af.createPTypeAssistant().typeResolve(type.getSeqof(), question.root, question.rootVisitor, question.question)); if (question.root != null) { question.root.setInfinite(false); // Could be empty } return type; } catch (TypeCheckException e) { type.apply(af.getTypeUnresolver()); throw e; } } @Override public PType defaultSSetType(SSetType type, Newquestion question) throws AnalysisException { if (type.getResolved()) { return type; } else { type.setResolved(true); } try { type.setSetof(af.createPTypeAssistant().typeResolve(type.getSetof(), question.root, question.rootVisitor, question.question)); if (question.root != null) { question.root.setInfinite(false); // Could be empty } return type; } catch (TypeCheckException e) { type.apply(af.getTypeUnresolver()); throw e; } } @Override public PType caseAUnionType(AUnionType type, Newquestion question) throws AnalysisException { if (type.getResolved()) { return type; } else { type.setResolved(true); type.setInfinite(true); } PTypeSet fixed = new PTypeSet(af); TypeCheckException problem = null; for (PType t : type.getTypes()) { if (question.root != null) { question.root.setInfinite(false); } try { fixed.add(af.createPTypeAssistant().typeResolve(t, question.root, question.rootVisitor, question.question)); } catch (TypeCheckException e) { if (problem == null) { problem = e; } else { problem.addExtra(e); } } if (question.root != null) { type.setInfinite(type.getInfinite() && question.root.getInfinite()); } } if (problem != null) { type.apply(af.getTypeUnresolver()); throw problem; } type.setTypes(new Vector<PType>(fixed)); if (question.root != null) { question.root.setInfinite(type.getInfinite()); } // Resolved types may be unions, so force a re-expand type.setExpanded(false); af.createAUnionTypeAssistant().expand(type); return type; } @Override public PType caseAUnresolvedType(AUnresolvedType type, Newquestion question) throws AnalysisException { PType deref = dereference(type, question.question.env, question.root, question.question.assistantFactory); if (!(deref instanceof AClassType)) { deref = af.createPTypeAssistant().typeResolve(deref, question.root, question.rootVisitor, question.question); } // TODO: return deref.clone() return deref; } private static PType dereference(AUnresolvedType type, Environment env, ATypeDefinition root, ITypeCheckerAssistantFactory af) { PDefinition def = env.findType(type.getName(), type.getLocation().getModule()); if (def == null) { throw new TypeCheckException("Unable to resolve type name '" + type.getName() + "'", type.getLocation(), type); } if (def instanceof AImportedDefinition) { AImportedDefinition idef = (AImportedDefinition) def; def = idef.getDef(); } if (def instanceof ARenamedDefinition) { ARenamedDefinition rdef = (ARenamedDefinition) def; def = rdef.getDef(); } if (!(def instanceof ATypeDefinition) && !(def instanceof AStateDefinition) && !(def instanceof SClassDefinition) && !(def instanceof AInheritedDefinition)) { TypeCheckerErrors.report(3434, "'" + type.getName() + "' is not the name of a type definition", type.getLocation(), type); } if (def instanceof ATypeDefinition) { if (def == root) { root.setInfinite(true); } } if ((def instanceof ACpuClassDefinition || def instanceof ABusClassDefinition) && !env.isSystem()) { TypeCheckerErrors.report(3296, "Cannot use '" + type.getName() + "' outside system class", type.getLocation(), type); } PType r = null; r = af.createPDefinitionAssistant().getType(def); List<PDefinition> tempDefs = new Vector<PDefinition>(); tempDefs.add(def); r.setDefinitions(tempDefs); return r; } // @Override // public PType caseATypeBind(ATypeBind type, Newquestion question) // throws AnalysisException // { // type.setType(af.createPTypeAssistant().typeResolve(type.getType(), null, question.rootVisitor, // question.question)); // } @Override public PType defaultPType(PType type, Newquestion question) throws AnalysisException { type.setResolved(true); return type; } @Override public PType createNewReturnValue(INode node, Newquestion question) throws AnalysisException { // TODO Auto-generated method stub return null; } @Override public PType createNewReturnValue(Object node, Newquestion question) throws AnalysisException { // TODO Auto-generated method stub return null; } }