Array access as LValue resolution fixed

This commit is contained in:
Andrey Breslav
2011-08-26 21:40:37 +04:00
parent 6f410eace1
commit d38fbbe2c6
5 changed files with 275 additions and 33 deletions
@@ -1478,6 +1478,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> {
}
else {
final PsiElement declaration = BindingContextUtils.resolveToDeclarationPsiElement(bindingContext, expression);
assert declaration != null : "No declaration found for " + expression.getText();
final CallableMethod accessor;
if (declaration instanceof PsiMethod) {
accessor = JetTypeMapper.mapToCallableMethod((PsiMethod) declaration);
@@ -1486,7 +1487,7 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> {
accessor = typeMapper.mapToCallableMethod((JetNamedFunction) declaration);
}
else {
throw new UnsupportedOperationException("unknown accessor type");
throw new UnsupportedOperationException("unknown accessor type: " + declaration);
}
boolean isGetter = accessor.getSignature().getName().equals("get");
return StackValue.collectionElement(JetTypeMapper.TYPE_OBJECT, isGetter ? accessor : null,
@@ -0,0 +1,54 @@
package org.jetbrains.jet.lang;
import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetReferenceExpression;
import org.jetbrains.jet.lang.types.JetType;
/**
* @author abreslav
*/
public class CompositeErrorHandler extends ErrorHandler {
private final ErrorHandler[] handlers;
public CompositeErrorHandler(ErrorHandler... handlers) {
this.handlers = handlers;
}
@Override
public void unresolvedReference(@NotNull JetReferenceExpression referenceExpression) {
for (ErrorHandler handler : handlers) {
handler.unresolvedReference(referenceExpression);
}
}
@Override
public void typeMismatch(@NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull JetType actualType) {
for (ErrorHandler handler : handlers) {
handler.typeMismatch(expression, expectedType, actualType);
}
}
@Override
public void redeclaration(@NotNull DeclarationDescriptor existingDescriptor, @NotNull DeclarationDescriptor redeclaredDescriptor) {
for (ErrorHandler handler : handlers) {
handler.redeclaration(existingDescriptor, redeclaredDescriptor);
}
}
@Override
public void genericError(@NotNull ASTNode node, @NotNull String errorMessage) {
for (ErrorHandler handler : handlers) {
handler.genericError(node, errorMessage);
}
}
@Override
public void genericWarning(@NotNull ASTNode node, @NotNull String message) {
for (ErrorHandler handler : handlers) {
handler.genericWarning(node, message);
}
}
}
@@ -10,6 +10,8 @@ import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeTypes;
import org.jetbrains.jet.lang.CompositeErrorHandler;
import org.jetbrains.jet.lang.ErrorHandler;
import org.jetbrains.jet.lang.JetSemanticServices;
import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider;
import org.jetbrains.jet.lang.descriptors.*;
@@ -228,7 +230,7 @@ public class JetTypeInferrer {
expectedReturnType = NO_EXPECTED_TYPE;
}
JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, trace);
checkFunctionReturnType(functionInnerScope, function, expectedReturnType, dataFlowInfo);
checkFunctionReturnType(functionInnerScope, function, expectedReturnType, dataFlowInfo, CoercionStrategy.NO_COERCION);
// Map<JetElement, JetType> typeMap = collectReturnedExpressionsWithTypes(outerScope, function, functionDescriptor, expectedReturnType);
// if (typeMap.isEmpty()) {
// return; // The function returns Nothing
@@ -263,10 +265,10 @@ public class JetTypeInferrer {
}
public void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, @NotNull final JetType expectedReturnType) {
checkFunctionReturnType(functionInnerScope, function, expectedReturnType, DataFlowInfo.getEmpty());
checkFunctionReturnType(functionInnerScope, function, expectedReturnType, DataFlowInfo.getEmpty(), CoercionStrategy.NO_COERCION);
}
private void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, @NotNull final JetType expectedReturnType, @NotNull DataFlowInfo dataFlowInfo) {
private void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, @NotNull final JetType expectedReturnType, @NotNull DataFlowInfo dataFlowInfo, CoercionStrategy coercionForLastExpression) {
JetExpression bodyExpression = function.getBodyExpression();
assert bodyExpression != null;
@@ -276,7 +278,13 @@ public class JetTypeInferrer {
? new TypeInferenceContext(trace, functionInnerScope, function.hasBlockBody(), dataFlowInfo, NO_EXPECTED_TYPE, expectedReturnType)
: new TypeInferenceContext(trace, functionInnerScope, function.hasBlockBody(), dataFlowInfo, expectedReturnType, FORBIDDEN);
typeInferrerVisitor.getType(bodyExpression, context);
if (function instanceof JetFunctionLiteralExpression) {
JetFunctionLiteralExpression functionLiteralExpression = (JetFunctionLiteralExpression) function;
getBlockReturnedType(functionInnerScope, functionLiteralExpression.getBodyExpression(), CoercionStrategy.COERCION_TO_UNIT, context);
}
else {
typeInferrerVisitor.getType(bodyExpression, context);
}
List<JetElement> unreachableElements = Lists.newArrayList();
flowInformationProvider.collectUnreachableExpressions(function.asElement(), unreachableElements);
@@ -326,6 +334,18 @@ public class JetTypeInferrer {
}
}
@Nullable
private JetType getBlockReturnedType(@NotNull JetScope outerScope, @NotNull JetBlockExpression expression, @NotNull CoercionStrategy coercionStrategyForLastExpression, TypeInferenceContext context) {
List<JetElement> block = expression.getStatements();
if (block.isEmpty()) {
return checkType(JetStandardClasses.getUnitType(), expression, context);
}
DeclarationDescriptor containingDescriptor = outerScope.getContainingDeclaration();
WritableScope scope = new WritableScopeImpl(outerScope, containingDescriptor, context.trace.getErrorHandler()).setDebugName("getBlockReturnedType");
return getBlockReturnedTypeWithWritableScope(scope, block, coercionStrategyForLastExpression, context);
}
@NotNull
public JetType inferFunctionReturnType(@NotNull JetScope outerScope, JetDeclarationWithBody function, FunctionDescriptor functionDescriptor) {
Map<JetElement, JetType> typeMap = collectReturnedExpressionsWithTypes(trace, outerScope, function, functionDescriptor);
@@ -361,7 +381,7 @@ public class JetTypeInferrer {
return typeMap;
}
private JetType getBlockReturnedTypeWithWritableScope(@NotNull WritableScope scope, @NotNull List<? extends JetElement> block, TypeInferenceContext context) {
private JetType getBlockReturnedTypeWithWritableScope(@NotNull WritableScope scope, @NotNull List<? extends JetElement> block, @NotNull CoercionStrategy coercionStrategyForLastExpression, TypeInferenceContext context) {
if (block.isEmpty()) {
return JetStandardClasses.getUnitType();
}
@@ -371,15 +391,42 @@ public class JetTypeInferrer {
JetType result = null;
for (Iterator<? extends JetElement> iterator = block.iterator(); iterator.hasNext(); ) {
JetElement statement = iterator.next();
final JetElement statement = iterator.next();
trace.record(STATEMENT, statement);
JetExpression statementExpression = (JetExpression) statement;
final JetExpression statementExpression = (JetExpression) statement;
//TODO constructor assert context.expectedType != FORBIDDEN : ""
if (!iterator.hasNext() && context.expectedType != NO_EXPECTED_TYPE) {
newContext = new TypeInferenceContext(trace, scope, true, newContext.dataFlowInfo, context.expectedType, context.expectedReturnType);
if (coercionStrategyForLastExpression == CoercionStrategy.COERCION_TO_UNIT && JetStandardClasses.isUnit(context.expectedType)) {
// This implements coercion to Unit
TemporaryBindingTrace temporaryTraceExpectingUnit = TemporaryBindingTrace.create(trace);
final boolean[] mismatch = new boolean[1];
BindingTraceAdapter errorInterceptingTrace = makeTraceInterceptingTypeMismatch(temporaryTraceExpectingUnit, statementExpression, mismatch);
newContext = new TypeInferenceContext(errorInterceptingTrace, scope, true, newContext.dataFlowInfo, context.expectedType, context.expectedReturnType);
result = blockLevelVisitor.getType(statementExpression, newContext);
if (mismatch[0]) {
TemporaryBindingTrace temporaryTraceNoExpectedType = TemporaryBindingTrace.create(trace);
mismatch[0] = false;
BindingTraceAdapter interceptingTrace = makeTraceInterceptingTypeMismatch(temporaryTraceNoExpectedType, statementExpression, mismatch);
newContext = new TypeInferenceContext(interceptingTrace, scope, true, newContext.dataFlowInfo, NO_EXPECTED_TYPE, context.expectedReturnType);
result = blockLevelVisitor.getType(statementExpression, newContext);
if (mismatch[0]) {
temporaryTraceExpectingUnit.commit();
}
else {
temporaryTraceNoExpectedType.commit();
}
}
}
else {
newContext = new TypeInferenceContext(trace, scope, true, newContext.dataFlowInfo, context.expectedType, context.expectedReturnType);
result = blockLevelVisitor.getType(statementExpression, newContext);
}
}
else {
result = blockLevelVisitor.getType(statementExpression, newContext);
}
result = blockLevelVisitor.getType(statementExpression, newContext);
DataFlowInfo newDataFlowInfo = blockLevelVisitor.getResultingDataFlowInfo();
if (newDataFlowInfo == null) {
newDataFlowInfo = context.dataFlowInfo;
@@ -392,6 +439,23 @@ public class JetTypeInferrer {
return result;
}
private BindingTraceAdapter makeTraceInterceptingTypeMismatch(final BindingTrace trace, JetExpression expression, final boolean[] mismatchFound) {
return new BindingTraceAdapter(trace) {
@NotNull
@Override
public ErrorHandler getErrorHandler() {
return new CompositeErrorHandler(super.getErrorHandler(), new ErrorHandler() {
@Override
public void typeMismatch(@NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull JetType actualType) {
if (expression == expression) {
mismatchFound[0] = true;
}
}
});
}
};
}
//TODO
private JetType enrichOutType(JetExpression expression, JetType initialType, @NotNull TypeInferenceContext context) {
if (expression == null) return initialType;
@@ -482,17 +546,22 @@ public class JetTypeInferrer {
}
return variableDescriptor;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private enum CoercionStrategy {
NO_COERCION,
COERCION_TO_UNIT
}
private class TypeInferenceContext {
public final BindingTrace trace;
public final TypeResolver typeResolver;
public final ClassDescriptorResolver classDescriptorResolver;
public final JetScope scope;
public final Services services;
public final boolean preferBlock;
public final DataFlowInfo dataFlowInfo;
@@ -516,7 +585,6 @@ public class JetTypeInferrer {
this.dataFlowInfo = dataFlowInfo;
this.expectedType = expectedType;
this.expectedReturnType = expectedReturnType;
}
public TypeInferenceContext replaceDataFlowInfo(DataFlowInfo newDataFlowInfo) {
@@ -612,18 +680,6 @@ public class JetTypeInferrer {
}
}
@Nullable
private JetType getBlockReturnedType(@NotNull JetScope outerScope, @NotNull JetBlockExpression expression, TypeInferenceContext context) {
List<JetElement> block = expression.getStatements();
if (block.isEmpty()) {
return context.services.checkType(JetStandardClasses.getUnitType(), expression, context);
}
DeclarationDescriptor containingDescriptor = outerScope.getContainingDeclaration();
WritableScope scope = new WritableScopeImpl(outerScope, containingDescriptor, context.trace.getErrorHandler()).setDebugName("getBlockReturnedType");
return context.services.getBlockReturnedTypeWithWritableScope(scope, block, context);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
@@ -742,7 +798,7 @@ public class JetTypeInferrer {
JetFunctionLiteral functionLiteral = expression.getFunctionLiteral();
if (context.preferBlock && !functionLiteral.hasParameterSpecification()) {
context.trace.record(BindingContext.BLOCK, expression);
return context.services.checkType(getBlockReturnedType(context.scope, functionLiteral.getBodyExpression(), context), expression, context);
return context.services.checkType(context.services.getBlockReturnedType(context.scope, functionLiteral.getBodyExpression(), CoercionStrategy.NO_COERCION, context), expression, context);
}
JetTypeReference receiverTypeRef = functionLiteral.getReceiverTypeRef();
@@ -809,18 +865,22 @@ public class JetTypeInferrer {
JetTypeReference returnTypeRef = functionLiteral.getReturnTypeRef();
if (returnTypeRef != null) {
returnType = context.typeResolver.resolveType(context.scope, returnTypeRef);
context.services.checkFunctionReturnType(functionInnerScope, expression, returnType, context.dataFlowInfo);
context.services.checkFunctionReturnType(functionInnerScope, expression, returnType, context.dataFlowInfo, CoercionStrategy.COERCION_TO_UNIT);
}
else {
if (functionTypeExpected) {
returnType = JetStandardClasses.getReturnType(expectedType);
}
returnType = getBlockReturnedType(functionInnerScope, functionLiteral.getBodyExpression(), context.replaceExpectedType(returnType));
returnType = context.services.getBlockReturnedType(functionInnerScope, functionLiteral.getBodyExpression(), CoercionStrategy.COERCION_TO_UNIT, context.replaceExpectedType(returnType));
}
JetType safeReturnType = returnType == null ? ErrorUtils.createErrorType("<return type>") : returnType;
functionDescriptor.setReturnType(safeReturnType);
if (functionTypeExpected) {
JetType expectedReturnType = JetStandardClasses.getReturnType(expectedType);
if (JetStandardClasses.isUnit(expectedReturnType)) {
return context.services.checkType(JetStandardClasses.getFunctionType(Collections.<AnnotationDescriptor>emptyList(), effectiveReceiverType, parameterTypes, expectedReturnType), expression, context);
}
}
return context.services.checkType(JetStandardClasses.getFunctionType(Collections.<AnnotationDescriptor>emptyList(), effectiveReceiverType, parameterTypes, safeReturnType), expression, context);
@@ -1082,7 +1142,7 @@ public class JetTypeInferrer {
@Override
public JetType visitBlockExpression(JetBlockExpression expression, TypeInferenceContext context) {
return context.services.checkType(getBlockReturnedType(context.scope, expression, context), expression, context);
return context.services.checkType(context.services.getBlockReturnedType(context.scope, expression, CoercionStrategy.NO_COERCION, context), expression, context);
}
@Override
@@ -1561,7 +1621,7 @@ public class JetTypeInferrer {
if (!function.getFunctionLiteral().hasParameterSpecification()) {
WritableScope writableScope = newWritableScopeImpl(context.scope, context.trace).setDebugName("do..while body scope");
conditionScope = writableScope;
context.services.getBlockReturnedTypeWithWritableScope(writableScope, function.getFunctionLiteral().getBodyExpression().getStatements(), context);
context.services.getBlockReturnedTypeWithWritableScope(writableScope, function.getFunctionLiteral().getBodyExpression().getStatements(), CoercionStrategy.NO_COERCION, context);
context.trace.record(BindingContext.BLOCK, function);
} else {
getType(context.scope, body, true, context);
@@ -1570,7 +1630,7 @@ public class JetTypeInferrer {
else if (body != null) {
WritableScope writableScope = newWritableScopeImpl(context.scope, context.trace).setDebugName("do..while body scope");
conditionScope = writableScope;
context.services.getBlockReturnedTypeWithWritableScope(writableScope, Collections.singletonList(body), context);
context.services.getBlockReturnedTypeWithWritableScope(writableScope, Collections.singletonList(body), CoercionStrategy.NO_COERCION, context);
}
JetExpression condition = expression.getCondition();
checkCondition(conditionScope, condition, context);
@@ -2368,14 +2428,15 @@ public class JetTypeInferrer {
// context.services.callResolver.resolveCallWithGivenName(
// scope,
// call,
// operationSign,
// arrayAccessExpression,
// "set", arrayAccessExpression.getArrayExpression(), NO_EXPECTED_TYPE);
FunctionDescriptor functionDescriptor = context.services.callResolver.resolveCallWithGivenName(
scope,
call,
operationSign,
arrayAccessExpression,
"set", receiverType, NO_EXPECTED_TYPE);
if (functionDescriptor == null) return null;
context.trace.record(REFERENCE_TARGET, operationSign, functionDescriptor);
return context.services.checkType(functionDescriptor.getReturnType(), arrayAccessExpression, context);
}
@@ -42,4 +42,9 @@ public final class ManyMapKey<K, V> {
result = 31 * result + (key != null ? key.hashCode() : 0);
return result;
}
@Override
public String toString() {
return slice + " -> " + key;
}
}
+121
View File
@@ -0,0 +1,121 @@
import java.util.*
namespace html {
abstract class Factory<T> {
fun create() : T
}
abstract class Element
class TextElement(val text : String) : Element
abstract class Tag(val name : String) : Element {
val children = ArrayList<Element>()
val attributes = HashMap<String, String>()
protected fun initTag<T : Element>(init : fun T.() : Unit) : T
where class object T : Factory<T>{
val tag = T.create()
tag.init()
children.add(tag)
return tag
}
}
abstract class TagWithText(name : String) : Tag(name) {
fun String.plus() {
children.add(TextElement(this))
}
}
class HTML() : TagWithText("html") {
class object : Factory<HTML> {
override fun create() = HTML()
}
fun head(init : fun Head.() : Unit) = initTag<Head>(init)
fun body(init : fun Body.() : Unit) = initTag<Body>(init)
}
class Head() : TagWithText("head") {
class object : Factory<Head> {
override fun create() = Head()
}
fun title(init : fun Title.() : Unit) = initTag<Title>(init)
}
class Title() : TagWithText("title")
class BodyTag(name : String) : TagWithText(name) {
}
class Body() : BodyTag("body") {
class object : Factory<Body> {
override fun create() = Body()
}
fun b(init : fun B.() : Unit) = initTag<B>(init)
fun p(init : fun P.() : Unit) = initTag<P>(init)
fun h1(init : fun H1.() : Unit) = initTag<H1>(init)
fun a(href : String, init : fun A.() : Unit) {
val a = initTag<A>(init)
a.href = href
}
}
class B() : BodyTag("b")
class P() : BodyTag("p")
class H1() : BodyTag("h1")
class A() : BodyTag("a") {
var href : String
get() = attributes["href"]
set(value) { attributes["href"] = value }
}
fun Map<String, String>.set(key : String, value : String) = this.put(key, value)
fun html(init : fun HTML.() : Unit) : HTML {
val html = HTML()
html.init()
return html
}
}
namespace foo {
import html.*
fun result(args : Array<String>) =
html {
head {
title {+"XML encoding with Groovy"}
}
body {
h1 {+"XML encoding with Groovy"}
p {+"this format can be used as an alternative markup to XML"}
// an element with attributes and text content
a(href = "http://groovy.codehaus.org") {+"Groovy"}
// mixed content
p {
+"This is some"
b {+"mixed"}
+"text. For more see the"
a(href = "http://groovy.codehaus.org") {+"Groovy"}
+"project"
}
p {+"some text"}
// content generated by
p {
for (arg in args)
+arg
}
}
}
}