You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by da...@apache.org on 2019/10/23 06:37:18 UTC
[calcite] branch master updated: [CALCITE-3414] In calcite-core,
use RexToLixTranslator.convert for type conversion code generation
uniformly (DonnyZone)
This is an automated email from the ASF dual-hosted git repository.
danny0405 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push:
new 429c0d0 [CALCITE-3414] In calcite-core, use RexToLixTranslator.convert for type conversion code generation uniformly (DonnyZone)
429c0d0 is described below
commit 429c0d0e4cacc76933ae6cc651e5e4164f868383
Author: wellfengzhu <we...@gmail.com>
AuthorDate: Mon Oct 14 20:44:01 2019 +0800
[CALCITE-3414] In calcite-core, use RexToLixTranslator.convert for type conversion code generation uniformly (DonnyZone)
close apache/calcite#1507
---
.../adapter/enumerable/EnumerableMatch.java | 10 +++---
.../adapter/enumerable/EnumerableMergeJoin.java | 9 +++--
.../calcite/adapter/enumerable/PhysTypeImpl.java | 20 +++++------
.../calcite/adapter/enumerable/RexImpTable.java | 23 +++++++------
.../adapter/enumerable/RexToLixTranslator.java | 39 ++++++++++++++++------
.../adapter/enumerable/StrictAggImplementor.java | 3 +-
.../calcite/adapter/java/ReflectiveSchema.java | 8 ++---
.../java/org/apache/calcite/schema/Schemas.java | 4 +--
.../java/org/apache/calcite/test/JdbcTest.java | 10 ++++--
.../apache/calcite/test/ReflectiveSchemaTest.java | 4 +--
.../java/org/apache/calcite/linq4j/tree/Types.java | 28 +++++++++++++---
11 files changed, 99 insertions(+), 59 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMatch.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMatch.java
index e56e0bd..5cc63df 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMatch.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMatch.java
@@ -176,8 +176,9 @@ public class EnumerableMatch extends Match implements EnumerableRel {
// Add loop variable initialization
builder2.add(
Expressions.declare(0, row_,
- Types.castIfNecessary(inputPhysType.getJavaRowType(),
- Expressions.call(rows_, BuiltInMethod.LIST_GET.method, i_))));
+ RexToLixTranslator.convert(
+ Expressions.call(rows_, BuiltInMethod.LIST_GET.method, i_),
+ inputPhysType.getJavaRowType())));
RexBuilder rexBuilder = new RexBuilder(implementor.getTypeFactory());
RexProgramBuilder rexProgramBuilder =
@@ -475,9 +476,10 @@ public class EnumerableMatch extends Match implements EnumerableRel {
return Expressions.condition(
Expressions.greaterThanOrEqual(this.index, Expressions.constant(0)),
generator.apply(
- Types.castIfNecessary(physType.getJavaRowType(),
+ RexToLixTranslator.convert(
Expressions.call(this.passedRows,
- BuiltInMethod.LIST_GET.method, this.index)))
+ BuiltInMethod.LIST_GET.method, this.index),
+ physType.getJavaRowType()))
.field(list, index, storageType),
Expressions.constant(null));
}
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java
index d17184b..aa86c61 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java
@@ -21,7 +21,6 @@ import org.apache.calcite.linq4j.tree.BlockBuilder;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.Expressions;
import org.apache.calcite.linq4j.tree.ParameterExpression;
-import org.apache.calcite.linq4j.tree.Types;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptCost;
import org.apache.calcite.plan.RelOptPlanner;
@@ -146,11 +145,11 @@ public class EnumerableMergeJoin extends Join implements EnumerableRel {
right.getRowType().getFieldList().get(pair.right).getType()));
final Type keyClass = typeFactory.getJavaClass(keyType);
leftExpressions.add(
- Types.castIfNecessary(keyClass,
- leftResult.physType.fieldReference(left_, pair.left)));
+ RexToLixTranslator.convert(
+ leftResult.physType.fieldReference(left_, pair.left), keyClass));
rightExpressions.add(
- Types.castIfNecessary(keyClass,
- rightResult.physType.fieldReference(right_, pair.right)));
+ RexToLixTranslator.convert(
+ rightResult.physType.fieldReference(right_, pair.right), keyClass));
}
final PhysType leftKeyPhysType =
leftResult.physType.project(joinInfo.leftKeys, JavaRowFormat.LIST);
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java
index 402f4ac..a48d0eb 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java
@@ -219,9 +219,9 @@ public class PhysTypeImpl implements PhysType {
final List<Expression> expressions = new ArrayList<>();
for (int field : argList) {
expressions.add(
- Types.castIfNecessary(
- fieldClass(field),
- fieldReference(v1, field)));
+ RexToLixTranslator.convert(
+ fieldReference(v1, field),
+ fieldClass(field)));
}
return expressions;
}
@@ -308,8 +308,8 @@ public class PhysTypeImpl implements PhysType {
Expression arg1 = fieldReference(parameterV1, index);
switch (Primitive.flavor(fieldClass(index))) {
case OBJECT:
- arg0 = Types.castIfNecessary(Comparable.class, arg0);
- arg1 = Types.castIfNecessary(Comparable.class, arg1);
+ arg0 = RexToLixTranslator.convert(arg0, Comparable.class);
+ arg1 = RexToLixTranslator.convert(arg1, Comparable.class);
}
final boolean nullsFirst =
collation.nullDirection
@@ -407,8 +407,8 @@ public class PhysTypeImpl implements PhysType {
Expression arg1 = fieldReference(parameterV1, index);
switch (Primitive.flavor(fieldClass(index))) {
case OBJECT:
- arg0 = Types.castIfNecessary(Comparable.class, arg0);
- arg1 = Types.castIfNecessary(Comparable.class, arg1);
+ arg0 = RexToLixTranslator.convert(arg0, Comparable.class);
+ arg1 = RexToLixTranslator.convert(arg1, Comparable.class);
}
final boolean nullsFirst =
fieldCollation.nullDirection
@@ -566,9 +566,9 @@ public class PhysTypeImpl implements PhysType {
// }
Class returnType = fieldClasses.get(field0);
Expression fieldReference =
- Types.castIfNecessary(
- returnType,
- fieldReference(v1, field0));
+ RexToLixTranslator.convert(
+ fieldReference(v1, field0),
+ returnType);
return Expressions.lambda(
Function1.class,
fieldReference,
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 1737967..95bfac6 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -32,7 +32,6 @@ import org.apache.calcite.linq4j.tree.MethodCallExpression;
import org.apache.calcite.linq4j.tree.OptimizeShuttle;
import org.apache.calcite.linq4j.tree.ParameterExpression;
import org.apache.calcite.linq4j.tree.Primitive;
-import org.apache.calcite.linq4j.tree.Types;
import org.apache.calcite.linq4j.tree.UnaryExpression;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
@@ -1097,7 +1096,7 @@ public class RexImpTable {
final Expression ifTrue;
switch (nullAs) {
case NULL:
- ifTrue = Types.castIfNecessary(box.getType(), NULL_EXPR);
+ ifTrue = RexToLixTranslator.convert(NULL_EXPR, box.getType());
break;
case IS_NULL:
ifTrue = TRUE_EXPR;
@@ -1380,7 +1379,7 @@ public class RexImpTable {
next = Expressions.call(acc, "add", add.arguments().get(0));
} else {
next = Expressions.add(acc,
- Types.castIfNecessary(acc.type, add.arguments().get(0)));
+ RexToLixTranslator.convert(add.arguments().get(0), acc.type));
}
accAdvance(add, acc, next);
}
@@ -2200,9 +2199,9 @@ public class RexImpTable {
private Expression call(Expression operand, Type type,
TimeUnit timeUnit) {
return Expressions.call(SqlFunctions.class, methodName,
- Types.castIfNecessary(type, operand),
- Types.castIfNecessary(type,
- Expressions.constant(timeUnit.multiplier)));
+ RexToLixTranslator.convert(operand, type),
+ RexToLixTranslator.convert(
+ Expressions.constant(timeUnit.multiplier), type));
}
}
@@ -2228,7 +2227,7 @@ public class RexImpTable {
final Type returnType =
translator.typeFactory.getJavaClass(call.getType());
- return Types.castIfNecessary(returnType, expression);
+ return RexToLixTranslator.convert(expression, returnType);
}
}
@@ -2324,9 +2323,10 @@ public class RexImpTable {
final Type returnType =
translator.typeFactory.getJavaClass(call.getType());
- return Types.castIfNecessary(returnType,
+ return RexToLixTranslator.convert(
Expressions.makeBinary(expressionType, expressions.get(0),
- expressions.get(1)));
+ expressions.get(1)),
+ returnType);
}
/** Returns whether any of a call's operands have ANY type. */
@@ -2940,8 +2940,9 @@ public class RexImpTable {
@Override public Expression implement(RexToLixTranslator translator, RexCall call,
ParameterExpression row, ParameterExpression rows,
ParameterExpression symbols, ParameterExpression i) {
- return Types.castIfNecessary(String.class,
- Expressions.call(symbols, BuiltInMethod.LIST_GET.method, i));
+ return RexToLixTranslator.convert(
+ Expressions.call(symbols, BuiltInMethod.LIST_GET.method, i),
+ String.class);
}
}
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
index 200f552..a791157 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexToLixTranslator.java
@@ -30,6 +30,7 @@ import org.apache.calcite.linq4j.tree.Expressions;
import org.apache.calcite.linq4j.tree.ParameterExpression;
import org.apache.calcite.linq4j.tree.Primitive;
import org.apache.calcite.linq4j.tree.Statement;
+import org.apache.calcite.linq4j.tree.Types;
import org.apache.calcite.linq4j.tree.UnaryExpression;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactoryImpl;
@@ -963,7 +964,7 @@ public class RexToLixTranslator {
public static Expression convert(Expression operand, Type fromType,
Type toType) {
- if (fromType.equals(toType)) {
+ if (!Types.needTypeCast(fromType, toType)) {
return operand;
}
// E.g. from "Short" to "int".
@@ -1050,7 +1051,15 @@ public class RexToLixTranslator {
return Expressions.box(una.expression, toBox);
}
}
- return Expressions.box(operand, toBox);
+ if (fromType == toBox.primitiveClass) {
+ return Expressions.box(operand, toBox);
+ }
+ // E.g., from "int" to "Byte".
+ // Convert it first and generate "Byte.valueOf((byte)x)"
+ // Because there is no method "Byte.valueOf(int)" in Byte
+ return Expressions.box(
+ Expressions.convert_(operand, toBox.primitiveClass),
+ toBox);
} else if (fromType == java.sql.Date.class) {
if (toBox == Primitive.INT) {
return Expressions.call(BuiltInMethod.DATE_TO_INT.method, operand);
@@ -1134,7 +1143,7 @@ public class RexToLixTranslator {
}
} else if (fromType == BigDecimal.class) {
// E.g. from "BigDecimal" to "String"
- // Generate "x.toString()"
+ // Generate "SqlFunctions.toString(x)"
return Expressions.condition(
Expressions.equal(operand, RexImpTable.NULL_EXPR),
RexImpTable.NULL_EXPR,
@@ -1143,14 +1152,22 @@ public class RexToLixTranslator {
"toString",
operand));
} else {
- // E.g. from "BigDecimal" to "String"
- // Generate "x == null ? null : x.toString()"
- return Expressions.condition(
- Expressions.equal(operand, RexImpTable.NULL_EXPR),
- RexImpTable.NULL_EXPR,
- Expressions.call(
- operand,
- "toString"));
+ Expression result;
+ try {
+ // Try to call "toString()" method
+ // E.g. from "Integer" to "String"
+ // Generate "x == null ? null : x.toString()"
+ result = Expressions.condition(
+ Expressions.equal(operand, RexImpTable.NULL_EXPR),
+ RexImpTable.NULL_EXPR,
+ Expressions.call(operand, "toString"));
+ } catch (RuntimeException e) {
+ // For some special cases, e.g., "BuiltInMethod.LESSER",
+ // its return type is generic ("Comparable"), which contains
+ // no "toString()" method. We fall through to "(String)x".
+ return Expressions.convert_(operand, toType);
+ }
+ return result;
}
}
return Expressions.convert_(operand, toType);
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/StrictAggImplementor.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/StrictAggImplementor.java
index a34813c..3c85ddf 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/StrictAggImplementor.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/StrictAggImplementor.java
@@ -22,7 +22,6 @@ import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.Expressions;
import org.apache.calcite.linq4j.tree.ParameterExpression;
import org.apache.calcite.linq4j.tree.Primitive;
-import org.apache.calcite.linq4j.tree.Types;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexNode;
@@ -55,7 +54,7 @@ public abstract class StrictAggImplementor implements AggImplementor {
Expression next) {
add.currentBlock().add(
Expressions.statement(
- Expressions.assign(acc, Types.castIfNecessary(acc.type, next))));
+ Expressions.assign(acc, RexToLixTranslator.convert(next, acc.type))));
}
public final List<Type> getStateType(AggContext info) {
diff --git a/core/src/main/java/org/apache/calcite/adapter/java/ReflectiveSchema.java b/core/src/main/java/org/apache/calcite/adapter/java/ReflectiveSchema.java
index 89cb2ff..60a6ea2 100644
--- a/core/src/main/java/org/apache/calcite/adapter/java/ReflectiveSchema.java
+++ b/core/src/main/java/org/apache/calcite/adapter/java/ReflectiveSchema.java
@@ -17,6 +17,7 @@
package org.apache.calcite.adapter.java;
import org.apache.calcite.DataContext;
+import org.apache.calcite.adapter.enumerable.RexToLixTranslator;
import org.apache.calcite.linq4j.Enumerable;
import org.apache.calcite.linq4j.Enumerator;
import org.apache.calcite.linq4j.Linq4j;
@@ -26,7 +27,6 @@ import org.apache.calcite.linq4j.function.Function1;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.Expressions;
import org.apache.calcite.linq4j.tree.Primitive;
-import org.apache.calcite.linq4j.tree.Types;
import org.apache.calcite.rel.RelReferentialConstraint;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
@@ -167,13 +167,13 @@ public class ReflectiveSchema
/** Returns an expression for the object wrapped by this schema (not the
* schema itself). */
Expression getTargetExpression(SchemaPlus parentSchema, String name) {
- return Types.castIfNecessary(
- target.getClass(),
+ return RexToLixTranslator.convert(
Expressions.call(
Schemas.unwrap(
getExpression(parentSchema, name),
ReflectiveSchema.class),
- BuiltInMethod.REFLECTIVE_SCHEMA_GET_TARGET.method));
+ BuiltInMethod.REFLECTIVE_SCHEMA_GET_TARGET.method),
+ target.getClass());
}
/** Returns a table based on a particular field of this schema. If the
diff --git a/core/src/main/java/org/apache/calcite/schema/Schemas.java b/core/src/main/java/org/apache/calcite/schema/Schemas.java
index b303358..aa1b425 100644
--- a/core/src/main/java/org/apache/calcite/schema/Schemas.java
+++ b/core/src/main/java/org/apache/calcite/schema/Schemas.java
@@ -17,6 +17,7 @@
package org.apache.calcite.schema;
import org.apache.calcite.DataContext;
+import org.apache.calcite.adapter.enumerable.RexToLixTranslator;
import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.config.CalciteConnectionConfig;
import org.apache.calcite.config.CalciteConnectionConfigImpl;
@@ -30,7 +31,6 @@ import org.apache.calcite.linq4j.Queryable;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.linq4j.tree.Expressions;
import org.apache.calcite.linq4j.tree.MethodCallExpression;
-import org.apache.calcite.linq4j.tree.Types;
import org.apache.calcite.materialize.Lattice;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
@@ -176,7 +176,7 @@ public final class Schemas {
Expressions.constant(elementType),
Expressions.constant(tableName));
}
- return Types.castIfNecessary(clazz, expression);
+ return RexToLixTranslator.convert(expression, clazz);
}
public static DataContext createDataContext(
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 8cca9ed..5fb8f71 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -2408,7 +2408,7 @@ public class JdbcTest {
.planContains("return inp2_ == null "
+ "|| $L4J$C$_org_apache_calcite_runtime_SqlFunctions_ne_ ? (String) null"
+ " : org.apache.calcite.runtime.SqlFunctions.substring(inp2_, "
- + "current.deptno + 1);");
+ + "Integer.valueOf(current.deptno + 1).intValue());");
}
@Test public void testReuseExpressionWhenNullChecking4() {
@@ -2439,7 +2439,7 @@ public class JdbcTest {
+ ": org.apache.calcite.runtime.SqlFunctions.substring("
+ "org.apache.calcite.runtime.SqlFunctions.trim(true, true, \" \", "
+ "org.apache.calcite.runtime.SqlFunctions.substring(inp2_, "
- + "inp1_ * 0 + 1), true), (v5 ? 4 : 5) - 2);")
+ + "Integer.valueOf(inp1_ * 0 + 1).intValue()), true), Integer.valueOf((v5 ? 4 : 5) - 2).intValue());")
.returns("T=ill\n"
+ "T=ric\n"
+ "T=ebastian\n"
@@ -2463,6 +2463,10 @@ public class JdbcTest {
"final int inp1_ = current.deptno;")
.planContains(
"static final int $L4J$C$5_2 = 5 - 2;")
+ .planContains(
+ "static final Integer $L4J$C$Integer_valueOf_5_2_ = Integer.valueOf($L4J$C$5_2);")
+ .planContains(
+ "static final int $L4J$C$Integer_valueOf_5_2_intValue_ = $L4J$C$Integer_valueOf_5_2_.intValue();")
.planContains("static final boolean "
+ "$L4J$C$org_apache_calcite_runtime_SqlFunctions_eq_ = "
+ "org.apache.calcite.runtime.SqlFunctions.eq(\"\", \"\");")
@@ -2476,7 +2480,7 @@ public class JdbcTest {
+ ": org.apache.calcite.runtime.SqlFunctions.substring("
+ "org.apache.calcite.runtime.SqlFunctions.trim(true, true, \" \", "
+ "org.apache.calcite.runtime.SqlFunctions.substring(inp2_, "
- + "inp1_ * 0 + 1), true), $L4J$C$5_2);")
+ + "Integer.valueOf(inp1_ * 0 + 1).intValue()), true), $L4J$C$Integer_valueOf_5_2_intValue_);")
.returns("T=ll\n"
+ "T=ic\n"
+ "T=bastian\n"
diff --git a/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java b/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java
index 0c9b328..95f30c3 100644
--- a/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java
+++ b/core/src/test/java/org/apache/calcite/test/ReflectiveSchemaTest.java
@@ -612,8 +612,8 @@ public class ReflectiveSchemaTest {
"final Long inp13_ = ((org.apache.calcite.test.ReflectiveSchemaTest.EveryType) inputEnumerator.current()).wrapperLong;")
.planContains(
"return inp13_ == null ? (Long) null "
- + ": Long.valueOf(inp13_.longValue() / inp13_.longValue() "
- + "+ inp13_.longValue() / inp13_.longValue());")
+ + ": Long.valueOf(Long.valueOf(inp13_.longValue() / inp13_.longValue()).longValue() "
+ + "+ Long.valueOf(inp13_.longValue() / inp13_.longValue()).longValue());")
.returns("C=null\n");
}
diff --git a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Types.java b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Types.java
index 65a2726..1297c23 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Types.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/Types.java
@@ -427,11 +427,7 @@ public abstract class Types {
public static Expression castIfNecessary(Type returnType,
Expression expression) {
final Type type = expression.getType();
- if (returnType instanceof RecordType) {
- // We can't extract Class from RecordType since mapping Java Class might not generated yet.
- return expression;
- }
- if (Types.isAssignableFrom(returnType, type)) {
+ if (!needTypeCast(type, returnType)) {
return expression;
}
if (returnType instanceof Class
@@ -464,6 +460,28 @@ public abstract class Types {
return Expressions.convert_(expression, returnType);
}
+ /**
+ * When trying to cast/convert a {@code Type} to another {@code Type},
+ * it is necessary to pre-check whether the cast operation is needed.
+ * We summarize general exceptions, including:
+ *
+ * <ol>
+ * <li>target Type {@code toType} equals with original Type {@code fromType}</li>
+ * <li>target Type can be assignable from original Type</li>
+ * <li>target Type is an instance of {@code RecordType},
+ * since the mapping Java Class might not generated yet</li>
+ * </ol>
+ *
+ * @param fromType original type
+ * @param toType target type
+ * @return Whether a cast operation is needed
+ */
+ public static boolean needTypeCast(Type fromType, Type toType) {
+ return !(fromType.equals(toType)
+ || toType instanceof RecordType
+ || isAssignableFrom(toType, fromType));
+ }
+
public static PseudoField field(final Field field) {
return new ReflectedPseudoField(field);
}