Project

General

Profile

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

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.Statement;
51
import org.momut.ooas.ast.types.FunctionType;
52
import org.momut.ooas.ast.types.TypeKind;
53
import org.momut.ooas.ast.types.Type;
54
import org.momut.ooas.parser.ParserError;
55
import org.momut.ooas.parser.ParserState;
56
import org.momut.ooas.parser.ParserWarning;
57
import org.momut.ooas.parser.SymbolTable;
58
import org.momut.ooas.utils.exceptions.ArgumentException;
59
import org.momut.ooas.utils.exceptions.NotImplementedException;
60
import org.momut.ooas.utils.exceptions.OoasCompilerRuntimeException;
61

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

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

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

    
87
        }
88

    
89

    
90

    
91

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

    
105

    
106

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

    
120

    
121

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

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

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

    
144
                CheckMethodCallsArePure(gc.guard());
145

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

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

    
158

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

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

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

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

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

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

    
222
                        CheckPlace(aplace, assignment);
223

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

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

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

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

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

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

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

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

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

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

    
311

    
312
        private void TypeCheck(Statement statement)
313
        {
314
                switch (statement.kind())
315
                {
316
                case Abort:
317
                case Skip:
318
                case Break:
319
                        break;
320

    
321
                case NondetBlock:
322
                case PrioBlock:
323
                case SeqBlock:
324
                        statement.Accept(this);
325
                        break;
326

    
327
                case Assignment:
328
                        final Assignment assignment = (Assignment)statement;
329
                        TypeCheckAssignment(assignment);
330
                        break;
331
                case GuardedCommand:
332
                        final GuardedCommand gc = (GuardedCommand)statement;
333
                        TypeCheckGuardedCommand(gc);
334
                        break;
335
                case Kill:
336
                        final KillStatement kill = (KillStatement)statement;
337
                        if (kill.someOne.type().kind() != TypeKind.OoActionSystemType)
338
                                Error(kill, String.format(" %s not an object", kill.someOne.tokenText()));
339
                        break;
340
                case MethodCall:
341
                        final Call callstatement = (Call)statement;
342
                        if (callstatement.callExpression().GetUninitializedFreeVariables().size() > 0)
343
                                Error(callstatement, "Uninitialized free variables in call expression.");
344
                        if (callstatement.callExpression().kind() != ExpressionKind.Call
345
                                        && callstatement.callExpression().kind() != ExpressionKind.foldRL
346
                                        && callstatement.callExpression().kind() != ExpressionKind.foldLR)
347
                                Error(callstatement, "Not a valid method call");
348
                        break;
349
                default:
350
                        throw new NotImplementedException();
351
                }
352
        }
353

    
354
        //private void TypeCheck()
355

    
356
        private void TypeCheck(AttributeIdentifier attributeIdentifier)
357
        {
358
                if (attributeIdentifier.initializer() == null)
359
                        throw new ArgumentException();
360

    
361
                final Type atype = attributeIdentifier.initializer().type();
362
                if (atype == null)
363
                        Error(attributeIdentifier, "Initializer lacks type.");
364
                else
365
                {
366
                        /* we use the typecheck to validate a few more properties of the initializer that
367
                         * need a completely resolved expression
368
                         */
369
                        final OoaInitializerCheck initCheck = new OoaInitializerCheck();
370
                        attributeIdentifier.initializer().Accept(initCheck);
371
                        if (initCheck.attributeAccess.size() > 0)
372
                                // disallow use of other attributes in initializer
373
                                Error(attributeIdentifier, "Access of other attributes not allowed in attribute initializer.");
374
                        else if (attributeIdentifier.initializer().callTargets().size() > 0)
375
                                // disallow method calls in initializer
376
                                Error(attributeIdentifier, "Method calls not allowed in attribute initializer.");
377
                        else if (attributeIdentifier.initializer().freeVariables().symbolList().size() > 0)
378
                                // disallow use of free variables in initializer
379
                                Error(attributeIdentifier,
380
                                                String.format("Unknown identifier in initializer: '%s'",
381
                                                                attributeIdentifier.initializer().freeVariables().symbolList().get(0).tokenText()));
382
                        else
383
                        {
384
                                final Type idtype = attributeIdentifier.type();
385
                                final Type acover = Type.CoverType(idtype, atype);
386

    
387
                                if (acover == null)
388
                                        Error(attributeIdentifier, String.format("Type mismatch in attribute initializer: %s ( %s := %s )", attributeIdentifier.toString(), idtype.toString(), atype.toString()));
389
                                else if (!Type.TypeEqualByKind(idtype, acover)) /*ignore range things.. (see warning below)*/
390
                                        Error(attributeIdentifier, String.format("Type mismatch in attribute initializer: %s ( %s := %s )", attributeIdentifier.toString(), idtype.toString(), acover.toString()));
391
                                else
392
                                {
393
                                        final Expression constantvalue = attributeIdentifier.initializer().kind() == ExpressionKind.Value ? attributeIdentifier.initializer() : null;
394

    
395
                                        if (!Type.TypeEqual(atype, acover))
396
                                        {
397
                                                final UnaryOperator cast = new UnaryOperator(ExpressionKind.Cast, attributeIdentifier.initializer(),
398
                                                                attributeIdentifier.initializer().line(), attributeIdentifier.initializer().pos());
399
                                                cast.SetType(acover);
400
                                                attributeIdentifier.SetInitializer(cast);
401
                                        }
402

    
403
                                        if (Type.FirstTypeLessRange(idtype, acover))
404
                                        {
405
                                                if (constantvalue == null)
406
                                                {
407
                                                        Warning(attributeIdentifier, String.format("Assignment may over/underflow: %s := %s", idtype.toString(), acover.toString()));
408
                                                        final UnaryOperator cast = new UnaryOperator(ExpressionKind.Cast, attributeIdentifier.initializer(),
409
                                                                        attributeIdentifier.initializer().line(), attributeIdentifier.initializer().pos());
410
                                                        cast.SetType(idtype);
411
                                                        attributeIdentifier.SetInitializer(cast);
412
                                                }
413
                                                else
414
                                                {
415
                                                        Error(attributeIdentifier, String.format("Initialization out of range (%s  := %s)", attributeIdentifier.toString(), constantvalue.toString()));
416
                                                }
417
                                        }
418
                                }
419

    
420
                                /*
421

422

423
                if (cover == null || !UlyssesType.TypeEqual(cover, idtype))
424
                    Error(attributeIdentifier,
425
                        String.Format("Type mismatch in attribute initializer: expected '%s', found '%s'",
426
                            idtype.ToString(), atype.ToString()));
427
                else
428
                    attributeIdentifier.SetInitializer(UnaryOperator.CoerceUp(attributeIdentifier.initializer, idtype));
429
                                 * */
430
                        }
431
                }
432
        }
433

    
434

    
435
        @Override
436
        protected void VisitAstElement(IAst element, IAst parent)
437
        {
438
                if (element.nodeType() == AstNodeTypeEnum.statement)
439
                {
440
                        TypeCheck((Statement)element);
441
                }
442
                else if (element.nodeType() == AstNodeTypeEnum.identifier
443
                                && ((Identifier)element).kind() == IdentifierKind.AttributeIdentifier)
444
                {
445
                        TypeCheck((AttributeIdentifier)element);
446
                }
447
                else super.VisitAstElement(element, parent);
448
        }
449

    
450

    
451

    
452
        public OoaTypeCheckVisitor(ParserState aState)
453
        {
454
                super (aState);
455
                if (aState == null)
456
                        throw new ArgumentException();
457
        }
458
}