summaryrefslogtreecommitdiff
path: root/sources/scalac/typechecker/ConstantFolder.java
diff options
context:
space:
mode:
Diffstat (limited to 'sources/scalac/typechecker/ConstantFolder.java')
-rw-r--r--sources/scalac/typechecker/ConstantFolder.java631
1 files changed, 326 insertions, 305 deletions
diff --git a/sources/scalac/typechecker/ConstantFolder.java b/sources/scalac/typechecker/ConstantFolder.java
index 46d33b702f..20b37323eb 100644
--- a/sources/scalac/typechecker/ConstantFolder.java
+++ b/sources/scalac/typechecker/ConstantFolder.java
@@ -12,331 +12,352 @@
package scalac.typechecker;
-import scalac.util.*;
-import scalac.ast.*;
-import scalac.symtab.*;
-import scalac.symtab.Type.*;
+import scalac.Global;
+import scalac.ast.Tree;
+import scalac.ast.TreeGen;
+import scalac.atree.AConstant;
+import scalac.atree.ATypeKind;
+import scalac.symtab.Definitions;
+import scalac.symtab.Type;
+import scalac.util.Name;
+import scalac.util.Names;
-class ConstantFolder implements /*imports*/ TypeTags {
+public class ConstantFolder {
- private final Analyzer ana;
+ //########################################################################
+ // Private Fields
- ConstantFolder(Analyzer ana) {
- this.ana = ana;
+ /** The global definitions */
+ private final Definitions definitions;
+
+ /** The tree generator */
+ private final TreeGen gen;
+
+ //########################################################################
+ // Public Constructors
+
+ /** Initializes this instance. */
+ public ConstantFolder(Global global) {
+ this.definitions = global.definitions;
+ this.gen = global.treeGen;
}
- /** fold binary operation.
+ //########################################################################
+ // Public Methods
+
+ /**
+ * Attempts to constant fold given tree. Returns the input tree if
+ * constant folding fails.
*/
- Type foldBinary(int pos, ConstantType left, ConstantType right, Name op) {
- try {
- Type ltype = left.deconst();
- Symbol lsymbol = left.symbol();
- if (lsymbol == ana.definitions.BYTE_CLASS ||
- lsymbol == ana.definitions.CHAR_CLASS ||
- lsymbol == ana.definitions.SHORT_CLASS) {
- ltype = ana.definitions.INT_TYPE();
- lsymbol = ana.definitions.INT_CLASS;
- }
- Type rtype = right.deconst();
- Symbol rsymbol = right.symbol();
- if (rsymbol == ana.definitions.BYTE_CLASS ||
- rsymbol == ana.definitions.CHAR_CLASS ||
- rsymbol == ana.definitions.SHORT_CLASS) {
- rtype = ana.definitions.INT_TYPE();
- rsymbol = ana.definitions.INT_CLASS;
- }
- Type optype;
- if (ltype.isSameAs(rtype))
- optype = ltype;
- else if (lsymbol == ana.definitions.JAVA_STRING_CLASS)
- optype = ltype;
- else if (rsymbol == ana.definitions.JAVA_STRING_CLASS)
- optype = rtype;
- else if (lsymbol == ana.definitions.INT_CLASS)
- optype = rtype;
- else if (rsymbol == ana.definitions.INT_CLASS)
- optype = ltype;
- else if (lsymbol == ana.definitions.LONG_CLASS)
- optype = rtype;
- else if (rsymbol == ana.definitions.LONG_CLASS)
- optype = ltype;
- else if (lsymbol == ana.definitions.FLOAT_CLASS)
- optype = rtype;
- else if (rsymbol == ana.definitions.FLOAT_CLASS)
- optype = ltype;
- else
- throw Debug.abort("illegal case", ltype + " - " + rtype);
- Object value = null;
- switch (optype.unbox()) {
- case UnboxedType(INT):
- if (op == Names.ADD)
- value = new Integer(left.intValue() + right.intValue());
- else if (op == Names.SUB)
- value = new Integer(left.intValue() - right.intValue());
- else if (op == Names.MUL)
- value = new Integer(left.intValue() * right.intValue());
- else if (op == Names.DIV)
- value = new Integer(left.intValue() / right.intValue());
- else if (op == Names.MOD)
- value = new Integer(left.intValue() % right.intValue());
- else if (op == Names.EQ)
- value = new Boolean(left.intValue() == right.intValue());
- else if (op == Names.NE)
- value = new Boolean(left.intValue() != right.intValue());
- else if (op == Names.LT)
- value = new Boolean(left.intValue() < right.intValue());
- else if (op == Names.GT)
- value = new Boolean(left.intValue() > right.intValue());
- else if (op == Names.LE)
- value = new Boolean(left.intValue() <= right.intValue());
- else if (op == Names.GE)
- value = new Boolean(left.intValue() >= right.intValue());
- else if (op == Names.OR)
- value = new Integer(left.intValue() | right.intValue());
- else if (op == Names.AND)
- value = new Integer(left.intValue() & right.intValue());
- else if (op == Names.XOR)
- value = new Integer(left.intValue() ^ right.intValue());
- else if (op == Names.LSL)
- value = new Integer(left.intValue() << right.intValue());
- else if (op == Names.LSR)
- value = new Integer(left.intValue() >>> right.intValue());
- else if (op == Names.ASR)
- value = new Integer(left.intValue() >> right.intValue());
- break;
- case UnboxedType(LONG):
- if (op == Names.ADD)
- value = new Long(left.longValue() + right.longValue());
- else if (op == Names.SUB)
- value = new Long(left.longValue() - right.longValue());
- else if (op == Names.MUL)
- value = new Long(left.longValue() * right.longValue());
- else if (op == Names.DIV)
- value = new Long(left.longValue() / right.longValue());
- else if (op == Names.MOD)
- value = new Long(left.longValue() % right.longValue());
- else if (op == Names.EQ)
- value = new Boolean(left.longValue() == right.longValue());
- else if (op == Names.NE)
- value = new Boolean(left.longValue() != right.longValue());
- else if (op == Names.LT)
- value = new Boolean(left.longValue() < right.longValue());
- else if (op == Names.GT)
- value = new Boolean(left.longValue() > right.longValue());
- else if (op == Names.LE)
- value = new Boolean(left.longValue() <= right.longValue());
- else if (op == Names.GE)
- value = new Boolean(left.longValue() >= right.longValue());
- else if (op == Names.OR)
- value = new Long(left.longValue() | right.longValue());
- else if (op == Names.AND)
- value = new Long(left.longValue() & right.longValue());
- else if (op == Names.XOR)
- value = new Long(left.longValue() ^ right.longValue());
- else if (op == Names.LSL)
- value = new Long(left.longValue() << right.intValue());
- else if (op == Names.LSR)
- value = new Long(left.longValue() >>> right.intValue());
- else if (op == Names.ASR)
- value = new Long(left.longValue() >> right.intValue());
- break;
- case UnboxedType(FLOAT):
- if (op == Names.ADD)
- value = new Float(left.floatValue() + right.floatValue());
- else if (op == Names.SUB)
- value = new Float(left.floatValue() - right.floatValue());
- else if (op == Names.MUL)
- value = new Float(left.floatValue() * right.floatValue());
- else if (op == Names.DIV)
- value = new Float(left.floatValue() / right.floatValue());
- else if (op == Names.MOD)
- value = new Float(left.floatValue() % right.floatValue());
- else if (op == Names.EQ)
- value = new Boolean(left.floatValue() == right.floatValue());
- else if (op == Names.NE)
- value = new Boolean(left.floatValue() != right.floatValue());
- else if (op == Names.LT)
- value = new Boolean(left.floatValue() < right.floatValue());
- else if (op == Names.GT)
- value = new Boolean(left.floatValue() > right.floatValue());
- else if (op == Names.LE)
- value = new Boolean(left.floatValue() <= right.floatValue());
- else if (op == Names.GE)
- value = new Boolean(left.floatValue() >= right.floatValue());
- break;
- case UnboxedType(DOUBLE):
- if (op == Names.ADD)
- value = new Double(left.doubleValue() + right.doubleValue());
- else if (op == Names.SUB)
- value = new Double(left.doubleValue() - right.doubleValue());
- else if (op == Names.MUL)
- value = new Double(left.doubleValue() * right.doubleValue());
- else if (op == Names.DIV)
- value = new Double(left.doubleValue() / right.doubleValue());
- else if (op == Names.MOD)
- value = new Double(left.doubleValue() % right.doubleValue());
- else if (op == Names.EQ)
- value = new Boolean(left.doubleValue() == right.doubleValue());
- else if (op == Names.NE)
- value = new Boolean(left.doubleValue() != right.doubleValue());
- else if (op == Names.LT)
- value = new Boolean(left.doubleValue() < right.doubleValue());
- else if (op == Names.GT)
- value = new Boolean(left.doubleValue() > right.doubleValue());
- else if (op == Names.LE)
- value = new Boolean(left.doubleValue() <= right.doubleValue());
- else if (op == Names.GE)
- value = new Boolean(left.doubleValue() >= right.doubleValue());
- break;
- case UnboxedType(BOOLEAN):
- if (op == Names.EQ)
- value = new Boolean(left.booleanValue() == right.booleanValue());
- else if (op == Names.NE)
- value = new Boolean(left.booleanValue() != right.booleanValue());
- else if (op == Names.OR || op == Names.ZOR)
- value = new Boolean(left.booleanValue() | right.booleanValue());
- else if (op == Names.AND || op == Names.ZAND)
- value = new Boolean(left.booleanValue() & right.booleanValue());
- else if (op == Names.XOR)
- value = new Boolean(left.booleanValue() ^ right.booleanValue());
- break;
- default:
- if (optype.symbol() == ana.definitions.JAVA_STRING_CLASS &&
- op == Names.ADD)
- value = left.stringValue() + right.stringValue();
- }
- return (value != null) ? Type.constantType(value) : Type.NoType;
- } catch (ArithmeticException e) {
- ana.unit.warning(pos, e.toString());
- return Type.NoType;
- }
+ public Tree tryToFold(Tree tree) {
+ AConstant value = evaluate(tree);
+ return value != null ? gen.Literal(tree.pos, value) : tree;
}
- /** fold unary operation.
+ /**
+ * Evaluates the expression represented by the tree and returns
+ * the resulting value. Returns null if the evaluation can't be
+ * performed.
*/
- Type foldUnary(int pos, ConstantType od, Name op) {
- try {
- Object value = null;
- switch (od.deconst().unbox()) {
- case UnboxedType(INT):
- if (op == Names.ADD)
- value = new Integer(od.intValue());
- else if (op == Names.SUB)
- value = new Integer(-od.intValue());
- else if (op == Names.NOT)
- value = new Integer(~od.intValue());
- break;
- case UnboxedType(LONG):
- if (op == Names.ADD)
- value = new Long(od.longValue());
- else if (op == Names.SUB)
- value = new Long(-od.longValue());
- else if (op == Names.NOT)
- value = new Long(~od.longValue());
- break;
- case UnboxedType(FLOAT):
- if (op == Names.ADD)
- value = new Float(od.floatValue());
- else if (op == Names.SUB)
- value = new Float(-od.floatValue());
- break;
- case UnboxedType(DOUBLE):
- if (op == Names.ADD)
- value = new Double(od.doubleValue());
- else if (op == Names.SUB)
- value = new Double(-od.doubleValue());
- break;
- case UnboxedType(BOOLEAN):
- if (op == Names.ZNOT)
- value = new Boolean(!od.booleanValue());
- break;
- }
- return (value != null) ? Type.constantType(value) : Type.NoType;
- } catch (ArithmeticException e) {
- ana.unit.warning(pos, e.toString());
- return Type.NoType;
+ public AConstant evaluate(Tree tree) {
+ switch (tree) {
+ case TypeApply(Select(Tree qualifier, Name op), Tree[] args):
+ switch (qualifier.type()) {
+ case ConstantType(_, AConstant lvalue):
+ if (args.length == 1 && op == Names.asInstanceOf)
+ return cast(lvalue, args[0].type());
+ return null;
+ default:
+ return null;
+ }
+ case Apply(Select(Tree qualifier, Name op), Tree[] args):
+ switch (qualifier.type()) {
+ case ConstantType(_, AConstant lvalue):
+ switch (args.length) {
+ case 0:
+ return evaluate(op, lvalue);
+ case 1:
+ switch (args[0].type()) {
+ case ConstantType(_, AConstant rvalue):
+ return evaluate(op, lvalue, rvalue);
+ default:
+ return null;
+ }
+ default:
+ return null;
+ }
+ default:
+ return null;
+ }
+ default:
+ return null;
}
}
- /** fold cast operation
+ /**
+ * Evaluates the unary operation and returns the resulting value.
+ * Returns null if the evaluation can't be performed.
*/
- Type foldAsInstanceOf(int pos, ConstantType od, Type argtype) {
- try {
- Object value = null;
- switch (argtype.unbox()) {
- case UnboxedType(BYTE):
- value = new Byte((byte)od.intValue());
- break;
- case UnboxedType(CHAR):
- value = new Character((char)od.intValue());
- break;
- case UnboxedType(SHORT):
- value = new Short((short)od.intValue());
- break;
- case UnboxedType(INT):
- value = new Integer(od.intValue());
- break;
- case UnboxedType(LONG):
- value = new Long(od.longValue());
- break;
- case UnboxedType(FLOAT):
- value = new Float(od.longValue());
- break;
- case UnboxedType(DOUBLE):
- value = new Double(od.doubleValue());
- break;
- case UnboxedType(BOOLEAN):
- value = new Boolean(od.booleanValue());
- break;
- }
- return (value != null) ? new ConstantType(argtype, value)
- : Type.NoType;
- } catch (ClassCastException e) {
- ana.unit.warning(pos, e.toString());
- return Type.NoType;
- }
+ public AConstant evaluate(Name op, AConstant value) {
+ switch (kind(value)) {
+ case ATypeKind.BOOL:
+ boolean v = value.booleanValue();
+ if (op == Names.ZNOT) return AConstant.BOOLEAN(!v);
+ return null;
+ case ATypeKind.I4:
+ int v = value.intValue();
+ if (op == Names.ADD) return AConstant.INT(+v);
+ if (op == Names.SUB) return AConstant.INT(-v);
+ if (op == Names.NOT) return AConstant.INT(~v);
+ return null;
+ case ATypeKind.I8:
+ long v = value.longValue();
+ if (op == Names.ADD) return AConstant.LONG(+v);
+ if (op == Names.SUB) return AConstant.LONG(-v);
+ if (op == Names.NOT) return AConstant.LONG(~v);
+ return null;
+ case ATypeKind.R4:
+ float v = value.floatValue();
+ if (op == Names.ADD) return AConstant.FLOAT(+v);
+ if (op == Names.SUB) return AConstant.FLOAT(-v);
+ return null;
+ case ATypeKind.R8:
+ double v = value.doubleValue();
+ if (op == Names.ADD) return AConstant.DOUBLE(+v);
+ if (op == Names.SUB) return AConstant.DOUBLE(-v);
+ return null;
+ default:
+ return null;
+ }
}
- /** attempt to constant fold tree.
+ /**
+ * Evaluates the binary operation and returns the resulting value.
+ * Returns null if the evaluation can't be performed.
*/
- Tree tryToFold(Tree tree) {
- Type ctp = Type.NoType;
- switch (tree) {
- case Apply(Select(Tree qual, Name op), Tree[] args):
- if (qual.type instanceof ConstantType) {
- if (args.length == 0)
- ctp = foldUnary(
- tree.pos, (ConstantType)qual.type, op);
- else if (args.length == 1 &&
- args[0].type instanceof ConstantType)
- ctp = foldBinary(
- tree.pos,
- (ConstantType)qual.type,
- (ConstantType)(args[0].type),
- op);
- }
- break;
- case TypeApply(Select(Tree qual, Name op), Tree[] targs):
- if (qual.type instanceof Type.ConstantType &&
- op == Names.asInstanceOf)
- ctp = foldAsInstanceOf(
- tree.pos,
- (ConstantType)qual.type,
- targs[0].type);
- break;
- }
- switch (ctp) {
- case ConstantType(Type base, Object value):
- return ana.make.Literal(tree.pos, value).setType(ctp);
- default:
- return tree;
- }
- }
+ public AConstant evaluate(Name op, AConstant lvalue, AConstant rvalue) {
+ ATypeKind kind = kind(lvalue, rvalue);
+ if (kind == null) return null;
+ switch (kind) {
-}
+ case ATypeKind.I4:
+ int l = lvalue.intValue();
+ int r = rvalue.intValue();
+ if (op == Names.OR ) return AConstant.INT(l | r);
+ if (op == Names.AND) return AConstant.INT(l & r);
+ if (op == Names.XOR) return AConstant.INT(l ^ r);
+ if (op == Names.ADD) return AConstant.INT(l + r);
+ if (op == Names.SUB) return AConstant.INT(l - r);
+ if (op == Names.MUL) return AConstant.INT(l * r);
+ if (op == Names.DIV) return r == 0 ? null : AConstant.INT(l / r);
+ if (op == Names.MOD) return r == 0 ? null : AConstant.INT(l % r);
+ if (op == Names.EQ ) return AConstant.BOOLEAN(l == r);
+ if (op == Names.NE ) return AConstant.BOOLEAN(l != r);
+ if (op == Names.LT ) return AConstant.BOOLEAN(l < r);
+ if (op == Names.GT ) return AConstant.BOOLEAN(l > r);
+ if (op == Names.LE ) return AConstant.BOOLEAN(l <= r);
+ if (op == Names.GE ) return AConstant.BOOLEAN(l >= r);
+ if (op == Names.LSL) return AConstant.INT(l << r);
+ if (op == Names.LSR) return AConstant.INT(l >>> r);
+ if (op == Names.ASR) return AConstant.INT(l >> r);
+ return null;
+ case ATypeKind.I8:
+ long l = lvalue.longValue();
+ long r = rvalue.longValue();
+ if (op == Names.OR ) return AConstant.LONG(l | r);
+ if (op == Names.AND) return AConstant.LONG(l & r);
+ if (op == Names.XOR) return AConstant.LONG(l ^ r);
+ if (op == Names.ADD) return AConstant.LONG(l + r);
+ if (op == Names.SUB) return AConstant.LONG(l - r);
+ if (op == Names.MUL) return AConstant.LONG(l * r);
+ if (op == Names.DIV) return r == 0 ? null : AConstant.LONG(l / r);
+ if (op == Names.MOD) return r == 0 ? null : AConstant.LONG(l % r);
+ if (op == Names.EQ ) return AConstant.BOOLEAN(l == r);
+ if (op == Names.NE ) return AConstant.BOOLEAN(l != r);
+ if (op == Names.LT ) return AConstant.BOOLEAN(l < r);
+ if (op == Names.GT ) return AConstant.BOOLEAN(l > r);
+ if (op == Names.LE ) return AConstant.BOOLEAN(l <= r);
+ if (op == Names.GE ) return AConstant.BOOLEAN(l >= r);
+ if (kind(lvalue) == ATypeKind.I4) {
+ int li = lvalue.intValue();
+ if (op == Names.LSL) return AConstant.INT(li << r);
+ if (op == Names.LSR) return AConstant.INT(li >>> r);
+ if (op == Names.ASR) return AConstant.INT(li >> r);
+ } else {
+ if (op == Names.LSL) return AConstant.LONG(l << r);
+ if (op == Names.LSR) return AConstant.LONG(l >>> r);
+ if (op == Names.ASR) return AConstant.LONG(l >> r);
+ }
+ return null;
+ case ATypeKind.R4:
+ float l = lvalue.floatValue();
+ float r = rvalue.floatValue();
+ if (op == Names.ADD) return AConstant.FLOAT(l + r);
+ if (op == Names.SUB) return AConstant.FLOAT(l - r);
+ if (op == Names.MUL) return AConstant.FLOAT(l * r);
+ if (op == Names.DIV) return AConstant.FLOAT(l / r);
+ if (op == Names.MOD) return AConstant.FLOAT(l % r);
+ if (op == Names.EQ ) return AConstant.BOOLEAN(l == r);
+ if (op == Names.NE ) return AConstant.BOOLEAN(l != r);
+ if (op == Names.LT ) return AConstant.BOOLEAN(l < r);
+ if (op == Names.GT ) return AConstant.BOOLEAN(l > r);
+ if (op == Names.LE ) return AConstant.BOOLEAN(l <= r);
+ if (op == Names.GE ) return AConstant.BOOLEAN(l >= r);
+ return null;
+ case ATypeKind.R8:
+ double l = lvalue.doubleValue();
+ double r = rvalue.doubleValue();
+ if (op == Names.ADD) return AConstant.DOUBLE(l + r);
+ if (op == Names.SUB) return AConstant.DOUBLE(l - r);
+ if (op == Names.MUL) return AConstant.DOUBLE(l * r);
+ if (op == Names.DIV) return AConstant.DOUBLE(l / r);
+ if (op == Names.MOD) return AConstant.DOUBLE(l % r);
+ if (op == Names.EQ ) return AConstant.BOOLEAN(l == r);
+ if (op == Names.NE ) return AConstant.BOOLEAN(l != r);
+ if (op == Names.LT ) return AConstant.BOOLEAN(l < r);
+ if (op == Names.GT ) return AConstant.BOOLEAN(l > r);
+ if (op == Names.LE ) return AConstant.BOOLEAN(l <= r);
+ if (op == Names.GE ) return AConstant.BOOLEAN(l >= r);
+ return null;
+ case ATypeKind.BOOL:
+ boolean l = lvalue.booleanValue();
+ boolean r = rvalue.booleanValue();
+ if (op == Names.ZOR ) return AConstant.BOOLEAN(l | r);
+ if (op == Names.OR ) return AConstant.BOOLEAN(l | r);
+ if (op == Names.ZAND) return AConstant.BOOLEAN(l & r);
+ if (op == Names.AND ) return AConstant.BOOLEAN(l & r);
+ if (op == Names.XOR ) return AConstant.BOOLEAN(l ^ r);
+ if (op == Names.EQ ) return AConstant.BOOLEAN(l == r);
+ if (op == Names.NE ) return AConstant.BOOLEAN(l != r);
+ return null;
+ case ATypeKind.STR:
+ if (op == Names.ADD)
+ return AConstant.STRING(lvalue.stringValue()+rvalue.stringValue());
+ return null;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Casts the value to given type and returns the resulting value.
+ * Returns null if the cast can't be performed.
+ */
+ public AConstant cast(AConstant value, Type type) {
+ switch (value.kind()) {
+ case UNIT:
+ if (type.isSameAs(definitions.UNIT_CLASS.type()))
+ return value;
+ return null;
+ case BOOL:
+ if (type.isSameAs(definitions.BOOLEAN_TYPE()))
+ return value;
+ return null;
+ case U1:
+ case U2:
+ case U4:
+ case U8:
+ case I1:
+ case I2:
+ case I4:
+ case I8:
+ case R4:
+ case R8:
+ if (type.isSameAs(definitions.BYTE_TYPE()))
+ return AConstant.BYTE(value.byteValue());
+ if (type.isSameAs(definitions.SHORT_TYPE()))
+ return AConstant.SHORT(value.shortValue());
+ if (type.isSameAs(definitions.CHAR_TYPE()))
+ return AConstant.CHAR(value.charValue());
+ if (type.isSameAs(definitions.INT_TYPE()))
+ return AConstant.INT(value.intValue());
+ if (type.isSameAs(definitions.LONG_TYPE()))
+ return AConstant.LONG(value.longValue());
+ if (type.isSameAs(definitions.FLOAT_TYPE()))
+ return AConstant.FLOAT(value.floatValue());
+ if (type.isSameAs(definitions.DOUBLE_TYPE()))
+ return AConstant.DOUBLE(value.doubleValue());
+ return null;
+ case STR:
+ if (type.isSameAs(definitions.JAVA_STRING_TYPE()))
+ return value;
+ return null;
+ default:
+ return null;
+ }
+ }
+
+ //########################################################################
+ // Private Methods
+
+ /** Returns the kind of given value. */
+ private ATypeKind kind(AConstant value) {
+ ATypeKind kind = value.kind();
+ switch (kind) {
+ case I1:
+ case I2:
+ case U2:
+ return ATypeKind.I4;
+ default:
+ return kind;
+ }
+ }
+
+ /**
+ * Returns the combined kind of given values or null if the values
+ * can't be combined.
+ */
+ private ATypeKind kind(AConstant lvalue, AConstant rvalue) {
+ ATypeKind lkind = kind(lvalue);
+ ATypeKind rkind = kind(rvalue);
+ if (lkind == rkind) return lkind;
+ if (lkind == ATypeKind.ZERO) return null;
+ if (rkind == ATypeKind.ZERO) return null;
+ if (lkind == ATypeKind.STR) return lkind;
+ if (rkind == ATypeKind.STR) return rkind;
+ switch (lkind) {
+ case I4:
+ switch (rkind) {
+ case I4: return lkind;
+ case I8: return rkind;
+ case R4: return rkind;
+ case R8: return rkind;
+ default: return null;
+ }
+ case I8:
+ switch (rkind) {
+ case I4: return lkind;
+ case I8: return lkind;
+ case R4: return rkind;
+ case R8: return rkind;
+ default: return null;
+ }
+ case R4:
+ switch (rkind) {
+ case I4: return lkind;
+ case I8: return lkind;
+ case R4: return lkind;
+ case R8: return rkind;
+ default: return null;
+ }
+ case R8:
+ switch (rkind) {
+ case I4: return lkind;
+ case I8: return lkind;
+ case R4: return lkind;
+ case R8: return lkind;
+ default: return null;
+ }
+ default:
+ return null;
+ }
+ }
+
+ //########################################################################
+}