diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesystem/utils/JoinComputer.java b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesystem/utils/JoinComputer.java new file mode 100644 index 0000000000..2af2d8818b --- /dev/null +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesystem/utils/JoinComputer.java @@ -0,0 +1,775 @@ +/** + * Copyright (c) 2016 NumberFour AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * NumberFour AG - Initial API and implementation + */ +package org.eclipse.n4js.typesystem.utils; + +import static java.util.Collections.singletonList; +import static org.eclipse.n4js.types.utils.SuperTypesList.newSuperTypesList; +import static org.eclipse.n4js.types.utils.TypeUtils.declaredSuperTypes; +import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.anyTypeRef; +import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.getContextResource; +import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.objectTypeRef; +import static org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.wrap; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.exists; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.filter; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.filterNull; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.forall; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.head; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.isEmpty; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.map; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.size; +import static org.eclipse.xtext.xbase.lib.IterableExtensions.toList; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.eclipse.n4js.scoping.members.TypingStrategyFilter; +import org.eclipse.n4js.ts.typeRefs.FunctionTypeExprOrRef; +import org.eclipse.n4js.ts.typeRefs.FunctionTypeExpression; +import org.eclipse.n4js.ts.typeRefs.IntersectionTypeExpression; +import org.eclipse.n4js.ts.typeRefs.ParameterizedTypeRef; +import org.eclipse.n4js.ts.typeRefs.ParameterizedTypeRefStructural; +import org.eclipse.n4js.ts.typeRefs.TypeArgument; +import org.eclipse.n4js.ts.typeRefs.TypeRef; +import org.eclipse.n4js.ts.typeRefs.TypeRefsFactory; +import org.eclipse.n4js.ts.typeRefs.UnionTypeExpression; +import org.eclipse.n4js.ts.typeRefs.Wildcard; +import org.eclipse.n4js.ts.types.AnyType; +import org.eclipse.n4js.ts.types.ContainerType; +import org.eclipse.n4js.ts.types.PrimitiveType; +import org.eclipse.n4js.ts.types.TClassifier; +import org.eclipse.n4js.ts.types.TField; +import org.eclipse.n4js.ts.types.TFormalParameter; +import org.eclipse.n4js.ts.types.TGetter; +import org.eclipse.n4js.ts.types.TMember; +import org.eclipse.n4js.ts.types.TMethod; +import org.eclipse.n4js.ts.types.TSetter; +import org.eclipse.n4js.ts.types.TStructField; +import org.eclipse.n4js.ts.types.TStructGetter; +import org.eclipse.n4js.ts.types.TStructMember; +import org.eclipse.n4js.ts.types.TStructMethod; +import org.eclipse.n4js.ts.types.TStructSetter; +import org.eclipse.n4js.ts.types.Type; +import org.eclipse.n4js.ts.types.TypeVariable; +import org.eclipse.n4js.ts.types.TypesFactory; +import org.eclipse.n4js.ts.types.TypingStrategy; +import org.eclipse.n4js.types.utils.SuperTypesList; +import org.eclipse.n4js.types.utils.TypeCompareHelper; +import org.eclipse.n4js.types.utils.TypeHelper; +import org.eclipse.n4js.types.utils.TypeUtils; +import org.eclipse.n4js.typesystem.N4JSTypeSystem; +import org.eclipse.n4js.utils.ContainerTypesHelper; +import org.eclipse.xtext.xbase.lib.IterableExtensions; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.inject.Inject; + +/** + * Type System Helper Strategy computing the join of a given collection of types. + *

+ * Definition from [Pierce02a, pp. 218]: + * + *

+ * Join J = S v T, if
+ *     S <: J, T <:J,                         common super type
+ *     forall U: S <:U and T <:U => J <: U    least
+ * 
+ *

+ * It is also rigor with regard to auto-conversion. That is, {@code string} and {@String} are not automatically + * converted into each other. This is because both types behave differently. E.g., if used as a boolean expression, + * {@code ""} and {@code new String("")} will be evaluated differently. The object type will always return + * true. This is even true for {@code new Boolean(false)}, which will also be evaluated to true (note however that + * {@code Boolean(false)} will return a {@code boolean} value, which is then evaluated to false). + *

+ * Special pseudo sub types of string, e.g. pathSelector, are handled as well. + */ +class JoinComputer extends TypeSystemHelperStrategy { + + @Inject + N4JSTypeSystem ts; + + @Inject + TypeCompareHelper tch; + + @Inject + TypeHelper th; + + @Inject + ContainerTypesHelper containerTypesHelper; + + @Inject + GenericsComputer genericsComputer; + + /** + * Returns the join, sometimes called least common super type (LCST), of the given types. This may be an + * intersection type but not a union type. See class description for details. + * + * @return the join, may be a contained reference. Thus clients may need to create a copy! + */ + TypeRef join(RuleEnvironment G, Iterable typeRefsToJoin) { + + if (typeRefsToJoin == null) { // quick return + return null; + } + + Iterable typeRefs = filterNull(typeRefsToJoin); + if (isEmpty(typeRefs)) { // quick return + return null; + } + if (size(typeRefs) == 1) { // quick return + return head(typeRefs); + } + + Iterable unionTypeRefs = filter(typeRefs, UnionTypeExpression.class); + Iterable nonUnionTypeRefs = filter(typeRefs, tr -> !(tr instanceof UnionTypeExpression)); + + TypeRef nonUnionJoin = joinNonUnionTypes(G, nonUnionTypeRefs); + if (isEmpty(unionTypeRefs)) { + return nonUnionJoin; + } else { + return joinNonUnionTypeWithUniontypes(G, nonUnionJoin, unionTypeRefs); + } + } + + /** + * join result of non-union types join with all union types + * + * see [N4JS Spec], 4.12 Union Type + * + * @param unionTypeRefs + * cannot be empty, called only from method above where it is checked + */ + private TypeRef joinNonUnionTypeWithUniontypes(RuleEnvironment G, TypeRef nonUnionJoin, + Iterable unionTypeRefs) { + + if (nonUnionJoin == null && size(unionTypeRefs) == 1) { + return tsh.simplify(G, head(unionTypeRefs)); + } + + UnionTypeExpression union = TypeRefsFactory.eINSTANCE.createUnionTypeExpression(); + if (nonUnionJoin != null) { + union.getTypeRefs().add(TypeUtils.copyIfContained(nonUnionJoin)); + } + union.getTypeRefs().addAll(toList(map(unionTypeRefs, tr -> TypeUtils.copyIfContained(tr)))); + return tsh.simplify(G, union); + } + + /** + * Called internally to compute the join of non-union types only. + * + * @param nonUnionTypeRefs + * type references, must not contain UnionTypeExpressions + */ + private TypeRef joinNonUnionTypes(RuleEnvironment G, Iterable nonUnionTypeRefs) { + if (isEmpty(nonUnionTypeRefs)) { // quick return + return null; + } + if (size(nonUnionTypeRefs) == 1) { // quick return + return head(nonUnionTypeRefs); + } + + // 1) find least common raw type. Although typerefs are used, the type arguments are ignored + List commonSuperTypesIgnoreTypeArgs; + Type firstType = head(nonUnionTypeRefs).getDeclaredType(); + if (firstType != null && forall(nonUnionTypeRefs, tr -> tr.getDeclaredType() == firstType)) { // shortcut + TypeRef head = head(nonUnionTypeRefs); + if (!head.isParameterized() && forall(nonUnionTypeRefs, tr -> !tr.isUseSiteStructuralTyping())) { // quick + // return + return head; + } + commonSuperTypesIgnoreTypeArgs = singletonList(head); + } else { + + // 1.1) collect all common super (raw) types. + commonSuperTypesIgnoreTypeArgs = commonSuperTypesTypeargsIgnored(G, nonUnionTypeRefs); + if (commonSuperTypesIgnoreTypeArgs.isEmpty()) { // quick return + return anyTypeRef(G); + } + + // 1.2) find LEAST common super (raw) type + if (commonSuperTypesIgnoreTypeArgs.size() > 1) { + + // remove all types which are super types of other types contained in commonSuperTypes + // precondition: the list is sorted: index of a type is always less than index of its supertypes! + // this is ensured by the order in which the elements are added to the list + int i = 0; + while (i < commonSuperTypesIgnoreTypeArgs.size()) { + removeAllSuperTypesOfType(commonSuperTypesIgnoreTypeArgs, commonSuperTypesIgnoreTypeArgs.get(i), G); + i = i + 1; + } + } + } + + // 2) handle type arguments + List commonSuperTypesParameterized; + if (forall(commonSuperTypesIgnoreTypeArgs, ta -> !ta.isParameterized())) { // shortcut + commonSuperTypesParameterized = commonSuperTypesIgnoreTypeArgs; + } else { + commonSuperTypesParameterized = Lists.newArrayListWithCapacity(commonSuperTypesIgnoreTypeArgs.size()); + for (TypeRef superTypeIgnoreTypeArgs : commonSuperTypesIgnoreTypeArgs) { + if (!superTypeIgnoreTypeArgs.isParameterized()) { + + // TODO raw, it it that simple? what about the bound in the generic declaration?! + commonSuperTypesParameterized.add(TypeUtils.copyIfContained(superTypeIgnoreTypeArgs)); + } else { + + // collect all super types again with these bounds, but this time properly substitute type variables + Set parameterizedSuperTypes = collectParameterizedSuperType(nonUnionTypeRefs, + superTypeIgnoreTypeArgs.getDeclaredType(), G); + if (parameterizedSuperTypes.size() == 1) { + commonSuperTypesParameterized.add(TypeUtils.copyIfContained(head(parameterizedSuperTypes))); + } else { + TypeRef merged = TypeUtils.copy(head(parameterizedSuperTypes)); + merged.getDeclaredTypeArgs().clear(); + int i = 0; + while (i < merged.getDeclaredType().getTypeVars().size()) { + int currentIndex = i; + TypeRef upperBound = join(G, + map(parameterizedSuperTypes, tr -> + // (typeArgs.get(currentIndex) as TypeRef).declaredUpperBound + ts.upperBound(G, (tr.getDeclaredTypeArgs().get(currentIndex))))); + TypeRef lowerBound = tsh.meet(G, + map(parameterizedSuperTypes, tr -> + // (typeArgs.get(currentIndex) as TypeRef).declaredLowerBound + ts.lowerBound(G, (tr.getDeclaredTypeArgs().get(currentIndex))))); + if (tch.compare(upperBound, lowerBound) == 0) { + merged.getDeclaredTypeArgs().add(TypeUtils.copyIfContained(upperBound)); + } else { + Wildcard wildcard = TypeRefsFactory.eINSTANCE.createWildcard(); + if (upperBound.isTopType() && !lowerBound.isBottomType()) { + + // this is the Java 6 and 7 behavior with wildcard capture: + // wildcard.declaredUpperBound = TypeUtils.copyIfContained(G.anyTypeRef) + // this is the Java 8 (and probably correct) behavior: + wildcard.setDeclaredLowerBound(TypeUtils.copyIfContained(lowerBound)); + } else { + wildcard.setDeclaredUpperBound(TypeUtils.copyIfContained(upperBound)); + } + merged.getDeclaredTypeArgs().add(wildcard); + } + i = i + 1; + } + commonSuperTypesParameterized.add(merged); + } + } + } + } + + // 3) handle structurally added members, first version in IDE-691, tested in IDE-142 + if (forall(nonUnionTypeRefs, tr -> tr.getDeclaredType() instanceof ContainerType)) { + TypingStrategy typingStrategy = TypingStrategy.DEFAULT; + + if (exists(nonUnionTypeRefs, tr -> tr.getTypingStrategy() == TypingStrategy.STRUCTURAL_FIELDS)) { + typingStrategy = TypingStrategy.STRUCTURAL_FIELDS; + } else if (exists(nonUnionTypeRefs, tr -> tr.getTypingStrategy() == TypingStrategy.STRUCTURAL)) { + typingStrategy = TypingStrategy.STRUCTURAL; + } + + if (typingStrategy != TypingStrategy.DEFAULT) { + ParameterizedTypeRefStructural ptrs = TypeRefsFactory.eINSTANCE.createParameterizedTypeRefStructural(); + TypeRef trTemplate; + if (head(commonSuperTypesParameterized) instanceof ParameterizedTypeRef) { + trTemplate = TypeUtils.copyIfContained(head(commonSuperTypesParameterized)); + } else { + trTemplate = objectTypeRef(G); + } + ptrs.setDefinedTypingStrategy(typingStrategy); + ptrs.setDeclaredType(trTemplate.getDeclaredType()); + ptrs.getDeclaredTypeArgs().addAll(trTemplate.getDeclaredTypeArgs()); + + TypingStrategyFilter filter = new TypingStrategyFilter(typingStrategy); + Map structuralMembersByName = new HashMap<>(); + Set structuralMembersWithDifferentTypeOrAlreadyContained = new HashSet<>(); + + Iterable filteredMembers = filter(containerTypesHelper.fromContext(getContextResource(G)) + .members((ContainerType) ptrs.getDeclaredType()), m -> filter.apply(m)); + + structuralMembersWithDifferentTypeOrAlreadyContained.addAll( + toList(map(filteredMembers, m -> m.getName()))); + + for (TypeRef tr : filter(nonUnionTypeRefs, tr -> tr.getDeclaredType() != ptrs.getDeclaredType())) { + + for (TMember structMember : Iterables.concat(tr.getStructuralMembers(), + filter(containerTypesHelper.fromContext(getContextResource(G)) + .members((ContainerType) tr.getDeclaredType()), m -> filter.apply(m)))) { + + if (!structuralMembersWithDifferentTypeOrAlreadyContained.contains(structMember.getName())) { + TMember duplicate = structuralMembersByName.get(structMember.getName()); + if (duplicate == null) { + structuralMembersByName.put(structMember.getName(), structMember); + } else if (!similarMember(G, duplicate, structMember)) { + structuralMembersWithDifferentTypeOrAlreadyContained.add(structMember.getName()); + structuralMembersByName.remove(structMember.getName()); + } + } + } + } + + ptrs.getGenStructuralMembers() + .addAll(toList(filter(map(structuralMembersByName.values(), m -> substituted(G, m)), + TStructMember.class))); + return ptrs; + } + } + + // 4) if more than one LCST has been found, create intersection + switch (commonSuperTypesParameterized.size()) { + case 0: + throw new IllegalStateException( + "Error processing least common super type, parameterization removed all types"); + case 1: + return head(commonSuperTypesParameterized); + default: + IntersectionTypeExpression intersectionTypeExpr = TypeRefsFactory.eINSTANCE + .createIntersectionTypeExpression(); + for (TypeRef tr : commonSuperTypesParameterized) { + intersectionTypeExpr.getTypeRefs().add(TypeUtils.copyIfContained(tr)); + } + + return intersectionTypeExpr; + + } + } + + private TMember substituted(RuleEnvironment G, TMember member) { + if (member instanceof TField) { + return substituted(G, (TField) member); + } + if (member instanceof TGetter) { + return substituted(G, (TGetter) member); + } + if (member instanceof TSetter) { + return substituted(G, (TSetter) member); + } + if (member instanceof TMethod) { + return substituted(G, (TMethod) member); + } + return member; + } + + private TMember substituted(RuleEnvironment G, TField member) { + if (member.getTypeRef().isParameterized()) { + TStructField subst = TypesFactory.eINSTANCE.createTStructField(); + subst.setName(member.getName()); + subst.setTypeRef(ts.substTypeVariables(G, member.getTypeRef())); + if (subst.getTypeRef() == null) { + subst.setTypeRef(anyTypeRef(G)); + } + return subst; + } else { + return member; + } + } + + private TMember substituted(RuleEnvironment G, TGetter member) { + if (member.getTypeRef() != null && member.getTypeRef().isParameterized()) { + TStructGetter subst = TypesFactory.eINSTANCE.createTStructGetter(); + subst.setName(member.getName()); + subst.setTypeRef(ts.substTypeVariables(G, member.getTypeRef())); + if (subst.getTypeRef() == null) { + subst.setTypeRef(anyTypeRef(G)); + } + return subst; + } else { + return member; + } + } + + private TMember substituted(RuleEnvironment G, TSetter member) { + if (member.getFpar() != null && member.getFpar().getTypeRef() != null + && member.getFpar().getTypeRef().isParameterized()) { + TStructSetter subst = TypesFactory.eINSTANCE.createTStructSetter(); + subst.setName(member.getName()); + + TypeRef tr = ts.substTypeVariables(G, member.getFpar().getTypeRef()); + if (tr == null) { + tr = anyTypeRef(G); + } + subst.setFpar(TypesFactory.eINSTANCE.createTFormalParameter()); + subst.getFpar().setName(member.getFpar().getName()); + subst.getFpar().setTypeRef(TypeUtils.copyIfContained(tr)); + return subst; + } else { + return member; + } + } + + private TMember substituted(RuleEnvironment G, TMethod member) { + FunctionTypeExpression ftype = (FunctionTypeExpression) ts.type(G, member); + TStructMethod subst = TypesFactory.eINSTANCE.createTStructMethod(); + subst.setName(member.getName()); + subst.getFpars().addAll(ftype.getFpars()); + subst.setReturnTypeRef(ftype.getReturnTypeRef()); + subst.getTypeVars().addAll(toList(map(member.getTypeVars(), tv -> TypeUtils.copyIfContained(tv)))); + return subst; + } + + private boolean similarMember(RuleEnvironment G, TMember m1, TMember m2) { + TypeRef t1 = ts.type(G, m1); + TypeRef t2 = ts.type(G, m2); + return ts.subtypeSucceeded(G, t1, t2) && ts.subtypeSucceeded(G, t2, t1); + } + + /* + * Removes all super types of ref from list of refs. This method is optimized for leastCommonSuperType and assumes + * that all types in orderedRefs are ordered as returned by collecAllDeclaredSuperTypes(). + */ + private void removeAllSuperTypesOfType(List orderedRefs, TypeRef ref, RuleEnvironment G) { + Iterable nonLeastSuperTypes = Iterables.concat( + th.collectAllDeclaredSuperTypesTypeargsIgnored(ref, false), + RuleEnvironmentExtensions.collectAllImplicitSuperTypes(G, ref)); + + for (TypeRef nonLeastSuperType : nonLeastSuperTypes) { + if (!th.removeTypeRef(orderedRefs, nonLeastSuperType)) { + return; + } + } + } + + /* + * Search for reference in list of super types of each type ref matching given rawSuperType, using a depth first + * search. + * + * @param refs collection of type references, size > 1 + * + * @param rawSuperTypeRef a raw super type, which is a common super type of all types in refs + */ + private Set collectParameterizedSuperType(Iterable refs, + Type rawSuperType, RuleEnvironment G) { + + Set result = new TreeSet<>(tch.getTypeRefComparator()); + for (TypeRef typeRef : refs) { + List pathFromSuperType = computePathFromSuperTypeReflexive(typeRef, rawSuperType, new HashSet<>()); + if (pathFromSuperType == null) { + throw new IllegalStateException("Did not found " + rawSuperType + " in super types of " + typeRef); + } + TypeRef concreteSuperTypeRef = head(pathFromSuperType); + + if (containsUnboundTypeVariables(concreteSuperTypeRef)) { + RuleEnvironment Gnext = wrap(G); + // original code: + // var Gnext = G; + // // parameterize the references: + // for (TypeRef tr : pathFromSuperType.reverseView) { + // var Gnew = new RuleEnvironment(); + // Gnew.next = Gnext; + // Gnext = Gnew; + // } + concreteSuperTypeRef = genericsComputer.bindTypeVariables(Gnext, concreteSuperTypeRef); + } + + result.add( + TypeUtils.copyIfContained(concreteSuperTypeRef)); + } + + return result; + } + + /** + * Returns transitive reflexive closure of common super types. Type arguments are ignored here. + * + * @return the returned list is sorted, that is, a type's super types are always AFTER the type in the list + */ + private List commonSuperTypesTypeargsIgnored(RuleEnvironment G, Iterable typeRefs) { + List commonSuperTypes = new ArrayList<>(); + for (TypeRef t : typeRefs) { + if (!addSuperTypesToCommonList(G, t, commonSuperTypes)) { + return Collections.emptyList(); + } + } + return commonSuperTypes; + } + + /** + * Returns transitive, non-reflexive closure of implicit super types. Relexive means, that in case of e.g., Object, + * Object is returned itself. + */ + private void collectAllImplicitSuperTypes(TypeRef ref, RuleEnvironment G, + SuperTypesList superTypesList) { + superTypesList.addAll(RuleEnvironmentExtensions.collectAllImplicitSuperTypes(G, ref)); + } + + /** + * Adds or intersects types in reflexive transitive closure of a given type t to/with given list of super + * types. + * + * @return true, if super types have been added and if client should proceed;
+ * false, if no common super type can ever by found and client can stop looking for it + */ + private boolean addSuperTypesToCommonList(RuleEnvironment G, TypeRef t, + List commonSuperTypes) { + + if (t instanceof IntersectionTypeExpression) { + return addSuperTypesToCommonList(G, (IntersectionTypeExpression) t, commonSuperTypes); + } + if (t instanceof UnionTypeExpression) { + return addSuperTypesToCommonList(G, (UnionTypeExpression) t, commonSuperTypes); + } + if (t instanceof FunctionTypeExprOrRef) { + return addSuperTypesToCommonList(G, (FunctionTypeExprOrRef) t, commonSuperTypes); + } + + if (t.getDeclaredType() instanceof TClassifier) { + SuperTypesList allDeclaredSuperTypes = newSuperTypesList(tch.getTypeRefComparator()); + allDeclaredSuperTypes.add(t); + th.collectAllDeclaredSuperTypesTypeargsIgnored(t, allDeclaredSuperTypes); + collectAllImplicitSuperTypes(t, G, allDeclaredSuperTypes); + + addOrIntersectTypes(G, commonSuperTypes, allDeclaredSuperTypes); + } else if (t.getDeclaredType() instanceof AnyType) { + return false; + } else if (t.getDeclaredType() instanceof PrimitiveType) { + addOrIntersectTypeWithAssignmentCompatibles(commonSuperTypes, t); + } else { + + // ignored (as they are common pseudo-sub types): NullType, UndefinedType, VoidType + // or + // handled in other addSuperTypesToCommonList, as they have different references + } + return true; + } + + private boolean addSuperTypesToCommonList(RuleEnvironment G, IntersectionTypeExpression t, + List commonSuperTypes) { + SuperTypesList allDeclaredSuperTypes = newSuperTypesList(tch.getTypeRefComparator()); + for (TypeRef containedRef : t.getTypeRefs()) { + allDeclaredSuperTypes.add(containedRef); + th.collectAllDeclaredSuperTypesTypeargsIgnored(containedRef, allDeclaredSuperTypes); + collectAllImplicitSuperTypes(containedRef, G, allDeclaredSuperTypes); + } + addOrIntersectTypes(G, commonSuperTypes, allDeclaredSuperTypes); + return true; + } + + private boolean addSuperTypesToCommonList(RuleEnvironment G, UnionTypeExpression t, + List commonSuperTypes) { + SuperTypesList allDeclaredSuperTypes = newSuperTypesList(tch.getTypeRefComparator()); + + allDeclaredSuperTypes.add(t); + addOrIntersectTypes(G, commonSuperTypes, allDeclaredSuperTypes); + return true; + } + + private void addOrIntersectTypeWithAssignmentCompatibles(List commonSuperTypes, + TypeRef typeRef) { + if (commonSuperTypes.isEmpty()) { // quick break + commonSuperTypes.add(typeRef); + return; + } + + if (th.containsByType(commonSuperTypes, typeRef)) { + if (commonSuperTypes.size() == 1) { + return; + } + commonSuperTypes.clear(); + commonSuperTypes.add(typeRef); + return; + } + + Type type = typeRef.getDeclaredType(); + if (type instanceof PrimitiveType) { + int index = th.findTypeRefOrAssignmentCompatible(commonSuperTypes, typeRef); + if (index >= 0) { + // e.g. we have string and found pathselector + if (((PrimitiveType) type).getAssignmentCompatible() == null) { + commonSuperTypes.clear(); + commonSuperTypes.add(typeRef); + } else { // e.g., we have pathselector and found string + if (commonSuperTypes.size() != 1) { + TypeRef tr = commonSuperTypes.get(index); + commonSuperTypes.clear(); + commonSuperTypes.add(tr); + } // else e.g. there is only string in the list, leave it there + } + return; + } + } + commonSuperTypes.clear(); + } + + private boolean addSuperTypesToCommonList(RuleEnvironment G, FunctionTypeExprOrRef f, + List commonSuperTypes) { + SuperTypesList allDeclaredSuperTypes = newSuperTypesList(tch.getTypeRefComparator()); + allDeclaredSuperTypes.add(f); + collectAllImplicitSuperTypes(f, G, allDeclaredSuperTypes); + addOrIntersectTypes(G, commonSuperTypes, allDeclaredSuperTypes); + return true; + } + + /** + * Returns path from super type to current ref, including the super type and the initial type. + * + * @return path, or null if the raw super type has not been found (which probably is an illegal result, except in + * combination with intersection types) + */ + private List computePathFromSuperTypeReflexive(TypeRef ref, Type rawSuperType, + Set processedTypes) { + + if (ref instanceof IntersectionTypeExpression) { + return computePathFromSuperTypeReflexive((IntersectionTypeExpression) ref, rawSuperType, processedTypes); + } + + if (ref.getDeclaredType() == rawSuperType) { + ArrayList result = new ArrayList<>(); + result.add(ref); + return result; + } else { + for (ParameterizedTypeRef superTypeRef : declaredSuperTypes(ref.getDeclaredType())) { + if (processedTypes.add(superTypeRef.getDeclaredType())) { + List superPath = computePathFromSuperTypeReflexive(superTypeRef, rawSuperType, + processedTypes); + if (superPath != null) { + superPath.add(ref); + + // we can break here, although there might be several paths to the rawSuperType. However, + // all paths must result in the same parameterized version, so we only need one + return superPath; + } + } + } + } + return null; + } + + /** + * For intersection types, this returns the first path found. + */ + private List computePathFromSuperTypeReflexive(IntersectionTypeExpression ref, + Type rawSuperType, Set processedTypes) { + for (TypeRef typeRef : ref.getTypeRefs()) { + List path = computePathFromSuperTypeReflexive(typeRef, rawSuperType, processedTypes); + if (path != null) { + return path; + } + } + return null; + } + + private void addOrIntersectTypes(RuleEnvironment G, List commonSuperTypes, + SuperTypesList allDeclaredSuperTypes) { + if (commonSuperTypes.isEmpty()) { + commonSuperTypes.addAll(allDeclaredSuperTypes); + } else { + + // extract all functions, they have to be handled differently: + // there must be only one FunctionTypeExprOrRef in the list: + FunctionTypeExprOrRef currentSuperFunction = head( + filter(allDeclaredSuperTypes, FunctionTypeExprOrRef.class)); + FunctionTypeExprOrRef prevCommonSuperFunction = (currentSuperFunction != null) + ? head(filter(commonSuperTypes, FunctionTypeExprOrRef.class)) + : null; // do not search, would be removed anyway + + th.retainAllTypeRefs(commonSuperTypes, allDeclaredSuperTypes); + if (tch.getTypeRefComparator().compare(prevCommonSuperFunction, currentSuperFunction) != 0) { + // null or retained anyway + FunctionTypeExprOrRef commonSuperFunction = joinFunctionTypeRefs(G, currentSuperFunction, + prevCommonSuperFunction); + if (commonSuperFunction != null) { + commonSuperTypes.add(commonSuperFunction); + } + } + } + + } + + /** + * May return null if no join is possible, e.g., in f(string) and f(number) + */ + private FunctionTypeExprOrRef joinFunctionTypeRefs(RuleEnvironment G, FunctionTypeExprOrRef f1, + FunctionTypeExprOrRef f2) { + FunctionTypeExpression joinedFunctionTypeExpr = TypeRefsFactory.eINSTANCE.createFunctionTypeExpression(); + + if (f1.getReturnTypeRef() != null && f2.getReturnTypeRef() != null) { + joinedFunctionTypeExpr.setReturnTypeRef( + TypeUtils.copyIfContained(tsh.join(G, f1.getReturnTypeRef(), f2.getReturnTypeRef()))); + } + joinedFunctionTypeExpr.setReturnValueMarkedOptional(f1.isReturnValueOptional() || f2.isReturnValueOptional()); + + int maxParSize = Math.max(f1.getFpars().size(), f2.getFpars().size()); + int i = 0; + boolean varOrOpt1 = false; + boolean varOrOpt2 = false; + while (i < maxParSize) { + TFormalParameter par1 = getFParSmartAndFailSafe(f1, i); + TFormalParameter par2 = getFParSmartAndFailSafe(f2, i); + + TFormalParameter fpar = null; + if (par1 == null) { + fpar = TypeUtils.copy(par2); + } else if (par2 == null) { + fpar = TypeUtils.copy(par1); + } else { + if (par1.isVariadicOrOptional()) { + varOrOpt1 = true; + } + if (par2.isVariadicOrOptional()) { + varOrOpt2 = true; + } + + fpar = TypesFactory.eINSTANCE.createTFormalParameter(); + TypeRef meet = tsh.meet(G, par1.getTypeRef(), par2.getTypeRef()); + + if (meet == null) { + if (varOrOpt1 && varOrOpt2) { + return joinedFunctionTypeExpr; // cut optional or variadic non-matching arguments + } else { + return null; // no join of function possible + } + } + + TypeRef parType = TypeUtils.copyIfContained(meet); + fpar.setTypeRef(parType); + + if (par1.isVariadic() && par2.isVariadic()) { + fpar.setVariadic(true); + } else if (par1.isVariadicOrOptional() && par2.isVariadicOrOptional()) { + fpar.setHasInitializerAssignment(true); + } + } + joinedFunctionTypeExpr.getFpars().add(fpar); + i = i + 1; + } + return joinedFunctionTypeExpr; + } + + private TFormalParameter getFParSmartAndFailSafe(FunctionTypeExprOrRef f, int index) { + if (f.getFpars().size() == 0) { + return null; + } + if (index < f.getFpars().size()) { + return f.getFpars().get(index); + } + TFormalParameter last = IterableExtensions.last(f.getFpars()); + if (last.isVariadic()) { + return last; + } + return null; + } + + private boolean containsUnboundTypeVariables(TypeArgument typeArg) { + if (typeArg instanceof ParameterizedTypeRef) { + ParameterizedTypeRef ptr = (ParameterizedTypeRef) typeArg; + Type declType = ptr.getDeclaredType(); + return declType instanceof TypeVariable // ok, that's simple + // no type args, type variable is indirectly referenced from raw type + || (!ptr.isParameterized() && declType.isGeneric()) + // transitively + || exists(ptr.getDeclaredTypeArgs(), ta -> containsUnboundTypeVariables(ta)); + } + return false; + } +} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesystem/utils/JoinComputer.xtend b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesystem/utils/JoinComputer.xtend deleted file mode 100644 index df5562bb0a..0000000000 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/typesystem/utils/JoinComputer.xtend +++ /dev/null @@ -1,698 +0,0 @@ -/** - * Copyright (c) 2016 NumberFour AG. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * NumberFour AG - Initial API and implementation - */ -package org.eclipse.n4js.typesystem.utils - -import com.google.common.collect.Iterables -import com.google.common.collect.Lists -import com.google.inject.Inject -import java.util.HashMap -import java.util.HashSet -import java.util.List -import java.util.Set -import org.eclipse.n4js.scoping.members.TypingStrategyFilter -import org.eclipse.n4js.ts.typeRefs.FunctionTypeExprOrRef -import org.eclipse.n4js.ts.typeRefs.FunctionTypeExpression -import org.eclipse.n4js.ts.typeRefs.IntersectionTypeExpression -import org.eclipse.n4js.ts.typeRefs.ParameterizedTypeRef -import org.eclipse.n4js.ts.typeRefs.TypeArgument -import org.eclipse.n4js.ts.typeRefs.TypeRef -import org.eclipse.n4js.ts.typeRefs.TypeRefsFactory -import org.eclipse.n4js.ts.typeRefs.UnionTypeExpression -import org.eclipse.n4js.ts.types.AnyType -import org.eclipse.n4js.ts.types.ContainerType -import org.eclipse.n4js.ts.types.PrimitiveType -import org.eclipse.n4js.ts.types.TClassifier -import org.eclipse.n4js.ts.types.TField -import org.eclipse.n4js.ts.types.TFormalParameter -import org.eclipse.n4js.ts.types.TGetter -import org.eclipse.n4js.ts.types.TMember -import org.eclipse.n4js.ts.types.TMethod -import org.eclipse.n4js.ts.types.TSetter -import org.eclipse.n4js.ts.types.TStructMember -import org.eclipse.n4js.ts.types.Type -import org.eclipse.n4js.ts.types.TypeVariable -import org.eclipse.n4js.ts.types.TypesFactory -import org.eclipse.n4js.ts.types.TypingStrategy -import org.eclipse.n4js.types.utils.SuperTypesList -import org.eclipse.n4js.types.utils.TypeCompareHelper -import org.eclipse.n4js.types.utils.TypeHelper -import org.eclipse.n4js.types.utils.TypeUtils -import org.eclipse.n4js.typesystem.N4JSTypeSystem -import org.eclipse.n4js.utils.ContainerTypesHelper - -import static java.util.Collections.* -import static org.eclipse.n4js.types.utils.SuperTypesList.* - -import static extension org.eclipse.n4js.types.utils.TypeUtils.* -import static extension org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions.* - -/** - * Type System Helper Strategy computing the join of a given collection of types. - *

- * Definition from [Pierce02a, pp. 218]: - *

- * Join J = S v T, if
- *     S <: J, T <:J,                         common super type
- *     forall U: S <:U and T <:U => J <: U    least
- * 
- *

- * It is also rigor with regard to auto-conversion. That is, {@code string} and {@String} are - * not automatically converted into each other. This is because both types behave differently. - * E.g., if used as a boolean expression, {@code ""} and {@code new String("")} will be evaluated differently. - * The object type will always return true. This is even true for {@code new Boolean(false)}, which will also - * be evaluated to true (note however that {@code Boolean(false)} will return a {@code boolean} value, which - * is then evaluated to false). - *

- * Special pseudo sub types of string, e.g. pathSelector, are handled as well. - */ -package class JoinComputer extends TypeSystemHelperStrategy { - - @Inject - private N4JSTypeSystem ts - - @Inject - extension TypeCompareHelper; - - @Inject - extension TypeHelper; - - @Inject ContainerTypesHelper containerTypesHelper; - - @Inject - GenericsComputer genericsComputer; - - /** - * Returns the join, sometimes called least common super type (LCST), - * of the given types. This may be an intersection type but not a union type. - * See class description for details. - * - * @return the join, may be a contained reference. Thus clients may need to create a copy! - */ - def TypeRef join(RuleEnvironment G, Iterable typeRefsToJoin) { - - if (typeRefsToJoin === null) { // quick return - return null; - } - - val typeRefs = typeRefsToJoin.filterNull; - if (typeRefs.empty) { // quick return - return null; - } - if (typeRefs.size === 1) { // quick return - return typeRefs.head; - } - - val unionTypeRefs = typeRefs.filter(UnionTypeExpression); - val nonUnionTypeRefs = typeRefs.filter[! (it instanceof UnionTypeExpression)] - - val nonUnionJoin = joinNonUnionTypes(G, nonUnionTypeRefs); - if (unionTypeRefs.empty) { - return nonUnionJoin; - } else { - return joinNonUnionTypeWithUniontypes(G, nonUnionJoin, unionTypeRefs) - } - } - - /** - * join result of non-union types join with all union types - * @see [N4JS Spec], 4.12 Union Type - * @param unionTypeRefs cannot be empty, called only from method above where it is checked - */ - private def TypeRef joinNonUnionTypeWithUniontypes(RuleEnvironment G, TypeRef nonUnionJoin, - Iterable unionTypeRefs) { - - if (nonUnionJoin === null && unionTypeRefs.size == 1) { - return tsh.simplify(G, unionTypeRefs.head) - } - - val union = TypeRefsFactory.eINSTANCE.createUnionTypeExpression(); - if (nonUnionJoin !== null) { - union.typeRefs.add(TypeUtils.copyIfContained(nonUnionJoin)); - } - union.typeRefs.addAll((unionTypeRefs.map[TypeUtils.copyIfContained(it)])) - return tsh.simplify(G, union) - } - - /** - * Called internally to compute the join of non-union types only. - * @param nonUnionTypeRefs type references, must not contain UnionTypeExpressions - */ - private def TypeRef joinNonUnionTypes(RuleEnvironment G, Iterable nonUnionTypeRefs) { - if (nonUnionTypeRefs.empty) { // quick return - return null; - } - if (nonUnionTypeRefs.size === 1) { // quick return - return nonUnionTypeRefs.head; - } - - // 1) find least common raw type. Although typerefs are used, the type arguments are ignored - var List commonSuperTypesIgnoreTypeArgs; - val firstType = nonUnionTypeRefs.head.declaredType - if (firstType !== null && nonUnionTypeRefs.forall[declaredType === firstType]) { // shortcut - val head = nonUnionTypeRefs.head - if (! head.parameterized && nonUnionTypeRefs.forall[! useSiteStructuralTyping]) { // quick return - return head; - } - commonSuperTypesIgnoreTypeArgs = singletonList(head) - } else { - - // 1.1) collect all common super (raw) types. - commonSuperTypesIgnoreTypeArgs = commonSuperTypesTypeargsIgnored(G, nonUnionTypeRefs) - if (commonSuperTypesIgnoreTypeArgs.empty) { // quick return - return G.anyTypeRef - } - - // 1.2) find LEAST common super (raw) type - if (commonSuperTypesIgnoreTypeArgs.size > 1) { - - // remove all types which are super types of other types contained in commonSuperTypes - // precondition: the list is sorted: index of a type is always less than index of its supertypes! - // this is ensured by the order in which the elements are added to the list - var i = 0; - while (i < commonSuperTypesIgnoreTypeArgs.size) { - commonSuperTypesIgnoreTypeArgs.removeAllSuperTypesOfType(commonSuperTypesIgnoreTypeArgs.get(i), G); - i = i + 1; - } - } - } - - // 2) handle type arguments - var List commonSuperTypesParameterized; - if (commonSuperTypesIgnoreTypeArgs.forall[! parameterized]) { // shortcut - commonSuperTypesParameterized = commonSuperTypesIgnoreTypeArgs; - } else { - commonSuperTypesParameterized = Lists.newArrayListWithCapacity(commonSuperTypesIgnoreTypeArgs.size) - for (TypeRef superTypeIgnoreTypeArgs : commonSuperTypesIgnoreTypeArgs) { - if (! superTypeIgnoreTypeArgs.parameterized) { - - // TODO raw, it it that simple? what about the bound in the generic declaration?! - commonSuperTypesParameterized.add(TypeUtils.copyIfContained(superTypeIgnoreTypeArgs)); - } else { - - // collect all super types again with these bounds, but this time properly substitute type variables - val parameterizedSuperTypes = collectParameterizedSuperType(nonUnionTypeRefs, - superTypeIgnoreTypeArgs.declaredType, G); - if (parameterizedSuperTypes.size == 1) { - commonSuperTypesParameterized.add(TypeUtils.copyIfContained(parameterizedSuperTypes.head)); - } else { - val TypeRef merged = TypeUtils.copy(parameterizedSuperTypes.head); - merged.declaredTypeArgs.clear(); - var i = 0; - while (i < merged.declaredType.typeVars.size) { - val currentIndex = i; - val upperBound = join(G, - parameterizedSuperTypes.map [ - // (typeArgs.get(currentIndex) as TypeRef).declaredUpperBound - ts.upperBound(G, (declaredTypeArgs.get(currentIndex))) - ]) - val lowerBound = tsh.meet(G, - parameterizedSuperTypes.map [ - // (typeArgs.get(currentIndex) as TypeRef).declaredLowerBound - ts.lowerBound(G, (declaredTypeArgs.get(currentIndex))) - ]) - if (compare(upperBound, lowerBound) == 0) { - merged.declaredTypeArgs.add(TypeUtils.copyIfContained(upperBound)) - } else { - val wildcard = TypeRefsFactory.eINSTANCE.createWildcard; - if (upperBound.topType && ! lowerBound.bottomType) { - - // this is the Java 6 and 7 behavior with wildcard capture: - // wildcard.declaredUpperBound = TypeUtils.copyIfContained(G.anyTypeRef) - // this is the Java 8 (and probably correct) behavior: - wildcard.declaredLowerBound = TypeUtils.copyIfContained(lowerBound); - } else { - wildcard.declaredUpperBound = TypeUtils.copyIfContained(upperBound); - } - merged.declaredTypeArgs.add(wildcard) - } - i = i + 1; - } - commonSuperTypesParameterized.add(merged); - } - } - } - } - - // 3) handle structurally added members, first version in IDE-691, tested in IDE-142 - if (nonUnionTypeRefs.forall[declaredType instanceof ContainerType]) { - val typingStrategy = if (nonUnionTypeRefs.exists[it.typingStrategy === TypingStrategy.STRUCTURAL_FIELDS]) { - TypingStrategy.STRUCTURAL_FIELDS - } else if (nonUnionTypeRefs.exists[it.typingStrategy === TypingStrategy.STRUCTURAL]) { - TypingStrategy.STRUCTURAL - } else { - TypingStrategy.DEFAULT - } - - if (typingStrategy !== TypingStrategy.DEFAULT) { - val ptrs = TypeRefsFactory.eINSTANCE.createParameterizedTypeRefStructural(); - val trTemplate = if (commonSuperTypesParameterized.head instanceof ParameterizedTypeRef) { - TypeUtils.copyIfContained(commonSuperTypesParameterized.head); - } else { - G.objectTypeRef?.copyIfContained - } - ptrs.definedTypingStrategy = typingStrategy - ptrs.declaredType = trTemplate.declaredType; - ptrs.declaredTypeArgs.addAll(trTemplate.declaredTypeArgs); - - val filter = new TypingStrategyFilter(typingStrategy); - val structuralMembersByName = new HashMap(); - val structuralMembersWithDifferentTypeOrAlreadyContained = new HashSet(); - structuralMembersWithDifferentTypeOrAlreadyContained.addAll( - containerTypesHelper.fromContext(G.contextResource).members(ptrs.declaredType as ContainerType).filter[filter.apply(it)].map[name]); - - for (TypeRef tr : nonUnionTypeRefs.filter[declaredType !== ptrs.declaredType]) { - for (TMember structMember : Iterables.concat(tr.structuralMembers, - containerTypesHelper.fromContext(G.contextResource).members(tr.declaredType as ContainerType).filter[filter.apply(it)])) { - if (!structuralMembersWithDifferentTypeOrAlreadyContained.contains(structMember.name)) { - val duplicate = structuralMembersByName.get(structMember.name); - if (duplicate === null) { - structuralMembersByName.put(structMember.name, structMember); - } else if (! similarMember(G, duplicate, structMember)) { - structuralMembersWithDifferentTypeOrAlreadyContained.add(structMember.name); - structuralMembersByName.remove(structMember.name); - } - } - } - } - - ptrs.genStructuralMembers.addAll(structuralMembersByName.values().map[substituted(G, it)].filter(TStructMember)); - return ptrs; - } - } - - // 4) if more than one LCST has been found, create intersection - val singleLCST = switch commonSuperTypesParameterized.size { - case 0: - throw new IllegalStateException( - "Error processing least common super type, parameterization removed all types") - case 1: - commonSuperTypesParameterized.head - default: { - val intersectionTypeExpr = TypeRefsFactory.eINSTANCE.createIntersectionTypeExpression(); - commonSuperTypesParameterized.forEach[ - intersectionTypeExpr.typeRefs.add(TypeUtils.copyIfContained(it))]; - intersectionTypeExpr; - } - } - - return singleLCST; - } - - private def dispatch substituted(RuleEnvironment G, TMember member) { - return member; - } - - private def dispatch substituted(RuleEnvironment G, TField member) { - if (member.typeRef.parameterized) { - val subst = TypesFactory.eINSTANCE.createTStructField(); - subst.name = member.name; - subst.typeRef = ts.substTypeVariables(G, member.typeRef); - if (subst.typeRef === null) { - subst.typeRef = G.anyTypeRef; - } - return subst; - } else { - return member - } - } - - private def dispatch substituted(RuleEnvironment G, TGetter member) { - if (member.typeRef !== null && member.typeRef.parameterized) { - val subst = TypesFactory.eINSTANCE.createTStructGetter(); - subst.name = member.name; - subst.typeRef = ts.substTypeVariables(G, member.typeRef); - if (subst.typeRef === null) { - subst.typeRef = G.anyTypeRef; - } - return subst; - } else { - return member - } - } - - private def dispatch substituted(RuleEnvironment G, TSetter member) { - if (member.fpar !== null && member.fpar.typeRef !== null && member.fpar.typeRef.parameterized) { - val subst = TypesFactory.eINSTANCE.createTStructSetter(); - subst.name = member.name; - - var tr = ts.substTypeVariables(G, member.fpar.typeRef); - if (tr === null) { - tr = G.anyTypeRef; - } - subst.fpar = TypesFactory.eINSTANCE.createTFormalParameter(); - subst.fpar.name = member.fpar.name; - subst.fpar.typeRef = TypeUtils.copyIfContained(tr); - return subst; - } else { - return member - } - } - - private def dispatch substituted(RuleEnvironment G, TMethod member) { - val ftype = ts.type(G, member) as FunctionTypeExpression - val subst = TypesFactory.eINSTANCE.createTStructMethod(); - subst.name = member.name; - subst.fpars.addAll(ftype.fpars); - subst.returnTypeRef = ftype.returnTypeRef; - subst.typeVars.addAll(member.typeVars.map[TypeUtils.copyIfContained(it)]); - return subst; - } - - def similarMember(RuleEnvironment G, TMember m1, TMember m2) { - - val t1 = ts.type(G, m1); - val t2 = ts.type(G, m2); - return ts.subtypeSucceeded(G, t1, t2) && ts.subtypeSucceeded(G, t2, t1) - } - - /* - * Removes all super types of ref from list of refs. This method is optimized for leastCommonSuperType and - * assumes that all types in orderedRefs are ordered as returned by collecAllDeclaredSuperTypes(). - */ - private def void removeAllSuperTypesOfType(List orderedRefs, TypeRef ref, RuleEnvironment G) { - val Iterable nonLeastSuperTypes = ref.collectAllDeclaredSuperTypesTypeargsIgnored(false) + - G.collectAllImplicitSuperTypes(ref) - - for (nonLeastSuperType : nonLeastSuperTypes) { - if (!orderedRefs.removeTypeRef(nonLeastSuperType)) { - return; - } - } - } - - /* - * Search for reference in list of super types of each type ref matching given rawSuperType, using a - * depth first search. - * - * @param refs collection of type references, size > 1 - * @param rawSuperTypeRef a raw super type, which is a common super type of all types in refs - */ - private def Set collectParameterizedSuperType(Iterable refs, - Type rawSuperType, RuleEnvironment G) { - - val result = newTreeSet(getTypeRefComparator); - for (typeRef : refs) { - val pathFromSuperType = typeRef.computePathFromSuperTypeReflexive(rawSuperType, newHashSet()) - if (pathFromSuperType === null) { - throw new IllegalStateException("Did not found " + rawSuperType + " in super types of " + typeRef); - } - val concreteSuperTypeRef = pathFromSuperType.head; - - result.add( - TypeUtils.copyIfContained( - if (! concreteSuperTypeRef.containsUnboundTypeVariables) { - concreteSuperTypeRef - } else { - val Gnext = G.wrap; -// original code: -// var Gnext = G; -// // parameterize the references: -// for (TypeRef tr : pathFromSuperType.reverseView) { -// var Gnew = new RuleEnvironment(); -// Gnew.next = Gnext; -// Gnext = Gnew; -// } - genericsComputer.bindTypeVariables(Gnext,concreteSuperTypeRef); - } - )); - } - - return result; - } - - /** - * Returns transitive reflexive closure of common super types. - * Type arguments are ignored here. - * @return the returned list is sorted, that is, a type's super types are always AFTER the type in the list - */ - private def List commonSuperTypesTypeargsIgnored(RuleEnvironment G, Iterable typeRefs) { - val List commonSuperTypes = newArrayList(); - for (TypeRef t : typeRefs) { - if (! addSuperTypesToCommonList(G, t, commonSuperTypes)) { - return emptyList - } - } - return commonSuperTypes - } - - /** - * Returns transitive, non-reflexive closure of implicit super types. Relexive means, that in case of e.g., Object, Object is returned itself. - */ - private def void collectAllImplicitSuperTypes(TypeRef ref, RuleEnvironment G, - SuperTypesList superTypesList) { - superTypesList.addAll(G.collectAllImplicitSuperTypes(ref)); - } - - /** - * Adds or intersects types in reflexive transitive closure of a given type t to/with given list of super types. - * - * @return true, if super types have been added and if client should proceed;
- * false, if no common super type can ever by found and client can stop looking for it - */ - private def dispatch boolean addSuperTypesToCommonList(RuleEnvironment G, TypeRef t, - List commonSuperTypes) { - switch t.declaredType { - TClassifier: { - val allDeclaredSuperTypes = newSuperTypesList(getTypeRefComparator); - allDeclaredSuperTypes.add(t) - t.collectAllDeclaredSuperTypesTypeargsIgnored(allDeclaredSuperTypes) - t.collectAllImplicitSuperTypes(G, allDeclaredSuperTypes) - - addOrIntersectTypes(G, commonSuperTypes, allDeclaredSuperTypes) - } - AnyType: - return false - PrimitiveType: - addOrIntersectTypeWithAssignmentCompatibles(G, commonSuperTypes, t) - default: { - // ignored (as they are common pseudo-sub types): NullType, UndefinedType, VoidType - // or - // handled in other addSuperTypesToCommonList, as they have different references - } - } - return true; - } - - private def dispatch addSuperTypesToCommonList(RuleEnvironment G, IntersectionTypeExpression t, - List commonSuperTypes) { - val allDeclaredSuperTypes = newSuperTypesList(getTypeRefComparator); - for (TypeRef containedRef : t.typeRefs) { - allDeclaredSuperTypes.add(containedRef) - containedRef.collectAllDeclaredSuperTypesTypeargsIgnored(allDeclaredSuperTypes) - containedRef.collectAllImplicitSuperTypes(G, allDeclaredSuperTypes) - } - addOrIntersectTypes(G, commonSuperTypes, allDeclaredSuperTypes) - return true; - } - - private def dispatch addSuperTypesToCommonList(RuleEnvironment G, UnionTypeExpression t, - List commonSuperTypes) { - val allDeclaredSuperTypes = newSuperTypesList(getTypeRefComparator); - - allDeclaredSuperTypes.add(t) - addOrIntersectTypes(G, commonSuperTypes, allDeclaredSuperTypes) - return true; - } - - private def void addOrIntersectTypeWithAssignmentCompatibles(RuleEnvironment G, List commonSuperTypes, - TypeRef typeRef) { - if (commonSuperTypes.empty) { // quick break - commonSuperTypes.add(typeRef); - return; - } - - if (commonSuperTypes.containsByType(typeRef)) { - if (commonSuperTypes.size == 1) { - return; - } - commonSuperTypes.clear(); - commonSuperTypes.add(typeRef); - return; - } - - val type = typeRef.declaredType - if (type instanceof PrimitiveType) { - val index = commonSuperTypes.findTypeRefOrAssignmentCompatible(typeRef) - if (index >= 0) { - if (type.assignmentCompatible === null) { // e.g. we have string and found pathselector - commonSuperTypes.clear(); - commonSuperTypes.add(typeRef); - } else { // e.g., we have pathselector and found string - if (commonSuperTypes.size != 1) { - val TypeRef tr = commonSuperTypes.get(index) - commonSuperTypes.clear(); - commonSuperTypes.add(tr); - } // else e.g. there is only string in the list, leave it there - } - return; - } - } - commonSuperTypes.clear(); - } - - private def dispatch addSuperTypesToCommonList(RuleEnvironment G, FunctionTypeExprOrRef f, - List commonSuperTypes) { - val allDeclaredSuperTypes = newSuperTypesList(getTypeRefComparator); - allDeclaredSuperTypes.add(f) - f.collectAllImplicitSuperTypes(G, allDeclaredSuperTypes) - addOrIntersectTypes(G, commonSuperTypes, allDeclaredSuperTypes) - return true; - } - - /* - * Returns path from super type to current ref, including the super type and the initial type. - * @return path, or null if the raw super type has not been found (which probably is an illegal result, except in combination with intersection types) - */ - private def dispatch List computePathFromSuperTypeReflexive(TypeRef ref, Type rawSuperType, - Set processedTypes) { - if (ref.declaredType == rawSuperType) { - return newArrayList(ref); - } else { - for (superTypeRef : ref.declaredType.declaredSuperTypes) { - if (processedTypes.add(superTypeRef.declaredType)) { - val superPath = computePathFromSuperTypeReflexive(superTypeRef, rawSuperType, processedTypes) - if (superPath !== null) { - superPath.add(ref); - - // we can break here, although there might be several paths to the rawSuperType. However, - // all paths must result in the same parameterized version, so we only need one - return superPath; - } - } - } - } - return null; - } - - /* - * For intersection types, this returns the first path found. - */ - private def dispatch List computePathFromSuperTypeReflexive(IntersectionTypeExpression ref, - Type rawSuperType, Set processedTypes) { - for (typeRef : ref.typeRefs) { - val path = typeRef.computePathFromSuperTypeReflexive(rawSuperType, processedTypes) - if (path !== null) { - return path; - } - } - return null; - } - - private def void addOrIntersectTypes(RuleEnvironment G, List commonSuperTypes, - SuperTypesList allDeclaredSuperTypes) { - if (commonSuperTypes.empty) { - commonSuperTypes.addAll(allDeclaredSuperTypes) - } else { - - // extract all functions, they have to be handled differently: - // there must be only one FunctionTypeExprOrRef in the list: - val FunctionTypeExprOrRef currentSuperFunction = allDeclaredSuperTypes.filter(FunctionTypeExprOrRef).head(); - val FunctionTypeExprOrRef prevCommonSuperFunction = if (currentSuperFunction !== null) { - commonSuperTypes.filter(FunctionTypeExprOrRef).head() - } else { - null // do not search, would be removed anyway - } - - commonSuperTypes.retainAllTypeRefs(allDeclaredSuperTypes); - if (getTypeRefComparator.compare(prevCommonSuperFunction, currentSuperFunction) != 0) { // null or retained anyway - val commonSuperFunction = joinFunctionTypeRefs(G, currentSuperFunction, prevCommonSuperFunction) - if (commonSuperFunction !== null) { - commonSuperTypes.add(commonSuperFunction) - } - } - } - - } - - /** - * May return null if no join is possible, e.g., in f(string) and f(number) - */ - private def FunctionTypeExprOrRef joinFunctionTypeRefs(RuleEnvironment G, FunctionTypeExprOrRef f1, - FunctionTypeExprOrRef f2) { - val joinedFunctionTypeExpr = TypeRefsFactory.eINSTANCE.createFunctionTypeExpression; - - if (f1.returnTypeRef !== null && f2.returnTypeRef !== null) { - joinedFunctionTypeExpr.setReturnTypeRef( - TypeUtils.copyIfContained(tsh.join(G, f1.returnTypeRef, f2.returnTypeRef))); - } - joinedFunctionTypeExpr.returnValueMarkedOptional = f1.returnValueOptional || f2.returnValueOptional; - - val maxParSize = Math.max(f1.fpars.size, f2.fpars.size); - var i = 0; - var varOrOpt1 = false; - var varOrOpt2 = false; - while (i < maxParSize) { - val par1 = f1.getFParSmartAndFailSafe(i) - val par2 = f2.getFParSmartAndFailSafe(i) - - var TFormalParameter fpar = null; - if (par1 === null) { - fpar = TypeUtils.copy(par2) - } else if (par2 === null) { - fpar = TypeUtils.copy(par1) - } else { - if (par1.variadicOrOptional) { - varOrOpt1 = true; - } - if (par2.variadicOrOptional) { - varOrOpt2 = true; - } - - fpar = TypesFactory.eINSTANCE.createTFormalParameter(); - val meet = tsh.meet(G, par1.typeRef, par2.typeRef); - - if (meet === null) { - if (varOrOpt1 && varOrOpt2) { - return joinedFunctionTypeExpr; // cut optional or variadic non-matching arguments - } else { - return null; // no join of function possible - } - } - - val parType = TypeUtils.copyIfContained(meet); - fpar.setTypeRef(parType); - - if (par1.variadic && par2.variadic) { - fpar.setVariadic(true); - } else if (par1.variadicOrOptional && par2.variadicOrOptional) { - fpar.hasInitializerAssignment=true; - } - } - joinedFunctionTypeExpr.fpars.add(fpar); - i = i + 1; - } - return joinedFunctionTypeExpr - } - - private def TFormalParameter getFParSmartAndFailSafe(FunctionTypeExprOrRef f, int index) { - if (f.fpars.size == 0) { - return null; - } - if (index < f.fpars.size) { - return f.fpars.get(index); - } - val last = f.fpars.last; - if (last.variadic) { - return last; - } - return null; - } - - private def boolean containsUnboundTypeVariables(TypeArgument typeArg) { - if (typeArg instanceof ParameterizedTypeRef) { - val declType = typeArg.declaredType; - return declType instanceof TypeVariable // ok, that's simple - || (!typeArg.parameterized && declType.generic) // no type args, type variable is indirectly referenced from raw type - || typeArg.declaredTypeArgs.exists[containsUnboundTypeVariables(it)] // transitively - } - return false; - } -}