Project

General

Profile

Revision 7

Added by Willibald K. over 8 years ago

changing java, cpp, hpp files to unix line endings

View differences:

OoaTypeCheckVisitor.java
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
  */

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 26

  
27 27

  
28
package org.momut.ooas.visitors;
29

  
30
import java.util.ArrayList;
31
import java.util.ListIterator;
28
package org.momut.ooas.visitors;
32 29

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

  
33 33
import org.momut.ooas.ast.AstNodeTypeEnum;
34 34
import org.momut.ooas.ast.IAst;
35 35
import org.momut.ooas.ast.expressions.AccessExpression;
......
59 59
import org.momut.ooas.utils.exceptions.ArgumentException;
60 60
import org.momut.ooas.utils.exceptions.NotImplementedException;
61 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
}
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
}

Also available in: Unified diff