8315457: Implement JEP 459: String Templates (Second Preview)

Reviewed-by: jlahoda, alanb, vromero
This commit is contained in:
Jim Laskey 2023-11-17 12:53:49 +00:00
parent 5522656af7
commit 9902d2eb17
16 changed files with 34 additions and 50 deletions

View File

@ -46,7 +46,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.RejectedExecutionException;
import java.util.stream.Stream;
import jdk.internal.javac.PreviewFeature;
import jdk.internal.misc.CarrierThreadLocal;
import jdk.internal.module.ServicesCatalog;
import jdk.internal.reflect.ConstantPool;
@ -420,19 +419,16 @@ public interface JavaLangAccess {
/**
* Get the coder for the supplied character.
*/
@PreviewFeature(feature=PreviewFeature.Feature.STRING_TEMPLATES)
long stringConcatCoder(char value);
/**
* Update lengthCoder for StringBuilder.
*/
@PreviewFeature(feature=PreviewFeature.Feature.STRING_TEMPLATES)
long stringBuilderConcatMix(long lengthCoder, StringBuilder sb);
/**
* Prepend StringBuilder content.
*/
@PreviewFeature(feature=PreviewFeature.Feature.STRING_TEMPLATES)
long stringBuilderConcatPrepend(long lengthCoder, byte[] buf, StringBuilder sb);
/**

View File

@ -67,8 +67,7 @@ public @interface PreviewFeature {
// not used, but required for interim javac to not warn.
VIRTUAL_THREADS,
FOREIGN,
@JEP(number=430, title="String Templates")
@JEP(number=459, title="String Templates", status="Second Preview")
STRING_TEMPLATES,
@JEP(number=445, title="Unnamed Classes and Instance Main Methods")
UNNAMED_CLASSES,

View File

@ -177,6 +177,7 @@ public interface Tree {
/**
* Used for instances of {@link StringTemplateTree}.
* @since 21
*/
@PreviewFeature(feature=PreviewFeature.Feature.STRING_TEMPLATES, reflective=true)
TEMPLATE(StringTemplateTree.class),

View File

@ -264,6 +264,7 @@ public interface TreeVisitor<R,P> {
* @param node the node being visited
* @param p a parameter value
* @return a result value
* @since 21
*/
@PreviewFeature(feature=PreviewFeature.Feature.STRING_TEMPLATES, reflective=true)
R visitStringTemplate(StringTemplateTree node, P p);

View File

@ -634,6 +634,7 @@ public class SimpleTreeVisitor <R,P> implements TreeVisitor<R,P> {
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of {@code defaultAction}
* @since 21
*/
@Override
@PreviewFeature(feature=PreviewFeature.Feature.STRING_TEMPLATES, reflective=true)

View File

@ -783,6 +783,7 @@ public class TreeScanner<R,P> implements TreeVisitor<R,P> {
* @param node {@inheritDoc}
* @param p {@inheritDoc}
* @return the result of scanning
* @since 21
*/
@Override
@PreviewFeature(feature=PreviewFeature.Feature.STRING_TEMPLATES, reflective=true)

View File

@ -5024,12 +5024,11 @@ public class Attr extends JCTree.Visitor {
public void visitStringTemplate(JCStringTemplate tree) {
JCExpression processor = tree.processor;
Type resultType = syms.stringTemplateType;
if (processor != null) {
resultType = attribTree(processor, env, new ResultInfo(KindSelector.VAL, Type.noType));
resultType = chk.checkProcessorType(processor, resultType, env);
}
Type processorType = attribTree(processor, env, new ResultInfo(KindSelector.VAL, Type.noType));
chk.checkProcessorType(processor, processorType, env);
Type processMethodType = getProcessMethodType(tree, processorType);
tree.processMethodType = processMethodType;
Type resultType = processMethodType.getReturnType();
Env<AttrContext> localEnv = env.dup(tree, env.info.dup());
@ -5039,10 +5038,16 @@ public class Attr extends JCTree.Visitor {
tree.type = resultType;
result = resultType;
check(tree, resultType, KindSelector.VAL, resultInfo);
}
private Type getProcessMethodType(JCStringTemplate tree, Type processorType) {
MethodSymbol processSymbol = rs.resolveInternalMethod(tree.pos(),
env, types.skipTypeVars(processorType, false),
names.process, List.of(syms.stringTemplateType), List.nil());
return types.memberType(processorType, processSymbol);
}
public void visitTypeIdent(JCPrimitiveTypeTree tree) {
result = check(tree, syms.typeOfTag[tree.typetag.ordinal()], KindSelector.TYP, resultInfo);
}

View File

@ -4370,8 +4370,7 @@ public class Check {
if (typeArguments.size() == 2) {
resultType = typeArguments.head;
} else {
log.error(DiagnosticFlag.RESOLVE_ERROR, processor.pos,
Errors.ProcessorTypeCannotBeARawType(processorType.tsym));
resultType = syms.objectType;
}
} else {
log.error(DiagnosticFlag.RESOLVE_ERROR, processor.pos,

View File

@ -1678,23 +1678,8 @@ public class Flow {
@Override
public void visitStringTemplate(JCStringTemplate tree) {
JCExpression processor = tree.processor;
if (processor != null) {
scan(processor);
Type interfaceType = types.asSuper(processor.type, syms.processorType.tsym);
if (interfaceType != null) {
List<Type> typeArguments = interfaceType.getTypeArguments();
if (typeArguments.size() == 2) {
Type throwType = typeArguments.tail.head;
if (throwType != null) {
markThrown(tree, throwType);
}
}
}
for (Type thrown : tree.processMethodType.getThrownTypes()) {
markThrown(tree, thrown);
}
scan(tree.expressions);

View File

@ -254,7 +254,12 @@ public final class TransLiterals extends TreeTranslator {
}
boolean isNamedProcessor(Name name) {
if (processor instanceof JCIdent ident && ident.sym instanceof VarSymbol varSym) {
Symbol sym = switch (processor) {
case JCIdent ident -> ident.sym;
case JCFieldAccess access -> access.sym;
default -> null;
};
if (sym instanceof VarSymbol varSym) {
if (varSym.flags() == (Flags.PUBLIC | Flags.FINAL | Flags.STATIC) &&
varSym.name == name &&
types.isSameType(varSym.owner.type, syms.stringTemplateType)) {
@ -265,8 +270,7 @@ public final class TransLiterals extends TreeTranslator {
}
boolean isLinkageProcessor() {
return processor != null &&
!useValuesList &&
return !useValuesList &&
types.isSubtype(processor.type, syms.linkageType) &&
processor.type.isFinal() &&
TreeInfo.symbol(processor) instanceof VarSymbol varSymbol &&
@ -278,7 +282,7 @@ public final class TransLiterals extends TreeTranslator {
JCExpression result;
make.at(tree.pos);
if (processor == null || isNamedProcessor(names.RAW)) {
if (isNamedProcessor(names.RAW)) {
result = newStringTemplate();
} else if (isNamedProcessor(names.STR)) {
result = concatExpression(fragments, expressions);

View File

@ -1354,10 +1354,6 @@ compiler.err.text.block.template.is.not.well.formed=\
compiler.err.processor.missing.from.string.template.expression=\
processor missing from string template expression
# 0: symbol
compiler.err.processor.type.cannot.be.a.raw.type=\
processor type cannot be a raw type: {0}
# 0: symbol
compiler.err.not.a.processor.type=\
not a processor type: {0}

View File

@ -2488,6 +2488,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
public JCExpression processor;
public List<String> fragments;
public List<JCExpression> expressions;
public Type processMethodType;
protected JCStringTemplate(JCExpression processor,
List<String> fragments,

View File

@ -1480,9 +1480,7 @@ public class Pretty extends JCTree.Visitor {
try {
JCExpression processor = tree.processor;
print("[");
if (processor != null) {
printExpr(processor);
}
print("]");
print("\"" + tree.fragments.stream().collect(Collectors.joining("\\{}")) + "\"");
print("(");

View File

@ -548,11 +548,7 @@ public class TreeInfo {
}
case STRING_TEMPLATE: {
JCStringTemplate node = (JCStringTemplate) tree;
if (node.processor == null) {
return node.pos;
} else {
return getStartPos(node.processor);
}
return node.processor == null ? node.pos : getStartPos(node.processor);
}
case ERRONEOUS: {
JCErroneous node = (JCErroneous)tree;

View File

@ -22,6 +22,8 @@
*/
// key: compiler.note.preview.filename
// key: compiler.err.cant.resolve.location.args
// key: compiler.misc.location
// key: compiler.note.preview.recompile
// key: compiler.err.not.a.processor.type
// options: --enable-preview -source ${jdk.version}

View File

@ -25,7 +25,6 @@
// key: compiler.note.preview.recompile
// key: compiler.misc.unexpected.ret.val
// key: compiler.err.prob.found.req
// key: compiler.err.processor.type.cannot.be.a.raw.type
// options: --enable-preview -source ${jdk.version}
import java.lang.*;