提交 7ec6adec 编写于 作者: A Andrey Breslav

Basic support for blocks and local variables

上级 3bda886b
......@@ -19,7 +19,8 @@ public class JetBlockExpression extends JetExpression {
visitor.visitBlockExpression(this);
}
public List<JetExpression> getStatements() {
return Arrays.asList(findChildrenByClass(JetExpression.class));
@NotNull
public List<JetElement> getStatements() {
return Arrays.asList(findChildrenByClass(JetElement.class));
}
}
......@@ -46,8 +46,9 @@ public class JetChangeUtil {
JetNamespace rootNamespace = file.getRootNamespace();
List<JetDeclaration> dcls = rootNamespace.getDeclarations();
assert dcls.size() == 1;
//noinspection unchecked
return (T) dcls.get(0);
@SuppressWarnings("unchecked")
T result = (T) dcls.get(0);
return result;
}
public static PsiElement createNameIdentifier(Project project, String name) {
......
......@@ -45,8 +45,8 @@ public class JetFunctionLiteralExpression extends JetExpression {
}
@NotNull
public List<JetExpression> getBody() {
return PsiTreeUtil.getChildrenOfTypeAsList(getBodyNode().getPsi(), JetExpression.class);
public List<JetElement> getBody() {
return PsiTreeUtil.getChildrenOfTypeAsList(getBodyNode().getPsi(), JetElement.class);
}
@Nullable
......@@ -81,4 +81,8 @@ public class JetFunctionLiteralExpression extends JetExpression {
return null;
}
public boolean hasParameterSpecification() {
return findChildByType(JetTokens.DOUBLE_ARROW) != null;
}
}
package org.jetbrains.jet.lang.resolve;
import org.jetbrains.jet.lang.types.PropertyDescriptor;
import java.util.HashMap;
import java.util.Map;
/**
* @author abreslav
*/
public class AccumulatingScope extends JetScopeAdapter {
private final Map<String, PropertyDescriptor> properties = new HashMap<String, PropertyDescriptor>();
public AccumulatingScope(JetScope outerScope) {
super(outerScope);
}
public void addPropertyDescriptor(PropertyDescriptor propertyDescriptor) {
if (properties.put(propertyDescriptor.getName(), propertyDescriptor) != null) {
throw new IllegalArgumentException("Duplicate property: " + propertyDescriptor.getName());
}
}
@Override
public PropertyDescriptor getProperty(String name) {
PropertyDescriptor propertyDescriptor = properties.get(name);
if (propertyDescriptor == null) {
return super.getProperty(name);
}
return propertyDescriptor;
}
}
......@@ -77,8 +77,8 @@ public class ClassDescriptorResolver {
}
private static final class TypeParameterExtensibleScope extends JetScopeAdapter {
private final Map<String, TypeParameterDescriptor> typeParameterDescriptors = new HashMap<String, TypeParameterDescriptor>();
private final Map<String, TypeParameterDescriptor> typeParameterDescriptors = new HashMap<String, TypeParameterDescriptor>();
private TypeParameterExtensibleScope(JetScope scope) {
super(scope);
}
......@@ -99,8 +99,8 @@ public class ClassDescriptorResolver {
}
return super.getTypeParameterDescriptor(name);
}
}
}
@NotNull
public PropertyDescriptor resolvePropertyDescriptor(@NotNull JetScope scope, @NotNull JetParameter parameter) {
return new PropertyDescriptor(
......@@ -108,4 +108,24 @@ public class ClassDescriptorResolver {
parameter.getName(),
TypeResolver.INSTANCE.resolveType(scope, parameter.getTypeReference()));
}
public static PropertyDescriptor resolvePropertyDescriptor(@NotNull JetScope scope, JetProperty property) {
// TODO : receiver?
JetTypeReference propertyTypeRef = property.getPropertyTypeRef();
Type type;
if (propertyTypeRef == null) {
JetExpression initializer = property.getInitializer();
assert initializer != null;
// TODO : ??? Fix-point here: what if we have something like "val a = foo {a.bar()}"
type = JetTypeChecker.INSTANCE.getType(scope, initializer, false);
} else {
type = TypeResolver.INSTANCE.resolveType(scope, propertyTypeRef);
}
return new PropertyDescriptor(
AttributeResolver.INSTANCE.resolveAttributes(property.getModifierList()),
property.getName(),
type);
}
}
package org.jetbrains.jet.lang.resolve;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.types.*;
/**
......@@ -12,6 +13,7 @@ public class JetScopeAdapter implements JetScope {
this.scope = scope;
}
@NotNull
@Override
public Type getThisType() {
return scope.getThisType();
......
......@@ -2,7 +2,11 @@ package org.jetbrains.jet.lang.types;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.resolve.JetScope;
import org.jetbrains.jet.lang.resolve.JetScopeImpl;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
/**
......@@ -135,6 +139,43 @@ public class JetStandardClasses {
Collections.<TypeProjection>emptyList(),
TypeMemberDomain.EMPTY);
private static final Map<String, ClassDescriptor> CLASS_MAP = new HashMap<String, ClassDescriptor>();
static {
Field[] declaredFields = JetStandardClasses.class.getDeclaredFields();
for (Field field : declaredFields) {
if ((field.getModifiers() & Modifier.STATIC) == 0) {
continue;
}
Class<?> type = field.getType();
if (type == ClassDescriptor.class) {
try {
ClassDescriptor descriptor = (ClassDescriptor) field.get(null);
CLASS_MAP.put(descriptor.getName(), descriptor);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
} else if (type.isArray() && type.getComponentType() == ClassDescriptor.class) {
try {
ClassDescriptor[] array = (ClassDescriptor[]) field.get(null);
for (ClassDescriptor descriptor : array) {
CLASS_MAP.put(descriptor.getName(), descriptor);
}
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
CLASS_MAP.put("Unit", getTuple(0));
}
@NotNull
public static final JetScope STANDARD_CLASSES = new JetScopeImpl() {
@Override
public ClassDescriptor getClass(String name) {
return CLASS_MAP.get(name);
}
};
@NotNull
public static ClassDescriptor getAny() {
return ANY;
......@@ -204,6 +245,16 @@ public class JetStandardClasses {
return TUPLE[size];
}
@NotNull
public static ClassDescriptor getFunction(int parameterCount) {
return FUNCTION[parameterCount];
}
@NotNull
public static ClassDescriptor getReceiverFunction(int parameterCount) {
return RECEIVER_FUNCTION[parameterCount];
}
public static Type getIntType() {
return INT_TYPE;
}
......
......@@ -5,10 +5,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeTypes;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.ClassDescriptorResolver;
import org.jetbrains.jet.lang.resolve.JetScope;
import org.jetbrains.jet.lang.resolve.JetScopeAdapter;
import org.jetbrains.jet.lang.resolve.TypeResolver;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.*;
......@@ -33,7 +30,7 @@ public class JetTypeChecker {
: declaration
: "namespace" // for the root namespace
*/
public Type getType(@NotNull final JetScope scope, @NotNull JetExpression expression) {
public Type getType(@NotNull final JetScope scope, @NotNull JetExpression expression, final boolean preferBlock) {
final Type[] result = new Type[1];
expression.accept(new JetVisitor() {
@Override
......@@ -48,6 +45,11 @@ public class JetTypeChecker {
@Override
public void visitFunctionLiteralExpression(JetFunctionLiteralExpression expression) {
if (preferBlock && !expression.hasParameterSpecification()) {
result[0] = getBlockReturnedType(scope, expression.getBody());
return;
}
JetTypeReference returnTypeRef = expression.getReturnTypeRef();
JetTypeReference receiverTypeRef = expression.getReceiverTypeRef();
......@@ -58,7 +60,7 @@ public class JetTypeChecker {
thisType = scope.getThisType();
}
List<JetExpression> body = expression.getBody();
List<JetElement> body = expression.getBody();
final Map<String, PropertyDescriptor> parameterDescriptors = new HashMap<String, PropertyDescriptor>();
List<Type> parameterTypes = new ArrayList<Type>();
for (JetParameter parameter : expression.getParameters()) {
......@@ -73,10 +75,8 @@ public class JetTypeChecker {
Type returnType;
if (returnTypeRef != null) {
returnType = TypeResolver.INSTANCE.resolveType(scope, returnTypeRef);
} else if (body.isEmpty()) {
returnType = JetStandardClasses.getUnitType();
} else {
returnType = getType(new JetScopeAdapter(scope) {
returnType = getBlockReturnedType(new JetScopeAdapter(scope) {
@Override
public Type getThisType() {
return thisType;
......@@ -90,14 +90,14 @@ public class JetTypeChecker {
}
return propertyDescriptor;
}
}, body.get(body.size() - 1));
}, body);
}
result[0] = JetStandardClasses.getFunctionType(null, receiverTypeRef == null ? null : thisType, parameterTypes, returnType);
}
@Override
public void visitParenthesizedExpression(JetParenthesizedExpression expression) {
result[0] = getType(scope, expression.getExpression());
result[0] = getType(scope, expression.getExpression(), false);
}
@Override
......@@ -157,7 +157,7 @@ public class JetTypeChecker {
@Override
public void visitBinaryWithTypeRHSExpression(JetBinaryExpressionWithTypeRHS expression) {
if (expression.getOperationSign() == JetTokens.COLON) {
Type actualType = getType(scope, expression.getLeft());
Type actualType = getType(scope, expression.getLeft(), false);
Type expectedType = TypeResolver.INSTANCE.resolveType(scope, expression.getRight());
if (isSubtypeOf(actualType, expectedType)) {
result[0] = expectedType;
......@@ -179,8 +179,8 @@ public class JetTypeChecker {
// TODO : type-check the branch
result[0] = JetStandardClasses.getUnitType();
} else {
Type thenType = getType(scope, expression.getThen());
Type elseType = getType(scope, elseBranch);
Type thenType = getType(scope, expression.getThen(), true);
Type elseType = getType(scope, elseBranch, true);
result[0] = commonSupertype(Arrays.asList(thenType, elseType));
}
}
......@@ -202,12 +202,12 @@ public class JetTypeChecker {
if (finallyBlock == null) {
for (JetCatchClause catchClause : catchClauses) {
// TODO: change scope here
types.add(getType(scope, catchClause.getCatchBody()));
types.add(getType(scope, catchClause.getCatchBody(), true));
}
} else {
types.add(getType(scope, finallyBlock.getFinalExpression()));
types.add(getType(scope, finallyBlock.getFinalExpression(), true));
}
types.add(getType(scope, tryBlock));
types.add(getType(scope, tryBlock, true));
result[0] = commonSupertype(types);
}
......@@ -216,7 +216,7 @@ public class JetTypeChecker {
List<JetExpression> entries = expression.getEntries();
List<Type> types = new ArrayList<Type>();
for (JetExpression entry : entries) {
types.add(getType(scope, entry));
types.add(getType(scope, entry, false));
}
// TODO : labels
result[0] = JetStandardClasses.getTupleType(types);
......@@ -247,12 +247,7 @@ public class JetTypeChecker {
@Override
public void visitBlockExpression(JetBlockExpression expression) {
// TODO : this is a stub, consider function literals
List<JetExpression> statements = expression.getStatements();
if (statements.isEmpty()) {
result[0] = JetStandardClasses.getUnitType();
} else {
result[0] = getType(scope, statements.get(statements.size() - 1));
}
result[0] = getBlockReturnedType(scope, expression.getStatements());
}
@Override
......@@ -268,6 +263,29 @@ public class JetTypeChecker {
return result[0];
}
private Type getBlockReturnedType(@NotNull JetScope outerScope, List<JetElement> block) {
// TODO : this is a stub, consider function literals
if (block.isEmpty()) {
return JetStandardClasses.getUnitType();
} else {
AccumulatingScope scope = new AccumulatingScope(outerScope);
for (JetElement statement : block) {
// TODO: consider other declarations
if (statement instanceof JetProperty) {
JetProperty property = (JetProperty) statement;
scope.addPropertyDescriptor(ClassDescriptorResolver.resolvePropertyDescriptor(scope, property));
}
}
JetElement lastElement = block.get(block.size() - 1);
if (lastElement instanceof JetExpression) {
JetExpression expression = (JetExpression) lastElement;
return getType(scope, expression, true);
}
// TODO: functions, classes, etc.
throw new IllegalArgumentException("Last item in the block must be an expression");
}
}
private void collectAllReturnTypes(JetWhenExpression whenExpression, JetScope scope, List<Type> result) {
for (JetWhenEntry entry : whenExpression.getEntries()) {
JetWhenExpression subWhen = entry.getSubWhen();
......@@ -276,7 +294,7 @@ public class JetTypeChecker {
} else {
JetExpression resultExpression = entry.getExpression();
if (resultExpression != null) {
result.add(getType(scope, resultExpression));
result.add(getType(scope, resultExpression, true));
}
}
}
......
......@@ -3,6 +3,7 @@ package org.jetbrains.jet.types;
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.types.*;
......@@ -296,6 +297,13 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
assertType("{Any.(a : Int, b : String) => b}", "{Any.(Int, String) : String}");
}
public void testBlocks() throws Exception {
assertType("if (1) {val a = 1; a} else {null}", "Int?");
assertType("if (1) {() => val a = 1; a} else {() => null}", "Function0<Int?>");
assertType("if (1) {() => val a = 1; a; var b : Boolean; b = true; b} else null", "Function0<Boolean>?");
assertType("if (1) {() => val a = 1; a; var b = a; b} else null", "Function0<Int>?");
}
public void testImplicitConversions() throws Exception {
assertConvertibleTo("1", JetStandardClasses.getByteType());
}
......@@ -344,13 +352,14 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
private static void assertType(String expression, Type expectedType) {
Project project = getProject();
JetExpression jetExpression = JetChangeUtil.createExpression(project, expression);
Type type = JetTypeChecker.INSTANCE.getType(ClassDefinitions.BASIC_SCOPE, jetExpression);
Type type = JetTypeChecker.INSTANCE.getType(ClassDefinitions.BASIC_SCOPE, jetExpression, false);
assertTrue(type + " != " + expectedType, JetTypeChecker.INSTANCE.equalTypes(type, expectedType));
}
private void assertType(String contextType, String expression, String expectedType) {
private static void assertType(String contextType, String expression, String expectedType) {
final Type thisType = makeType(contextType);
JetScope scope = new JetScopeAdapter(ClassDefinitions.BASIC_SCOPE) {
@NotNull
@Override
public Type getThisType() {
return thisType;
......@@ -366,7 +375,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
private static void assertType(JetScope scope, String expression, String expectedTypeStr) {
Project project = getProject();
JetExpression jetExpression = JetChangeUtil.createExpression(project, expression);
Type type = JetTypeChecker.INSTANCE.getType(scope, jetExpression);
Type type = JetTypeChecker.INSTANCE.getType(scope, jetExpression, false);
Type expectedType = makeType(expectedTypeStr);
assertTrue(type + " != " + expectedType, JetTypeChecker.INSTANCE.equalTypes(type, expectedType));
}
......@@ -392,32 +401,9 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
"open class Derived_outT<out T> : Base_outT<T>",
};
public static JetScope BASIC_SCOPE = new JetScopeImpl() {
public static JetScope BASIC_SCOPE = new JetScopeAdapter(JetStandardClasses.STANDARD_CLASSES) {
@Override
public ClassDescriptor getClass(String name) {
if ("Int".equals(name)) {
return JetStandardClasses.getInt();
} else if ("Boolean".equals(name)) {
return JetStandardClasses.getBoolean();
} else if ("Byte".equals(name)) {
return JetStandardClasses.getByte();
} else if ("Char".equals(name)) {
return JetStandardClasses.getChar();
} else if ("Short".equals(name)) {
return JetStandardClasses.getShort();
} else if ("Long".equals(name)) {
return JetStandardClasses.getLong();
} else if ("Float".equals(name)) {
return JetStandardClasses.getFloat();
} else if ("Double".equals(name)) {
return JetStandardClasses.getDouble();
} else if ("Unit".equals(name)) {
return JetStandardClasses.getTuple(0);
} else if ("Any".equals(name)) {
return JetStandardClasses.getAny();
} else if ("Nothing".equals(name)) {
return JetStandardClasses.getNothing();
}
if (CLASSES.isEmpty()) {
for (String classDeclaration : CLASS_DECLARATIONS) {
JetClass classElement = JetChangeUtil.createClass(getProject(), classDeclaration);
......@@ -429,7 +415,7 @@ public class JetTypeCheckerTest extends LightDaemonAnalyzerTestCase {
if (classDescriptor != null) {
return classDescriptor;
}
return null;
return super.getClass(name);
}
};
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册