001/* 002 * Copyright 2010-2013 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.jetbrains.jet.lang.types; 018 019import com.google.common.base.Function; 020import com.google.common.collect.Collections2; 021import com.google.common.collect.Maps; 022import org.jetbrains.annotations.NotNull; 023import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor; 024import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; 025import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; 026import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl; 027import org.jetbrains.jet.utils.DFS; 028 029import java.util.Collections; 030import java.util.List; 031import java.util.Map; 032 033public class DescriptorSubstitutor { 034 035 private static final Function<TypeProjection,JetType> PROJECTIONS_TO_TYPES = new Function<TypeProjection, JetType>() { 036 @Override 037 public JetType apply(TypeProjection projection) { 038 return projection.getType(); 039 } 040 }; 041 042 @NotNull 043 public static TypeSubstitutor substituteTypeParameters( 044 @NotNull List<TypeParameterDescriptor> typeParameters, 045 @NotNull final TypeSubstitutor originalSubstitutor, 046 @NotNull DeclarationDescriptor newContainingDeclaration, 047 @NotNull List<TypeParameterDescriptor> result) { 048 final Map<TypeConstructor, TypeProjection> mutableSubstitution = Maps.newHashMap(); 049 TypeSubstitutor substitutor = TypeSubstitutor.create(new TypeSubstitution() { 050 051 @Override 052 public TypeProjection get(TypeConstructor key) { 053 if (originalSubstitutor.inRange(key)) { 054 return originalSubstitutor.getSubstitution().get(key); 055 } 056 return mutableSubstitution.get(key); 057 } 058 059 @Override 060 public boolean isEmpty() { 061 return originalSubstitutor.isEmpty() && mutableSubstitution.isEmpty(); 062 } 063 064 @Override 065 public String toString() { 066 return "DescriptorSubstitutor.substituteTypeParameters(" + mutableSubstitution + " / " + originalSubstitutor.getSubstitution() + ")"; 067 } 068 }); 069 Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> substitutedMap = Maps.newHashMap(); 070 for (TypeParameterDescriptor descriptor : typeParameters) { 071 TypeParameterDescriptorImpl substituted = TypeParameterDescriptorImpl.createForFurtherModification( 072 newContainingDeclaration, 073 descriptor.getAnnotations(), 074 descriptor.isReified(), 075 descriptor.getVariance(), 076 descriptor.getName(), 077 descriptor.getIndex()); 078 substituted.setInitialized(); 079 080 mutableSubstitution.put(descriptor.getTypeConstructor(), new TypeProjection(substituted.getDefaultType())); 081 082 substitutedMap.put(descriptor, substituted); 083 result.add(substituted); 084 } 085 086 for (TypeParameterDescriptor descriptor : typeParameters) { 087 TypeParameterDescriptorImpl substituted = substitutedMap.get(descriptor); 088 for (JetType upperBound : descriptor.getUpperBounds()) { 089 substituted.getUpperBounds().add(substitutor.substitute(upperBound, Variance.INVARIANT)); 090 } 091 } 092 093 return substitutor; 094 } 095 096 097 098 @NotNull 099 public static TypeSubstitutor createUpperBoundsSubstitutor( 100 @NotNull List<TypeParameterDescriptor> typeParameters 101 ) { 102 Map<TypeConstructor, TypeProjection> mutableSubstitution = Maps.newHashMap(); 103 TypeSubstitutor substitutor = TypeSubstitutor.create(mutableSubstitution); 104 105 // todo assert: no loops 106 for (TypeParameterDescriptor descriptor : topologicallySortTypeParameters(typeParameters)) { 107 JetType upperBoundsAsType = descriptor.getUpperBoundsAsType(); 108 JetType substitutedUpperBoundsAsType = substitutor.substitute(upperBoundsAsType, Variance.INVARIANT); 109 mutableSubstitution.put(descriptor.getTypeConstructor(), new TypeProjection(substitutedUpperBoundsAsType)); 110 } 111 112 return substitutor; 113 } 114 115 private static List<TypeParameterDescriptor> topologicallySortTypeParameters(final List<TypeParameterDescriptor> typeParameters) { 116 // In the end, we want every parameter to have no references to those after it in the list 117 // This gives us the reversed order: the one that refers to everybody else comes first 118 List<TypeParameterDescriptor> topOrder = DFS.topologicalOrder( 119 typeParameters, 120 new DFS.Neighbors<TypeParameterDescriptor>() { 121 @NotNull 122 @Override 123 public Iterable<TypeParameterDescriptor> getNeighbors(TypeParameterDescriptor current) { 124 return getTypeParametersFromUpperBounds(current, typeParameters); 125 } 126 }); 127 128 assert topOrder.size() == typeParameters.size() : "All type parameters must be visited, but only " + topOrder + " were"; 129 130 // Now, the one that refers to everybody else stands in the last position 131 Collections.reverse(topOrder); 132 return topOrder; 133 } 134 135 private static List<TypeParameterDescriptor> getTypeParametersFromUpperBounds( 136 TypeParameterDescriptor current, 137 final List<TypeParameterDescriptor> typeParameters 138 ) { 139 return DFS.dfs( 140 current.getUpperBounds(), 141 new DFS.Neighbors<JetType>() { 142 @NotNull 143 @Override 144 public Iterable<JetType> getNeighbors(JetType current) { 145 return Collections2.transform(current.getArguments(), PROJECTIONS_TO_TYPES); 146 } 147 }, 148 new DFS.NodeHandlerWithListResult<JetType, TypeParameterDescriptor>() { 149 @Override 150 public void beforeChildren(JetType current) { 151 ClassifierDescriptor declarationDescriptor = current.getConstructor().getDeclarationDescriptor(); 152 // typeParameters in a list, but it contains very few elements, so it's fine to call contains() on it 153 //noinspection SuspiciousMethodCalls 154 if (typeParameters.contains(declarationDescriptor)) { 155 result.add((TypeParameterDescriptor) declarationDescriptor); 156 } 157 } 158 } 159 ); 160 } 161}