Project

General

Profile

root / trunk / compiler / ooasCompiler / src / org / momut / ooas / visitors / OoaTypeCheckVisitor.java @ 7

1
/**
2
  *
3
  *                      OOAS Compiler
4
  *
5
  *       Copyright 2015, AIT Austrian Institute of Technology.
6
  * This code is based on the C# Version of the OOAS Compiler, which is
7
  * copyright 2015 by the Institute of Software Technology, Graz University
8
  * of Technology with portions copyright by the AIT Austrian Institute of
9
  * Technology. All rights reserved.
10
  *
11
  * SEE THE "LICENSE" FILE FOR THE TERMS UNDER WHICH THIS FILE IS PROVIDED.
12
  *
13
  * If you modify the file please update the list of contributors below to in-
14
  * clude your name. Please also stick to the coding convention of using TABs
15
  * to do the basic (block-level) indentation and spaces for anything after
16
  * that. (Enable the display of special chars and it should be pretty obvious
17
  * what this means.) Also, remove all trailing whitespace.
18
  *
19
  * Contributors:
20
  *               Willibald Krenn (AIT)
21
  *               Stephan Zimmerer (AIT)
22
  *               Markus Demetz (AIT)
23
  *               Christoph Czurda (AIT)
24
  *
25
  */
26

    
27

    
28
package org.momut.ooas.visitors;
29

    
30
import java.util.ArrayList;
31
import java.util.ListIterator;
32

    
33
import org.momut.ooas.ast.AstNodeTypeEnum;
34
import org.momut.ooas.ast.IAst;
35
import org.momut.ooas.ast.expressions.AccessExpression;
36
import org.momut.ooas.ast.expressions.Expression;
37
import org.momut.ooas.ast.expressions.ExpressionKind;
38
import org.momut.ooas.ast.expressions.IdentifierExpression;
39
import org.momut.ooas.ast.expressions.TupleMapAccessExpression;
40
import org.momut.ooas.ast.expressions.UnaryOperator;
41
import org.momut.ooas.ast.identifiers.AttributeIdentifier;
42
import org.momut.ooas.ast.identifiers.ExpressionVariableIdentifier;
43
import org.momut.ooas.ast.identifiers.FunctionIdentifier;
44
import org.momut.ooas.ast.identifiers.Identifier;
45
import org.momut.ooas.ast.identifiers.IdentifierKind;
46
import org.momut.ooas.ast.statements.Assignment;
47
import org.momut.ooas.ast.statements.Call;
48
import org.momut.ooas.ast.statements.GuardedCommand;
49
import org.momut.ooas.ast.statements.KillStatement;
50
import org.momut.ooas.ast.statements.QualitativeConstraintStatement;
51
import org.momut.ooas.ast.statements.Statement;
52
import org.momut.ooas.ast.types.FunctionType;
53
import org.momut.ooas.ast.types.TypeKind;
54
import org.momut.ooas.ast.types.UlyssesType;
55
import org.momut.ooas.parser.ParserError;
56
import org.momut.ooas.parser.ParserState;
57
import org.momut.ooas.parser.ParserWarning;
58
import org.momut.ooas.parser.SymbolTable;
59
import org.momut.ooas.utils.exceptions.ArgumentException;
60
import org.momut.ooas.utils.exceptions.NotImplementedException;
61
import org.momut.ooas.utils.exceptions.OoasCompilerRuntimeException;
62

    
63
/// <summary>
64
/// Requires: ResolveExpressionVisitor and MethodPureClassifierVisitor
65
///
66
/// Does the typechecking of statements and initializers.
67
/// Needs resolved expressions, hence a run of ResolveVisitor first.
68
/// </summary>
69
public final class OoaTypeCheckVisitor extends OoaCompleteAstTraversalVisitor
70
{
71

    
72
        /// <summary>
73
        /// internal; used to count occurrences of attribute access in an expression
74
        /// </summary>
75
        private static final class OoaInitializerCheck extends OoaExpressionVisitor
76
        {
77
                ArrayList<Identifier> attributeAccess = new ArrayList<Identifier>();
78

    
79
                @Override
80
                public void visit(IdentifierExpression identifierExpression)
81
                {
82
                        final Identifier id = identifierExpression.identifier();
83
                        if (id.kind() == IdentifierKind.AttributeIdentifier)
84
                                attributeAccess.add(id);
85
                        super.visit(identifierExpression);
86
                }
87

    
88
        }
89

    
90

    
91

    
92

    
93
        private void Error(Statement statement, String p)
94
        {
95
                final ParserError error = new ParserError(m_ParserState.filename,
96
                                statement.line(), statement.pos(), p);
97
                m_ParserState.AddErrorMessage(error);
98
        }
99
        private void Error(Identifier id, String p)
100
        {
101
                final ParserError error = new ParserError(m_ParserState.filename,
102
                                id.line(), id.column(), p);
103
                m_ParserState.AddErrorMessage(error);
104
        }
105

    
106

    
107

    
108
        private void Warning(Statement aStatement, String p)
109
        {
110
                final ParserWarning warn = new ParserWarning(m_ParserState.filename,
111
                                aStatement.line(), aStatement.pos(), p);
112
                m_ParserState.AddWarningMessage(warn);
113
        }
114
        private void Warning(Identifier aStatement, String p)
115
        {
116
                final ParserWarning warn = new ParserWarning(m_ParserState.filename,
117
                                aStatement.line(), aStatement.column(), p);
118
                m_ParserState.AddWarningMessage(warn);
119
        }
120

    
121

    
122

    
123
        private void RemoveWarning(Identifier expression, String p)
124
        {
125
                final ArrayList<ParserWarning> toRemove = new ArrayList<ParserWarning>();
126
                for (final ParserWarning x: m_ParserState.listOfParserWarnings)
127
                {
128
                        if (p.equals(x.message()) && x.line() == expression.line() && x.column() == expression.column())
129
                                toRemove.add(x);
130
                }
131
                for (final ParserWarning x: toRemove)
132
                        m_ParserState.listOfParserWarnings.remove(x);
133
        }
134

    
135
        private void TypeCheckGuardedCommand(GuardedCommand gc)
136
        {
137
                if (gc.guard().type() == null ||
138
                                gc.guard().type().kind() != TypeKind.BoolType)
139
                        Error(gc, "Guard needs to be boolean expression");
140

    
141
                final ArrayList<ExpressionVariableIdentifier> uninitfree = gc.guard().GetUninitializedFreeVariables();
142
                if (uninitfree.size() > 0)
143
                        Error(gc, String.format("Undefined variable: '%s'", uninitfree.get(0).tokenText()));
144

    
145
                CheckMethodCallsArePure(gc.guard());
146

    
147
                gc.body().Accept(this);
148
        }
149

    
150
        private void CheckMethodCallsArePure(Expression expression)
151
        {
152
                for (final FunctionIdentifier method: expression.callTargets())
153
                {
154
                        if (!((FunctionType)method.type()).isPureFunction())
155
                                Error(method, String.format("Method '%s' needs to be pure: No change of state allowed.", method.tokenText()));
156
                }
157
        }
158

    
159

    
160
        private void TypeCheckAssignment(Assignment assignment)
161
        {
162
                SymbolTable freevars = new SymbolTable();
163
                if (assignment.nondetExpression() != null)
164
                {
165
                        CheckMethodCallsArePure(assignment.nondetExpression());
166

    
167
                        if (assignment.nondetExpression().freeVariables() != null
168
                                        && assignment.nondetExpression().freeVariables().symbolList().size() > 0)
169
                        {
170
                                // expression has free variables we need to match with right-hand-side of assignment.
171
                                freevars = assignment.nondetExpression().freeVariables();
172

    
173
                                for (final Expression aRhsExpr: assignment.values())
174
                                {
175
                                        if (aRhsExpr.freeVariables() != null)
176
                                                for (final Identifier freeRhsVar: aRhsExpr.freeVariables().symbolList())
177
                                                {
178
                                                        if (freevars.Defined(freeRhsVar.tokenText()))
179
                                                        {
180
                                                                final Identifier exprvar = freevars.Get(freeRhsVar.tokenText());
181
                                                                if (freeRhsVar.type().kind() == TypeKind.Any)
182
                                                                {
183
                                                                        freeRhsVar.SetType(exprvar.type());
184
                                                                        assignment.AddIdentifier(exprvar, null);
185
                                                                        // remove free variable warnings
186
                                                                        RemoveWarning(exprvar, String.format("Free variable in expression: '%s'.", exprvar.tokenText()));
187
                                                                        RemoveWarning(freeRhsVar, String.format("Free variable in expression: '%s'.", exprvar.tokenText()));
188
                                                                }
189
                                                                else if (UlyssesType.TypeEqual(freeRhsVar.type(), exprvar.type()))
190
                                                                {
191
                                                                        assignment.AddIdentifier(exprvar, null);
192
                                                                        // remove free variable warnings
193
                                                                        RemoveWarning(exprvar, String.format("Free variable in expression: '%s'.", exprvar.tokenText()));
194
                                                                        RemoveWarning(freeRhsVar, String.format("Free variable in expression: '%s'.", exprvar.tokenText()));
195
                                                                }
196
                                                                else
197
                                                                        Error(assignment, String.format("Type mismatch on variable %s: %s <> %s", freeRhsVar.tokenText(), freeRhsVar.type().toString(), exprvar.type().toString()));
198
                                                        }
199
                                                        else
200
                                                                Error(assignment, String.format("Undefined variable '%s'", freeRhsVar.tokenText()));
201
                                                }
202
                                }
203
                        }
204
                }
205

    
206
                for (final Identifier avar: freevars.symbolList())
207
                        if (!assignment.symbols().Defined(avar.tokenText()))
208
                                Error(assignment, String.format("Undefined variable '%s'", avar.tokenText()));
209

    
210
                if (assignment.places().size() != assignment.values().size())
211
                {
212
                        Error(assignment, "Number of places does not match number of values in assignment.");
213
                        //return;
214
                }
215

    
216
                final ListIterator<Expression> aplaceIt = assignment.places().listIterator();
217
                final ListIterator<Expression> avalueIt = assignment.values().listIterator();
218
                while (aplaceIt.hasNext() && avalueIt.hasNext())
219
                {
220
                        final Expression aplace = aplaceIt.next();
221
                        Expression avalue = avalueIt.next();
222

    
223
                        CheckPlace(aplace, assignment);
224

    
225
                        // we allow non-pure methods only for assignments of the form 'variable := function(x,y)'
226
                        if (avalue.kind() != ExpressionKind.Call)
227
                                CheckMethodCallsArePure(avalue);
228

    
229
                        final UlyssesType acover = UlyssesType.CoverType(aplace.type(), avalue.type());
230
                        if (acover == null)
231
                                Error(assignment, String.format("Type mismatch in assignment: %s ( %s := %s )", aplace.toString(), aplace.type().toString(), avalue.type().toString()));
232
                        else if (!UlyssesType.TypeEqualByKind(aplace.type(), acover)) /*ignore range things.. (see warning below)*/
233
                                Error(assignment, String.format("Type mismatch in assignment: %s ( %s := %s )", aplace.toString(), aplace.type().toString(), acover.toString()));
234
                        else
235
                        {
236
                                final ArrayList<ExpressionVariableIdentifier> uninitvars = avalue.GetUninitializedFreeVariables();
237
                                final Expression constantvalue = avalue.kind() == ExpressionKind.Value ? avalue : null;
238

    
239
                                if (uninitvars.size() > 0)
240
                                        Error(assignment, String.format("Undefined variable '%s'", uninitvars.get(0).tokenText()));
241

    
242
                                if (!UlyssesType.TypeEqual(avalue.type(), acover))
243
                                {
244
                                        final UnaryOperator cast = new UnaryOperator(ExpressionKind.Cast, avalue, avalue.line(), avalue.pos());
245
                                        cast.SetType(acover);
246
                                        avalueIt.remove();
247
                                        avalueIt.add(cast);
248
                                        avalue = avalueIt.previous();
249
                                        if (avalue != cast)
250
                                                throw new OoasCompilerRuntimeException();
251
                                }
252

    
253
                                if (UlyssesType.FirstTypeLessRange(aplace.type(), acover))
254
                                {
255
                                        if (constantvalue == null)
256
                                        {
257
                                                Warning(assignment, String.format("Assignment may over/underflow: %s := %s", aplace.type().toString(), acover.toString()));
258
                                                final UnaryOperator cast = new UnaryOperator(ExpressionKind.Cast, avalue, avalue.line(), avalue.pos());
259
                                                cast.SetType(aplace.type());
260
                                                avalueIt.remove();
261
                                                avalueIt.add(cast);
262
                                                avalue = avalueIt.previous();
263
                                                if (avalue != cast)
264
                                                        throw new OoasCompilerRuntimeException();
265
                                        }
266
                                        else
267
                                        {
268
                                                Error(assignment, String.format("Assignment out of range (%s  := %s)", aplace.toString(), constantvalue.toString()));
269
                                        }
270
                                }
271
                        }
272
                }
273
        }
274

    
275
        /// <summary>
276
        /// Checks whether the LHS-expression of an assignment is a valid place.
277
        /// IMPORTANT: If more complicated LHS expressions are allowed, then
278
        ///            ooaPrologExpression.cs needs to be adapted too!
279
        /// </summary>
280
        private void CheckPlace(Expression expression, Assignment assignment)
281
        {
282
                // check that we have some LHS of following forms
283
                // self.<var/attr>
284
                // attr[..]
285
                boolean result = true;
286

    
287
                // one array/map access allowed
288
                if (expression.kind() == ExpressionKind.TupleMapAccess)
289
                {
290
                        final TupleMapAccessExpression tAccessExpression = (TupleMapAccessExpression)expression;
291
                        expression = tAccessExpression.child();
292
                }
293

    
294
                // attributes feature a self ref
295
                if (expression.kind() == ExpressionKind.Access)
296
                {
297
                        final AccessExpression accessExpression = (AccessExpression)expression;
298
                        if (accessExpression.left().kind() == ExpressionKind.Identifier && ((IdentifierExpression)accessExpression.left()).isSelf())
299
                                expression = accessExpression.right();
300
                        else
301
                                result = false;
302
                }
303

    
304
                // now we need to have an identifier
305
                if (expression.kind() != ExpressionKind.Identifier)
306
                        result = false;
307

    
308
                if (!result)
309
                        Error(assignment, String.format("LHS of assignment must be variable or attribute."));
310
        }
311

    
312

    
313
        private void TypeCheckQualConstraint(QualitativeConstraintStatement qualConstr)
314
        {
315
                if (qualConstr.variable0().type().kind() != TypeKind.QrType && qualConstr.variable0().type().kind() != TypeKind.Any)
316
                        Error(qualConstr, String.format("Qualitative type expected: '%s'", qualConstr.variable0().tokenText()));
317
                if (qualConstr.variable1().type().kind() != TypeKind.QrType && qualConstr.variable1().type().kind() != TypeKind.Any)
318
                        Error(qualConstr, String.format("Qualitative type expected: '%s'", qualConstr.variable0().tokenText()));
319
                if (qualConstr.variable2() != null && qualConstr.variable2().type().kind() != TypeKind.QrType
320
                                && qualConstr.variable2().type().kind() != TypeKind.Any)
321
                        Error(qualConstr, String.format("Qualitative type expected: '%s'", qualConstr.variable0().tokenText()));
322
        }
323
        private void TypeCheck(Statement statement)
324
        {
325
                switch (statement.kind())
326
                {
327
                case Abort:
328
                case Skip:
329
                case Break:
330
                        break;
331

    
332
                case NondetBlock:
333
                case PrioBlock:
334
                case SeqBlock:
335
                        statement.Accept(this);
336
                        break;
337

    
338
                case Assignment:
339
                        final Assignment assignment = (Assignment)statement;
340
                        TypeCheckAssignment(assignment);
341
                        break;
342
                case GuardedCommand:
343
                        final GuardedCommand gc = (GuardedCommand)statement;
344
                        TypeCheckGuardedCommand(gc);
345
                        break;
346
                case Kill:
347
                        final KillStatement kill = (KillStatement)statement;
348
                        if (kill.someOne.type().kind() != TypeKind.OoActionSystemType)
349
                                Error(kill, String.format(" %s not an object", kill.someOne.tokenText()));
350
                        break;
351
                case MethodCall:
352
                        final Call callstatement = (Call)statement;
353
                        if (callstatement.callExpression().GetUninitializedFreeVariables().size() > 0)
354
                                Error(callstatement, "Uninitialized free variables in call expression.");
355
                        if (callstatement.callExpression().kind() != ExpressionKind.Call
356
                                        && callstatement.callExpression().kind() != ExpressionKind.foldRL
357
                                        && callstatement.callExpression().kind() != ExpressionKind.foldLR)
358
                                Error(callstatement, "Not a valid method call");
359
                        break;
360
                case QualConstraint:
361
                        final QualitativeConstraintStatement qualConstr = (QualitativeConstraintStatement)statement;
362
                        TypeCheckQualConstraint(qualConstr);
363
                        break;
364
                default:
365
                        throw new NotImplementedException();
366
                }
367
        }
368

    
369
        //private void TypeCheck()
370

    
371
        private void TypeCheck(AttributeIdentifier attributeIdentifier)
372
        {
373
                if (attributeIdentifier.initializer() == null)
374
                        throw new ArgumentException();
375

    
376
                final UlyssesType atype = attributeIdentifier.initializer().type();
377
                if (atype == null)
378
                        Error(attributeIdentifier, "Initializer lacks type.");
379
                else
380
                {
381
                        /* we use the typecheck to validate a few more properties of the initializer that
382
                         * need a completely resolved expression
383
                         */
384
                        final OoaInitializerCheck initCheck = new OoaInitializerCheck();
385
                        attributeIdentifier.initializer().Accept(initCheck);
386
                        if (initCheck.attributeAccess.size() > 0)
387
                                // disallow use of other attributes in initializer
388
                                Error(attributeIdentifier, "Access of other attributes not allowed in attribute initializer.");
389
                        else if (attributeIdentifier.initializer().callTargets().size() > 0)
390
                                // disallow method calls in initializer
391
                                Error(attributeIdentifier, "Method calls not allowed in attribute initializer.");
392
                        else if (attributeIdentifier.initializer().freeVariables().symbolList().size() > 0)
393
                                // disallow use of free variables in initializer
394
                                Error(attributeIdentifier,
395
                                                String.format("Unknown identifier in initializer: '%s'",
396
                                                                attributeIdentifier.initializer().freeVariables().symbolList().get(0).tokenText()));
397
                        else
398
                        {
399
                                final UlyssesType idtype = attributeIdentifier.type();
400
                                final UlyssesType acover = UlyssesType.CoverType(idtype, atype);
401

    
402
                                if (acover == null)
403
                                        Error(attributeIdentifier, String.format("Type mismatch in attribute initializer: %s ( %s := %s )", attributeIdentifier.toString(), idtype.toString(), atype.toString()));
404
                                else if (!UlyssesType.TypeEqualByKind(idtype, acover)) /*ignore range things.. (see warning below)*/
405
                                        Error(attributeIdentifier, String.format("Type mismatch in attribute initializer: %s ( %s := %s )", attributeIdentifier.toString(), idtype.toString(), acover.toString()));
406
                                else
407
                                {
408
                                        final Expression constantvalue = attributeIdentifier.initializer().kind() == ExpressionKind.Value ? attributeIdentifier.initializer() : null;
409

    
410
                                        if (!UlyssesType.TypeEqual(atype, acover))
411
                                        {
412
                                                final UnaryOperator cast = new UnaryOperator(ExpressionKind.Cast, attributeIdentifier.initializer(),
413
                                                                attributeIdentifier.initializer().line(), attributeIdentifier.initializer().pos());
414
                                                cast.SetType(acover);
415
                                                attributeIdentifier.SetInitializer(cast);
416
                                        }
417

    
418
                                        if (UlyssesType.FirstTypeLessRange(idtype, acover))
419
                                        {
420
                                                if (constantvalue == null)
421
                                                {
422
                                                        Warning(attributeIdentifier, String.format("Assignment may over/underflow: %s := %s", idtype.toString(), acover.toString()));
423
                                                        final UnaryOperator cast = new UnaryOperator(ExpressionKind.Cast, attributeIdentifier.initializer(),
424
                                                                        attributeIdentifier.initializer().line(), attributeIdentifier.initializer().pos());
425
                                                        cast.SetType(idtype);
426
                                                        attributeIdentifier.SetInitializer(cast);
427
                                                }
428
                                                else
429
                                                {
430
                                                        Error(attributeIdentifier, String.format("Initialization out of range (%s  := %s)", attributeIdentifier.toString(), constantvalue.toString()));
431
                                                }
432
                                        }
433
                                }
434

    
435
                                /*
436

437

438
                if (cover == null || !UlyssesType.TypeEqual(cover, idtype))
439
                    Error(attributeIdentifier,
440
                        String.Format("Type mismatch in attribute initializer: expected '%s', found '%s'",
441
                            idtype.ToString(), atype.ToString()));
442
                else
443
                    attributeIdentifier.SetInitializer(UnaryOperator.CoerceUp(attributeIdentifier.initializer, idtype));
444
                                 * */
445
                        }
446
                }
447
        }
448

    
449

    
450
        @Override
451
        protected void VisitAstElement(IAst element, IAst parent)
452
        {
453
                if (element.nodeType() == AstNodeTypeEnum.statement)
454
                {
455
                        TypeCheck((Statement)element);
456
                }
457
                else if (element.nodeType() == AstNodeTypeEnum.identifier
458
                                && ((Identifier)element).kind() == IdentifierKind.AttributeIdentifier)
459
                {
460
                        TypeCheck((AttributeIdentifier)element);
461
                }
462
                else super.VisitAstElement(element, parent);
463
        }
464

    
465

    
466

    
467
        public OoaTypeCheckVisitor(ParserState aState)
468
        {
469
                super (aState);
470
                if (aState == null)
471
                        throw new ArgumentException();
472
        }
473
}