// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz)
// All rights reserved.
//
// This software may be modified and distributed under the terms
// of the BSD license. See the LICENSE file for details.
package wyil.util.type;
import static wyil.util.TypeSystem.*;
import wyautl_old.lang.*;
import wyil.lang.Type;
import wyil.util.TypeSystem;
/**
* <p>
* The explicit coercion operator extends the implicit coercion operator to
* include coercions which must be specified with an explicit cast operation.
* This is necessary because such coercions correspond to a loss of precision
* and, hence, may fail at runtime. An example is the following
* </p>
*
* <pre>
* char f(int x):
* return (char) x
* </pre>
*
* <p>
* The above will only compile if the explicit <code>(char)</code> cast is
* provided. This is required because a <code>char</code> corresponds only to a
* subset of the possible integers (i.e. those codepoints defined by the Unicode
* standard). The semantics of the Whiley language dictate that, should the
* integer lie outside the range of permissible code points, then a runtime
* fault is raised.
* </p>
*
* <b>NOTE:</b> as for the subtype operator, both types must have been
* normalised beforehand to guarantee correct results from this operator. </p>
*
* @author David J. Pearce
*
*/
public class ExplicitCoercionOperator extends SubtypeOperator {
public ExplicitCoercionOperator(Automaton fromAutomata, Automaton toAutomata, LifetimeRelation lr) {
super(fromAutomata,toAutomata,lr);
}
@Override
public boolean isIntersectionInner(int fromIndex, boolean fromSign,
int toIndex, boolean toSign) {
Automaton.State fromState = from.states[fromIndex];
Automaton.State toState = to.states[toIndex];
int fromKind = fromState.kind;
int toKind = toState.kind;
if(fromKind == K_RECORD && toKind == K_RECORD) {
return intersectRecords(fromIndex,fromSign,toIndex,toSign);
} else {
return super.isIntersectionInner(fromIndex, fromSign, toIndex,
toSign);
}
}
/**
* <p>
* Check for intersection between two states with kind K_RECORD. The
* distinction between open and closed records adds complexity here.
* </p>
*
* <p>
* Intersection between <b>closed</b> records is the easiest case. The main
* examples are:
* </p>
* <ul>
* <li><code>{T1 f, T2 g} & {T3 f, T4 g} = if T1&T3 and T2&T4</code>.</li>
* <li><code>{T1 f, T2 g} & {T3 f, T4 h} = false</code>.</li>
* <li><code>{T1 f, T2 g} & !{T3 f, T4 g} = if T1&!T3 or T2&!T4</code>.</li>
* <li><code>{T1 f, T2 g} & !{T3 f} = false</code>.</li>
* <li><code>!{T1 f} & !{T2 f} = true</code>.</li>
* </ul>
* <p>
* Intersection between a <b>closed</b> and <b>open</b> record is similar. The main examples are:
* </p>
* <ul>
* <li><code>{T1 f, T2 g, ...} & {T3 f, T4 g} = if T1&T3 and T2&T4</code>.</li>
* <li><code>{T1 f, ...} & {T2 f, T3 g} = if T1&T2</code>.</li>
* <li><code>{T1 f, T2 g, ...} & {T3 f, T4 h} = false</code>.</li>
* <li><code>{T1 f, T2 g, ...} & !{T3 f, T4 g} = if T1&!T3 or T2&!T4</code>.</li> *
* <li><code>!{T1 f, T2 g, ...} & {T3 f, T4 g} = if T1&!T3 or T2&!T4</code>.</li> *
* <li><code>{T1 f, ...} & !{T2 f, T3 g} = true</code>.</li>
* <li><code>{T1 f, T2 g, ...} & !{T3 f, T4 h} = false</code>.</li>
* <li><code>!{T1 f,...} & !{T2 f} = true</code>.</li>
* </ul>
*
* @param fromIndex
* --- index of from state
* @param fromSign
* --- sign of from state (true = normal, false = inverted).
* @param toIndex
* --- index of to state
* @param toSign
* --- sign of from state (true = normal, false = inverted).
* @return --- true if such an intersection exists, false otherwise.
*/
@Override
protected boolean intersectRecords(int fromIndex, boolean fromSign, int toIndex, boolean toSign) {
Automaton.State fromState = from.states[fromIndex];
Automaton.State toState = to.states[toIndex];
if(fromSign || toSign) {
int[] fromChildren = fromState.children;
int[] toChildren = toState.children;
TypeSystem.RecordState fromFields = (TypeSystem.RecordState) fromState.data;
TypeSystem.RecordState toFields = (TypeSystem.RecordState) toState.data;
boolean fromOpen = true; // to force explicit coercions
boolean toOpen = true; // to force explicit coercions
if (fromChildren.length < toChildren.length && !fromOpen) {
return !fromSign || !toSign;
} else if (fromChildren.length > toChildren.length && !toOpen) {
return !fromSign || !toSign;
} else if (!fromSign && !fromOpen && toOpen) {
return true; // guaranteed true!
} else if (!toSign && !toOpen && fromOpen) {
return true; // guaranteed true!
}
boolean andChildren = true;
boolean orChildren = false;
int fi=0;
int ti=0;
while(fi != fromFields.size() && ti != toFields.size()) {
boolean v;
String fn = fromFields.get(fi);
String tn = toFields.get(ti);
int c = fn.compareTo(tn);
if(c == 0) {
int fromChild = fromChildren[fi++];
int toChild = toChildren[ti++];
v = isIntersection(fromChild, fromSign, toChild,
toSign);
} else if(c < 0 && toOpen) {
fi++;
v = toSign;
} else if(c > 0 && fromOpen) {
ti++;
v = fromSign;
} else {
return !fromSign || !toSign;
}
andChildren &= v;
orChildren |= v;
}
if(fi < fromFields.size()) {
if(toOpen) {
// assert fromSign || fromOpen
orChildren |= toSign;
andChildren &= toSign;
} else {
return !fromSign || !toSign;
}
} else if(ti < toFields.size()) {
if(fromOpen) {
// assert toSign || toOpen
orChildren |= fromSign;
andChildren &= fromSign;
} else {
return !fromSign || !toSign;
}
}
if(!fromSign || !toSign) {
return orChildren;
} else {
return andChildren;
}
}
return true;
}
}