diff --git a/idea/src/jet/Library.jet b/idea/src/jet/Library.jet index 211e49cf2db..0d535035baa 100644 --- a/idea/src/jet/Library.jet +++ b/idea/src/jet/Library.jet @@ -42,7 +42,7 @@ fun Any?.toString() : String// = this === other class Iterator { fun next() : T - abstract val hasNext : Boolean + abstract fun hasNext() : Boolean } class Iterable { diff --git a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 2c956b120c1..eb2bd96d784 100644 --- a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -11,11 +11,13 @@ import jet.Range; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethod; import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethods; +import org.jetbrains.jet.lang.ErrorHandler; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContextUtils; import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; +import org.jetbrains.jet.lang.resolve.java.JavaClassDescriptor; import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.TypeProjection; @@ -38,20 +40,13 @@ public class ExpressionCodegen extends JetVisitor { private static final String CLASS_STRING = "java/lang/String"; public static final String CLASS_STRING_BUILDER = "java/lang/StringBuilder"; private static final String CLASS_COMPARABLE = "java/lang/Comparable"; - private static final String CLASS_ITERABLE = "java/lang/Iterable"; - private static final String CLASS_ITERATOR = "java/util/Iterator"; private static final String CLASS_RANGE = "jet/Range"; private static final String CLASS_NO_PATTERN_MATCHED_EXCEPTION = "jet/NoPatternMatchedException"; private static final String CLASS_TYPE_CAST_EXCEPTION = "jet/TypeCastException"; - private static final String ITERABLE_ITERATOR_DESCRIPTOR = "()Ljava/util/Iterator;"; - private static final String ITERATOR_HASNEXT_DESCRIPTOR = "()Z"; - private static final String ITERATOR_NEXT_DESCRIPTOR = "()Ljava/lang/Object;"; - private static final Type OBJECT_TYPE = Type.getType(Object.class); private static final Type INTEGER_TYPE = Type.getType(Integer.class); - private static final Type ITERATOR_TYPE = Type.getType(Iterator.class); private static final Type THROWABLE_TYPE = Type.getType(Throwable.class); private static final Type STRING_TYPE = Type.getObjectType(CLASS_STRING); @@ -242,33 +237,43 @@ public class ExpressionCodegen extends JetVisitor { } else { final DeclarationDescriptor descriptor = expressionType.getConstructor().getDeclarationDescriptor(); - final PsiElement declaration = bindingContext.get(BindingContext.DESCRIPTOR_TO_DECLARATION, descriptor); - if (declaration instanceof PsiClass) { - final Project project = declaration.getProject(); - final PsiClass iterable = JavaPsiFacade.getInstance(project).findClass("java.lang.Iterable", ProjectScope.getAllScope(project)); - if (((PsiClass) declaration).isInheritor(iterable, true)) { - generateForInIterable(expression, loopRangeType); - return StackValue.none(); - } - } if (isClass(descriptor, "IntRange")) { // TODO IntRange subclasses new ForInRangeLoopGenerator(expression, loopRangeType).invoke(); return StackValue.none(); } - throw new UnsupportedOperationException("for/in loop currently only supported for arrays and Iterable instances"); + + generateForInIterable(expression, loopRangeType); + return StackValue.none(); } } private void generateForInIterable(JetForExpression expression, Type loopRangeType) { + final JetExpression loopRange = expression.getLoopRange(); + + FunctionDescriptor iteratorDescriptor = bindingContext.get(BindingContext.LOOP_RANGE_ITERATOR, loopRange); + FunctionDescriptor nextDescriptor = bindingContext.get(BindingContext.LOOP_RANGE_NEXT, loopRange); + DeclarationDescriptor hasNextDescriptor = bindingContext.get(BindingContext.LOOP_RANGE_HAS_NEXT, loopRange); + + if(iteratorDescriptor == null) + throw new IllegalStateException("No iterator() method " + ErrorHandler.atLocation(loopRange)); + if(nextDescriptor == null) + throw new IllegalStateException("No next() method " + ErrorHandler.atLocation(loopRange)); + if(hasNextDescriptor == null) + throw new IllegalStateException("No iterator() method " + ErrorHandler.atLocation(loopRange)); + final JetParameter loopParameter = expression.getLoopParameter(); final VariableDescriptor parameterDescriptor = bindingContext.get(BindingContext.VALUE_PARAMETER, loopParameter); + + JetType iteratorType = parameterDescriptor.getOutType(); + Type asmIterType = typeMapper.boxType(typeMapper.mapType(iteratorType)); + JetType paramType = parameterDescriptor.getOutType(); Type asmParamType = typeMapper.boxType(typeMapper.mapType(paramType)); int iteratorVar = myMap.enterTemp(); gen(expression.getLoopRange(), loopRangeType); - v.invokeinterface(CLASS_ITERABLE, "iterator", ITERABLE_ITERATOR_DESCRIPTOR); - v.store(iteratorVar, ITERATOR_TYPE); + invokeFunctionNoParams(iteratorDescriptor, asmIterType, v); + v.store(iteratorVar, asmIterType); Label begin = new Label(); Label end = new Label(); @@ -276,13 +281,21 @@ public class ExpressionCodegen extends JetVisitor { myBreakTargets.push(end); v.mark(begin); - v.load(iteratorVar, ITERATOR_TYPE); - v.invokeinterface(CLASS_ITERATOR, "hasNext", ITERATOR_HASNEXT_DESCRIPTOR); + v.load(iteratorVar, asmIterType); + FunctionDescriptor hND; + if(hasNextDescriptor instanceof FunctionDescriptor) { + hND = (FunctionDescriptor) hasNextDescriptor; + } + else { + hND = ((PropertyDescriptor) hasNextDescriptor).getGetter(); + } + invokeFunctionNoParams(hND, Type.BOOLEAN_TYPE, v); v.ifeq(end); myMap.enter(parameterDescriptor, asmParamType.getSize()); - v.load(iteratorVar, ITERATOR_TYPE); - v.invokeinterface(CLASS_ITERATOR, "next", ITERATOR_NEXT_DESCRIPTOR); + v.load(iteratorVar, asmIterType); + invokeFunctionNoParams(nextDescriptor, asmParamType, v); + // TODO checkcast should be generated via StackValue if (asmParamType.getSort() == Type.OBJECT && !"java.lang.Object".equals(asmParamType.getClassName())) { v.checkcast(asmParamType); @@ -770,11 +783,36 @@ public class ExpressionCodegen extends JetVisitor { return myMap.getIndex(descriptor); } + public void invokeFunctionNoParams(FunctionDescriptor functionDescriptor, Type type, InstructionAdapterEx v) { + DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration(); + boolean isStatic = containingDeclaration instanceof NamespaceDescriptorImpl; + functionDescriptor = functionDescriptor.getOriginal(); + String owner; + + boolean isInterface; + boolean isInsideClass = containingDeclaration == contextType(); + if (isInsideClass || isStatic) { + owner = typeMapper.getOwner(functionDescriptor, contextKind()); + isInterface = false; + } + else { + owner = typeMapper.getOwner(functionDescriptor, OwnerKind.INTERFACE); + if(containingDeclaration instanceof JavaClassDescriptor) { + PsiClass psiElement = (PsiClass) bindingContext.get(BindingContext.DESCRIPTOR_TO_DECLARATION, containingDeclaration); + isInterface = psiElement.isInterface(); + } + else + isInterface = !(containingDeclaration instanceof ClassDescriptor && ((ClassDescriptor) containingDeclaration).isObject()); + } + + v.visitMethodInsn(isStatic ? Opcodes.INVOKESTATIC : isInterface ? Opcodes.INVOKEINTERFACE : Opcodes.INVOKEVIRTUAL, owner, functionDescriptor.getName(), typeMapper.mapSignature(functionDescriptor.getName(),functionDescriptor).getDescriptor()); + StackValue.onStack(typeMapper.mapType(functionDescriptor.getReturnType())).coerce(type, v); + } + public StackValue intermediateValueForProperty(PropertyDescriptor propertyDescriptor, final boolean forceField, boolean forceInterface) { DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration(); boolean isStatic = containingDeclaration instanceof NamespaceDescriptorImpl; - while(propertyDescriptor != propertyDescriptor.getOriginal()) - propertyDescriptor = propertyDescriptor.getOriginal(); + propertyDescriptor = propertyDescriptor.getOriginal(); final JetType outType = propertyDescriptor.getOutType(); boolean isInsideClass = !forceInterface && containingDeclaration == contextType(); Method getter; @@ -1779,7 +1817,7 @@ public class ExpressionCodegen extends JetVisitor { v.anew(JetTypeMapper.TYPE_TYPEINFO); v.dup(); v.aconst(jvmType); - v.aconst(jetType.isNullable()); + v.iconst(jetType.isNullable()?1:0); List arguments = jetType.getArguments(); if (arguments.size() > 0) { v.iconst(arguments.size()); diff --git a/idea/src/org/jetbrains/jet/codegen/FunctionCodegen.java b/idea/src/org/jetbrains/jet/codegen/FunctionCodegen.java index 072be3d077a..51d53ad7b9c 100644 --- a/idea/src/org/jetbrains/jet/codegen/FunctionCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/FunctionCodegen.java @@ -133,7 +133,7 @@ public class FunctionCodegen { if(overriddenFunctions.size() > 0) { for (FunctionDescriptor overriddenFunction : overriddenFunctions) { // TODO should we check params here as well? - if(!JetTypeImpl.equalTypes(overriddenFunction.getReturnType(), functionDescriptor.getReturnType(), JetTypeImpl.EMPTY_AXIOMS)) { + if(!JetTypeImpl.equalTypeClasses(overriddenFunction.getReturnType(), functionDescriptor.getReturnType())) { generateBridgeMethod(jvmSignature, state.getTypeMapper().mapSignature(overriddenFunction.getName(), overriddenFunction)); } } diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/PropertyDescriptor.java b/idea/src/org/jetbrains/jet/lang/descriptors/PropertyDescriptor.java index 552f257ade8..de3545e086a 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/PropertyDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/PropertyDescriptor.java @@ -39,7 +39,7 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Member this.isVar = isVar; this.memberModifiers = memberModifiers; this.receiverType = receiverType; - this.original = original == null ? this : original; + this.original = original == null ? this : original.getOriginal(); } public PropertyDescriptor( diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeImpl.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeImpl.java index ac57307b37f..cbdc61d7e5f 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeImpl.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeImpl.java @@ -3,6 +3,7 @@ package org.jetbrains.jet.lang.types; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.annotations.AnnotatedImpl; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; @@ -114,6 +115,10 @@ public final class JetTypeImpl extends AnnotatedImpl implements JetType { } + public static boolean equalTypeClasses(@NotNull JetType type1, @NotNull JetType type2) { + return type1.getConstructor().getDeclarationDescriptor().getOriginal().equals(type2.getConstructor().getDeclarationDescriptor().getOriginal()); + } + public static boolean equalTypes(@NotNull JetType type1, @NotNull JetType type2, @NotNull BiMap equalityAxioms) { if (type1.isNullable() != type2.isNullable()) { return false; diff --git a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java index 7a0d440b7c8..f6cf4ec7815 100644 --- a/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java +++ b/idea/src/org/jetbrains/jet/lang/types/JetTypeInferrer.java @@ -2078,7 +2078,7 @@ public class JetTypeInferrer { boolean hasNextPropertySupported = hasNextProperty != null; if (hasNextFunctionSupported && hasNextPropertySupported && !ErrorUtils.isErrorType(iteratorType)) { // TODO : overload resolution rules impose priorities here??? - context.trace.getErrorHandler().genericError(reportErrorsOn, "An ambiguity between 'iterator().hasNext()' function and 'iterator().hasNext()' property"); + context.trace.getErrorHandler().genericError(reportErrorsOn, "An ambiguity between 'iterator().hasNext()' function and 'iterator().hasNext' property"); } else if (!hasNextFunctionSupported && !hasNextPropertySupported) { context.trace.getErrorHandler().genericError(reportErrorsOn, "Loop range must have an 'iterator().hasNext()' function or an 'iterator().hasNext' property"); diff --git a/idea/testData/codegen/controlStructures/forUserType.jet b/idea/testData/codegen/controlStructures/forUserType.jet new file mode 100644 index 00000000000..24425da2c4e --- /dev/null +++ b/idea/testData/codegen/controlStructures/forUserType.jet @@ -0,0 +1,45 @@ +fun box() : String { + val c5 = MyCollection3() + for (el in c5) {} + + val c1: java.lang.Iterable = MyCollection1() + for (el in c1) {} + + val c2 = MyCollection1() + for (el in c2) {} +/* + val c3: Iterable = MyCollection2() + for (el in c3) {} + + val c4 = MyCollection2() + for (el in c4) {} +*/ + return "OK" +} + +class MyCollection1(): java.lang.Iterable { + fun iterator(): java.util.Iterator = MyIterator() + + class MyIterator(): java.util.Iterator { + fun next() = 0 + fun hasNext() = false + } +} + +class MyCollection2(): Iterable { + fun iterator(): Iterator = MyIterator() + + class MyIterator(): Iterator { + fun next() = 0 + fun hasNext() = false + } +} + +class MyCollection3() { + fun iterator() = MyIterator() + + class MyIterator() { + fun next() = 0 + fun hasNext() = false + } +} diff --git a/idea/testData/codegen/regressions/kt259.jet b/idea/testData/codegen/regressions/kt259.jet index e4a1b9099d8..bf6ac4b0fe1 100644 --- a/idea/testData/codegen/regressions/kt259.jet +++ b/idea/testData/codegen/regressions/kt259.jet @@ -39,6 +39,11 @@ fun t3 () : Boolean { if(d.isT(10)) return false if(!d.isT(null)) return false + val f = B() + if(!f.isT("aaa")) return false + if(f.isT(10)) return false + if(!f.isT(null)) return false + val c = B() if(c.isT("aaa")) return false if(!c.isT(10)) return false diff --git a/idea/tests/org/jetbrains/jet/codegen/ControlStructuresTest.java b/idea/tests/org/jetbrains/jet/codegen/ControlStructuresTest.java index 3f154a44c31..0d703d1d242 100644 --- a/idea/tests/org/jetbrains/jet/codegen/ControlStructuresTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/ControlStructuresTest.java @@ -140,4 +140,8 @@ public class ControlStructuresTest extends CodegenTestCase { assertTrue(caught); assertEquals("foobar", sb.toString()); } + + public void testForUserType() throws Exception { + blackBoxFile("controlStructures/forUserType.jet"); + } }