From d38fbbe2c68157206dafc3864225c80dd3f7e514 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Fri, 26 Aug 2011 21:40:37 +0400 Subject: [PATCH] Array access as LValue resolution fixed --- .../jet/codegen/ExpressionCodegen.java | 3 +- .../jet/lang/CompositeErrorHandler.java | 54 ++++++++ .../jet/lang/types/JetTypeInferrer.java | 125 +++++++++++++----- .../org/jetbrains/jet/util/ManyMapKey.java | 5 + idea/testData/checker/Builders.jet | 121 +++++++++++++++++ 5 files changed, 275 insertions(+), 33 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/lang/CompositeErrorHandler.java create mode 100644 idea/testData/checker/Builders.jet diff --git a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 3d6c4d23c4c..9b288b7e33d 100644 --- a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -1478,6 +1478,7 @@ public class ExpressionCodegen extends JetVisitor { } 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 { 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, diff --git a/idea/src/org/jetbrains/jet/lang/CompositeErrorHandler.java b/idea/src/org/jetbrains/jet/lang/CompositeErrorHandler.java new file mode 100644 index 00000000000..b1e05adbe23 --- /dev/null +++ b/idea/src/org/jetbrains/jet/lang/CompositeErrorHandler.java @@ -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); + } + } +} diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index ab69e006e06..6238e576780 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -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 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 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 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 typeMap = collectReturnedExpressionsWithTypes(trace, outerScope, function, functionDescriptor); @@ -361,7 +381,7 @@ public class JetTypeInferrer { return typeMap; } - private JetType getBlockReturnedTypeWithWritableScope(@NotNull WritableScope scope, @NotNull List block, TypeInferenceContext context) { + private JetType getBlockReturnedTypeWithWritableScope(@NotNull WritableScope scope, @NotNull List block, @NotNull CoercionStrategy coercionStrategyForLastExpression, TypeInferenceContext context) { if (block.isEmpty()) { return JetStandardClasses.getUnitType(); } @@ -371,15 +391,42 @@ public class JetTypeInferrer { JetType result = null; for (Iterator 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 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("") : returnType; functionDescriptor.setReturnType(safeReturnType); if (functionTypeExpected) { + JetType expectedReturnType = JetStandardClasses.getReturnType(expectedType); + if (JetStandardClasses.isUnit(expectedReturnType)) { + return context.services.checkType(JetStandardClasses.getFunctionType(Collections.emptyList(), effectiveReceiverType, parameterTypes, expectedReturnType), expression, context); + } } return context.services.checkType(JetStandardClasses.getFunctionType(Collections.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); } diff --git a/idea/src/org/jetbrains/jet/util/ManyMapKey.java b/idea/src/org/jetbrains/jet/util/ManyMapKey.java index 7f93575413a..48720061b5a 100644 --- a/idea/src/org/jetbrains/jet/util/ManyMapKey.java +++ b/idea/src/org/jetbrains/jet/util/ManyMapKey.java @@ -42,4 +42,9 @@ public final class ManyMapKey { result = 31 * result + (key != null ? key.hashCode() : 0); return result; } + + @Override + public String toString() { + return slice + " -> " + key; + } } diff --git a/idea/testData/checker/Builders.jet b/idea/testData/checker/Builders.jet new file mode 100644 index 00000000000..9f99f6e20f2 --- /dev/null +++ b/idea/testData/checker/Builders.jet @@ -0,0 +1,121 @@ +import java.util.* + +namespace html { + + abstract class Factory { + fun create() : T + } + + abstract class Element + + class TextElement(val text : String) : Element + + abstract class Tag(val name : String) : Element { + val children = ArrayList() + val attributes = HashMap() + + protected fun initTag(init : fun T.() : Unit) : T + where class object T : Factory{ + 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 { + override fun create() = HTML() + } + + fun head(init : fun Head.() : Unit) = initTag(init) + + fun body(init : fun Body.() : Unit) = initTag(init) + } + + class Head() : TagWithText("head") { + class object : Factory { + override fun create() = Head() + } + + fun title(init : fun Title.() : Unit) = initTag(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 + } + } + } +}