mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-28 16:50:10 +00:00
8359335: Template-Framework Library: Primitive Types subtyping
Reviewed-by: chagedorn, epeter
This commit is contained in:
parent
e99ed13691
commit
262b31be3d
@ -52,6 +52,7 @@ import static compiler.lib.template_framework.Template.let;
|
||||
import static compiler.lib.template_framework.Template.$;
|
||||
import compiler.lib.template_framework.library.CodeGenerationDataNameType;
|
||||
import compiler.lib.template_framework.library.Expression;
|
||||
import compiler.lib.template_framework.library.Expression.Nesting;
|
||||
import compiler.lib.template_framework.library.Operations;
|
||||
import compiler.lib.template_framework.library.PrimitiveType;
|
||||
import compiler.lib.template_framework.library.TestFrameworkClass;
|
||||
@ -335,7 +336,7 @@ public class ExpressionFuzzer {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// The depth determines roughly how many operations are going to be used in the expression.
|
||||
int depth = RANDOM.nextInt(1, 20);
|
||||
Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, depth);
|
||||
Expression expression = Expression.nestRandomly(type, Operations.PRIMITIVE_OPERATIONS, depth, Nesting.EXACT);
|
||||
tests.add(testTemplate.asToken(expression));
|
||||
}
|
||||
}
|
||||
@ -350,7 +351,7 @@ public class ExpressionFuzzer {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// The depth determines roughly how many operations are going to be used in the expression.
|
||||
int depth = RANDOM.nextInt(1, 20);
|
||||
Expression expression = Expression.nestRandomly(type, Operations.SCALAR_NUMERIC_OPERATIONS, depth);
|
||||
Expression expression = Expression.nestRandomly(type, Operations.SCALAR_NUMERIC_OPERATIONS, depth, Nesting.EXACT);
|
||||
tests.add(testTemplate.asToken(expression));
|
||||
}
|
||||
}
|
||||
|
||||
@ -382,6 +382,23 @@ public class Expression {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Nesting} defines the different ways of selecting {@link Expression}s
|
||||
* to nest based on their types.
|
||||
*/
|
||||
public enum Nesting {
|
||||
/**
|
||||
* Only nest {@Expression}s where the argument and return types match exactly
|
||||
* based on the implementation of {@link CodeGenerateionDataNameType#isSubtypeOf}.
|
||||
*/
|
||||
EXACT,
|
||||
/**
|
||||
* Only nest {@Expression}s where the return type is a subtype of the argument
|
||||
* type based on the implemetation of {@link CodeGenerateionDataNameType#isSubtypeOf}.
|
||||
*/
|
||||
SUBTYPE
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a nested {@link Expression} with a specified {@code returnType} from a
|
||||
* set of {@code expressions}.
|
||||
@ -391,11 +408,13 @@ public class Expression {
|
||||
* the nested {@link Expression}.
|
||||
* @param maxNumberOfUsedExpressions the maximal number of {@link Expression}s from the
|
||||
* {@code expressions} are nested.
|
||||
* @param nesting control the {@link Nesting} of the sampled {@link Expression}s.
|
||||
* @return a new randomly nested {@link Expression}.
|
||||
*/
|
||||
public static Expression nestRandomly(CodeGenerationDataNameType returnType,
|
||||
List<Expression> expressions,
|
||||
int maxNumberOfUsedExpressions) {
|
||||
int maxNumberOfUsedExpressions,
|
||||
Nesting nesting) {
|
||||
List<Expression> filtered = expressions.stream().filter(e -> e.returnType.isSubtypeOf(returnType)).toList();
|
||||
|
||||
if (filtered.isEmpty()) {
|
||||
@ -406,7 +425,7 @@ public class Expression {
|
||||
Expression expression = filtered.get(r);
|
||||
|
||||
for (int i = 1; i < maxNumberOfUsedExpressions; i++) {
|
||||
expression = expression.nestRandomly(expressions);
|
||||
expression = expression.nestRandomly(expressions, nesting);
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
@ -416,12 +435,16 @@ public class Expression {
|
||||
* {@code this} {@link Expression}, ensuring compatibility of argument and return type.
|
||||
*
|
||||
* @param nestingExpressions list of expressions we sample from for the inner {@link Expression}.
|
||||
* @param nesting control the {@link Nesting} of the sampled {@link Expression}s.
|
||||
* @return a new nested {@link Expression}.
|
||||
*/
|
||||
public Expression nestRandomly(List<Expression> nestingExpressions) {
|
||||
public Expression nestRandomly(List<Expression> nestingExpressions, Nesting nesting) {
|
||||
int argumentIndex = RANDOM.nextInt(this.argumentTypes.size());
|
||||
CodeGenerationDataNameType argumentType = this.argumentTypes.get(argumentIndex);
|
||||
List<Expression> filtered = nestingExpressions.stream().filter(e -> e.returnType.isSubtypeOf(argumentType)).toList();
|
||||
List<Expression> filtered = nestingExpressions.stream()
|
||||
.filter(e -> e.returnType.isSubtypeOf(argumentType) &&
|
||||
(nesting == Nesting.EXACT ? argumentType.isSubtypeOf(e.returnType) : true))
|
||||
.toList();
|
||||
|
||||
if (filtered.isEmpty()) {
|
||||
// Found no expression that has a matching returnType.
|
||||
|
||||
@ -129,8 +129,17 @@ public final class Operations {
|
||||
ops.add(Expression.make(type, "(", type, " + ", type, ")"));
|
||||
ops.add(Expression.make(type, "(", type, " - ", type, ")"));
|
||||
ops.add(Expression.make(type, "(", type, " * ", type, ")"));
|
||||
ops.add(Expression.make(type, "(", type, " / ", type, ")"));
|
||||
ops.add(Expression.make(type, "(", type, " % ", type, ")"));
|
||||
// Because of subtyping, we can sample an expression like `(float)((int)(3) / (int)(0))`. Floating point
|
||||
// division and modulo do not throw an ArithmeticException on division by zero, integer division and modulo
|
||||
// do. In the expression above, the division has an integer on both sides, so it is executed as an integer
|
||||
// division and throws an ArithmeticException even though we would expect the float division not to do so.
|
||||
// To prevent this issue, we provide two versions of floating point division operations: one that casts
|
||||
// its operands and one that expects that an ArithmeticException might be thrown when we get unlucky when
|
||||
// sampling subtypes.
|
||||
ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") / (" + type.name() +")(", type, "))"));
|
||||
ops.add(Expression.make(type, "((" + type.name() + ")(", type, ") % (" + type.name() +")(", type, "))"));
|
||||
ops.add(Expression.make(type, "(", type, " / ", type, ")", WITH_ARITHMETIC_EXCEPTION));
|
||||
ops.add(Expression.make(type, "(", type, " % ", type, ")", WITH_ARITHMETIC_EXCEPTION));
|
||||
|
||||
// Relational / Comparison Operators
|
||||
ops.add(Expression.make(BOOLEANS, "(", type, " == ", type, ")"));
|
||||
|
||||
@ -71,7 +71,25 @@ public final class PrimitiveType implements CodeGenerationDataNameType {
|
||||
|
||||
@Override
|
||||
public boolean isSubtypeOf(DataName.Type other) {
|
||||
return (other instanceof PrimitiveType pt) && pt.kind == kind;
|
||||
// Implement other >: this according to JLS §4.10.1.
|
||||
if (other instanceof PrimitiveType superType) {
|
||||
if (superType.kind == Kind.BOOLEAN || kind == Kind.BOOLEAN) {
|
||||
// Boolean does not have a supertype and only itself as a subtype.
|
||||
return superType.kind == this.kind;
|
||||
}
|
||||
if (superType.kind == Kind.CHAR || kind == Kind.CHAR) {
|
||||
// Char does not have a subtype, but it is itself a subtype of any primitive type with
|
||||
// a larger byte size. The following is correct for the subtype relation to floats,
|
||||
// since chars are 16 bits wide and floats 32 bits or more.
|
||||
return superType.kind == this.kind || (superType.byteSize() > this.byteSize() && this.kind != Kind.BYTE);
|
||||
}
|
||||
// Due to float >: long, all integers are subtypes of floating point types.
|
||||
return (superType.isFloating() && !this.isFloating()) ||
|
||||
// Generally, narrower types are subtypes of wider types.
|
||||
(superType.isFloating() == this.isFloating() && superType.byteSize() >= this.byteSize());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -48,6 +48,7 @@ import static compiler.lib.template_framework.Template.let;
|
||||
import static compiler.lib.template_framework.Template.$;
|
||||
import static compiler.lib.template_framework.Template.addDataName;
|
||||
import static compiler.lib.template_framework.DataName.Mutability.MUTABLE;
|
||||
import static compiler.lib.template_framework.DataName.Mutability.MUTABLE_OR_IMMUTABLE;
|
||||
|
||||
import compiler.lib.template_framework.library.Hooks;
|
||||
import compiler.lib.template_framework.library.CodeGenerationDataNameType;
|
||||
@ -129,7 +130,8 @@ public class TestPrimitiveTypes {
|
||||
}
|
||||
|
||||
// Finally, test the type by creating some DataNames (variables), and sampling
|
||||
// from them. There should be no cross-over between the types.
|
||||
// from them. Sampling exactly should not lead to any conversion and sampling
|
||||
// subtypes should only lead to widening conversions.
|
||||
// IMPORTANT: since we are adding the DataName via an inserted Template, we
|
||||
// must chose a "transparentScope", so that the DataName escapes. If we
|
||||
// instead chose "scope", the test would fail, because it later
|
||||
@ -150,6 +152,14 @@ public class TestPrimitiveTypes {
|
||||
"""
|
||||
));
|
||||
|
||||
var assignmentTemplate = Template.make("lhsType", (PrimitiveType lhsType) -> scope(
|
||||
dataNames(MUTABLE).exactOf(lhsType).sampleAndLetAs("lhs"),
|
||||
dataNames(MUTABLE_OR_IMMUTABLE).subtypeOf(lhsType).sampleAndLetAs("rhs"),
|
||||
"""
|
||||
#lhs = #rhs;
|
||||
"""
|
||||
));
|
||||
|
||||
var namesTemplate = Template.make(() -> scope(
|
||||
"""
|
||||
public static void test_names() {
|
||||
@ -161,10 +171,16 @@ public class TestPrimitiveTypes {
|
||||
).toList()
|
||||
),
|
||||
"""
|
||||
// Now sample:
|
||||
// Sample exactly:
|
||||
""",
|
||||
Collections.nCopies(10,
|
||||
CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(sampleTemplate::asToken).toList()
|
||||
),
|
||||
"""
|
||||
// Sample subtypes:
|
||||
""",
|
||||
Collections.nCopies(10,
|
||||
CodeGenerationDataNameType.PRIMITIVE_TYPES.stream().map(assignmentTemplate::asToken).toList()
|
||||
)
|
||||
)),
|
||||
"""
|
||||
|
||||
@ -41,6 +41,7 @@ import compiler.lib.template_framework.TemplateToken;
|
||||
import static compiler.lib.template_framework.Template.scope;
|
||||
import compiler.lib.template_framework.library.CodeGenerationDataNameType;
|
||||
import compiler.lib.template_framework.library.Expression;
|
||||
import compiler.lib.template_framework.library.Expression.Nesting;
|
||||
|
||||
/**
|
||||
* This tests the use of the {@link Expression} from the template library. This is
|
||||
@ -174,44 +175,74 @@ public class TestExpression {
|
||||
Expression e4 = Expression.make(myTypeA1, "<", myTypeA, ">");
|
||||
Expression e5 = Expression.make(myTypeA, "[", myTypeB, "]");
|
||||
|
||||
Expression e1e2 = e1.nestRandomly(List.of(e2));
|
||||
Expression e1ex = e1.nestRandomly(List.of(e3, e2, e3));
|
||||
Expression e1e4 = e1.nestRandomly(List.of(e3, e4, e3));
|
||||
Expression e1ey = e1.nestRandomly(List.of(e3, e3));
|
||||
Expression e1e2 = e1.nestRandomly(List.of(e2), Nesting.SUBTYPE);
|
||||
Expression e1e2Exact = e1.nestRandomly(List.of(e2), Nesting.EXACT);
|
||||
Expression e1ex = e1.nestRandomly(List.of(e3, e2, e3), Nesting.SUBTYPE);
|
||||
Expression e1exExact = e1.nestRandomly(List.of(e3, e2, e3), Nesting.EXACT);
|
||||
Expression e1e4 = e1.nestRandomly(List.of(e3, e4, e3), Nesting.SUBTYPE);
|
||||
Expression e1e4Exact = e1.nestRandomly(List.of(e3, e4, e3), Nesting.EXACT);
|
||||
Expression e1ey = e1.nestRandomly(List.of(e3, e3), Nesting.SUBTYPE);
|
||||
Expression e1eyExact = e1.nestRandomly(List.of(e3, e3), Nesting.EXACT);
|
||||
|
||||
// 5-deep nesting of e1
|
||||
Expression deep1 = Expression.nestRandomly(myTypeA, List.of(e1, e3), 5);
|
||||
Expression deep1 = Expression.nestRandomly(myTypeA, List.of(e1, e3), 5, Nesting.SUBTYPE);
|
||||
Expression deep1Exact = Expression.nestRandomly(myTypeA, List.of(e1, e3), 5, Nesting.EXACT);
|
||||
// Alternating pattern
|
||||
Expression deep2 = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5);
|
||||
Expression deep2 = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5, Nesting.SUBTYPE);
|
||||
Expression deep2Exact = Expression.nestRandomly(myTypeA, List.of(e5, e3), 5, Nesting.SUBTYPE);
|
||||
|
||||
var template = Template.make(() -> scope(
|
||||
"xx", e1e2.toString(), "yy\n",
|
||||
"xx", e1e2Exact.toString(), "yy\n",
|
||||
"xx", e1ex.toString(), "yy\n",
|
||||
"xx", e1exExact.toString(), "yy\n",
|
||||
"xx", e1e4.toString(), "yy\n",
|
||||
"xx", e1e4Exact.toString(), "yy\n",
|
||||
"xx", e1ey.toString(), "yy\n",
|
||||
"xx", e1eyExact.toString(), "yy\n",
|
||||
"xx", deep1.toString(), "yy\n",
|
||||
"xx", deep1Exact.toString(), "yy\n",
|
||||
"xx", deep2.toString(), "yy\n",
|
||||
"xx", deep2Exact.toString(), "yy\n",
|
||||
"xx", e1e2.asToken(List.of("a")), "yy\n",
|
||||
"xx", e1e2Exact.asToken(List.of("a")), "yy\n",
|
||||
"xx", e1ex.asToken(List.of("a")), "yy\n",
|
||||
"xx", e1exExact.asToken(List.of("a")), "yy\n",
|
||||
"xx", e1e4.asToken(List.of("a")), "yy\n",
|
||||
"xx", e1e4Exact.asToken(List.of("a")), "yy\n",
|
||||
"xx", e1ey.asToken(List.of("a")), "yy\n",
|
||||
"xx", e1eyExact.asToken(List.of("a")), "yy\n",
|
||||
"xx", deep1.asToken(List.of("a")), "yy\n",
|
||||
"xx", deep2.asToken(List.of("a")), "yy\n"
|
||||
"xx", deep1Exact.asToken(List.of("a")), "yy\n",
|
||||
"xx", deep2.asToken(List.of("a")), "yy\n",
|
||||
"xx", deep2Exact.asToken(List.of("a")), "yy\n"
|
||||
));
|
||||
|
||||
String expected =
|
||||
"""
|
||||
xxExpression["[(", MyTypeA, ")]"]yy
|
||||
xxExpression["[(", MyTypeA, ")]"]yy
|
||||
xxExpression["[(", MyTypeA, ")]"]yy
|
||||
xxExpression["[(", MyTypeA, ")]"]yy
|
||||
xxExpression["[<", MyTypeA, ">]"]yy
|
||||
xxExpression["[", MyTypeA, "]"]yy
|
||||
xxExpression["[", MyTypeA, "]"]yy
|
||||
xxExpression["[", MyTypeA, "]"]yy
|
||||
xxExpression["[[[[[", MyTypeA, "]]]]]"]yy
|
||||
xxExpression["[[[[[", MyTypeA, "]]]]]"]yy
|
||||
xxExpression["[{[{[", MyTypeB, "]}]}]"]yy
|
||||
xxExpression["[{[{[", MyTypeB, "]}]}]"]yy
|
||||
xx[(a)]yy
|
||||
xx[(a)]yy
|
||||
xx[(a)]yy
|
||||
xx[(a)]yy
|
||||
xx[<a>]yy
|
||||
xx[a]yy
|
||||
xx[a]yy
|
||||
xx[a]yy
|
||||
xx[[[[[a]]]]]yy
|
||||
xx[[[[[a]]]]]yy
|
||||
xx[{[{[a]}]}]yy
|
||||
xx[{[{[a]}]}]yy
|
||||
""";
|
||||
String code = template.render();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user