Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
package org.jetbrains.jet.lang.resolve;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.jet.lang.diagnostics.Errors.UNRESOLVED_REFERENCE;
|
||||
import static org.jetbrains.jet.lang.diagnostics.Errors.UNSUPPORTED;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class ImportsResolver {
|
||||
private final TopDownAnalysisContext context;
|
||||
|
||||
public ImportsResolver(@NotNull TopDownAnalysisContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void processTypeImports() {
|
||||
for (JetNamespace jetNamespace : context.getNamespaceDescriptors().keySet()) {
|
||||
processImports(jetNamespace, context.getNamespaceScopes().get(jetNamespace), context.getDeclaringScopes().get(jetNamespace));
|
||||
}
|
||||
}
|
||||
|
||||
private void processImports(@NotNull JetNamespace namespace, @NotNull WritableScope namespaceScope, @NotNull JetScope outerScope) {
|
||||
context.getConfiguration().addDefaultImports(context.getTrace(), namespaceScope);
|
||||
|
||||
List<JetImportDirective> importDirectives = namespace.getImportDirectives();
|
||||
for (JetImportDirective importDirective : importDirectives) {
|
||||
if (importDirective.isAbsoluteInRootNamespace()) {
|
||||
context.getTrace().report(UNSUPPORTED.on(importDirective, "TypeHierarchyResolver")); // TODO
|
||||
continue;
|
||||
}
|
||||
if (importDirective.isAllUnder()) {
|
||||
JetExpression importedReference = importDirective.getImportedReference();
|
||||
if (importedReference != null) {
|
||||
ExpressionTypingServices typeInferrerServices = context.getSemanticServices().getTypeInferrerServices(context.getTrace());
|
||||
JetType type = typeInferrerServices.getTypeWithNamespaces(namespaceScope, importedReference);
|
||||
if (type != null) {
|
||||
namespaceScope.importScope(type.getMemberScope());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ClassifierDescriptor classifierDescriptor = null;
|
||||
NamespaceDescriptor namespaceDescriptor = null;
|
||||
JetSimpleNameExpression referenceExpression = null;
|
||||
|
||||
JetExpression importedReference = importDirective.getImportedReference();
|
||||
if (importedReference instanceof JetDotQualifiedExpression) {
|
||||
JetDotQualifiedExpression reference = (JetDotQualifiedExpression) importedReference;
|
||||
JetType type = context.getSemanticServices().getTypeInferrerServices(context.getTrace()).getTypeWithNamespaces(namespaceScope, reference.getReceiverExpression());
|
||||
JetExpression selectorExpression = reference.getSelectorExpression();
|
||||
if (selectorExpression != null) {
|
||||
referenceExpression = (JetSimpleNameExpression) selectorExpression;
|
||||
String referencedName = referenceExpression.getReferencedName();
|
||||
if (type != null && referencedName != null) {
|
||||
classifierDescriptor = type.getMemberScope().getClassifier(referencedName);
|
||||
namespaceDescriptor = type.getMemberScope().getNamespace(referencedName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert importedReference instanceof JetSimpleNameExpression;
|
||||
referenceExpression = (JetSimpleNameExpression) importedReference;
|
||||
|
||||
String referencedName = referenceExpression.getReferencedName();
|
||||
if (referencedName != null) {
|
||||
classifierDescriptor = outerScope.getClassifier(referencedName);
|
||||
namespaceDescriptor = outerScope.getNamespace(referencedName);
|
||||
|
||||
if (classifierDescriptor == null && namespaceDescriptor == null) {
|
||||
context.getTrace().report(UNRESOLVED_REFERENCE.on(referenceExpression));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String aliasName = importDirective.getAliasName();
|
||||
if (aliasName == null) {
|
||||
aliasName = referenceExpression != null ? referenceExpression.getReferencedName() : null;
|
||||
}
|
||||
if (classifierDescriptor != null) {
|
||||
context.getTrace().record(BindingContext.REFERENCE_TARGET, referenceExpression, classifierDescriptor);
|
||||
|
||||
if (aliasName != null) {
|
||||
namespaceScope.importClassifierAlias(aliasName, classifierDescriptor);
|
||||
}
|
||||
}
|
||||
if (namespaceDescriptor != null) {
|
||||
if (classifierDescriptor == null) {
|
||||
context.getTrace().record(BindingContext.REFERENCE_TARGET, referenceExpression, namespaceDescriptor);
|
||||
}
|
||||
if (aliasName != null) {
|
||||
namespaceScope.importNamespaceAlias(aliasName, namespaceDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.jetbrains.jet.lang.resolve;
|
||||
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
/**
|
||||
* @author Stepan Koltsov
|
||||
*/
|
||||
public class StdlibNames {
|
||||
|
||||
public static final Type JET_PARAMETER_TYPE = Type.getObjectType("jet/typeinfo/JetParameter");
|
||||
public static final String JET_PARAMETER_CLASS = "jet.typeinfo.JetParameter";
|
||||
public static final String JET_PARAMETER_DESCRIPTOR = "Ljet/typeinfo/JetParameter;";
|
||||
|
||||
public static final String JET_PARAMETER_NAME_FIELD = "name";
|
||||
public static final String JET_PARAMETER_HAS_DEFAULT_FIELD = "hasDefault";
|
||||
public static final String JET_PARAMETER_NULLABLE_FIELD = "nullable";
|
||||
|
||||
|
||||
public static final Type JET_TYPE_PARAMETER_TYPE = Type.getObjectType("jet/typeinfo/JetTypeParameter");
|
||||
public static final String JET_TYPE_PARAMETER_CLASS = "jet.typeinfo.JetTypeParameter";
|
||||
public static final String JET_TYPE_PARAMETER_DESCRIPTOR = "Ljet/typeinfo/JetTypeParameter;";
|
||||
|
||||
public static final String JET_TYPE_PARAMETER_NAME_FIELD = "name";
|
||||
|
||||
|
||||
public static final Type JET_METHOD_TYPE = Type.getObjectType("jet/typeinfo/JetMethod");
|
||||
public static final String JET_METHOD_CLASS = "jet.typeinfo.JetMethod";
|
||||
public static final String JET_METHOD_DESCRIPTOR = "Ljet/typeinfo/JetMethod;";
|
||||
|
||||
public static final String JET_METHOD_NULLABLE_RETURN_TYPE_FIELD = "nullableReturnType";
|
||||
|
||||
}
|
||||
@@ -15,7 +15,10 @@ import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.WriteThroughScope;
|
||||
import org.jetbrains.jet.lang.types.*;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.TypeConstructor;
|
||||
import org.jetbrains.jet.lang.types.TypeProjection;
|
||||
import org.jetbrains.jet.lang.types.TypeUtils;
|
||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
|
||||
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
@@ -30,17 +33,19 @@ import static org.jetbrains.jet.lang.resolve.BindingContext.*;
|
||||
*/
|
||||
public class TypeHierarchyResolver {
|
||||
private final TopDownAnalysisContext context;
|
||||
private final ImportsResolver importsResolver;
|
||||
private LinkedList<MutableClassDescriptor> topologicalOrder;
|
||||
|
||||
|
||||
public TypeHierarchyResolver(TopDownAnalysisContext context) {
|
||||
this.context = context;
|
||||
this.importsResolver = new ImportsResolver(context);
|
||||
}
|
||||
|
||||
public void process(@NotNull JetScope outerScope, @NotNull NamespaceLike owner, @NotNull Collection<? extends JetDeclaration> declarations) {
|
||||
collectNamespacesAndClassifiers(outerScope, outerScope, owner, declarations); // namespaceScopes, classes
|
||||
|
||||
processTypeImports();
|
||||
importsResolver.processTypeImports();
|
||||
|
||||
createTypeConstructors(); // create type constructors for classes and generic parameters, supertypes are not filled in
|
||||
resolveTypesInClassHeaders(); // Generic bounds and types in supertype lists (no expressions or constructor resolution)
|
||||
@@ -75,7 +80,6 @@ public class TypeHierarchyResolver {
|
||||
|
||||
WriteThroughScope namespaceScope = new WriteThroughScope(outerScope, namespaceDescriptor.getMemberScope(), new TraceBasedRedeclarationHandler(context.getTrace()));
|
||||
namespaceScope.changeLockLevel(WritableScope.LockLevel.BOTH);
|
||||
context.getConfiguration().addDefaultImports(context.getTrace(), namespaceScope);
|
||||
context.getNamespaceScopes().put(namespace, namespaceScope);
|
||||
context.getDeclaringScopes().put(namespace, outerScope);
|
||||
|
||||
@@ -233,82 +237,6 @@ public class TypeHierarchyResolver {
|
||||
return ClassKind.CLASS;
|
||||
}
|
||||
|
||||
private void processTypeImports() {
|
||||
for (JetNamespace jetNamespace : context.getNamespaceDescriptors().keySet()) {
|
||||
processImports(jetNamespace, context.getNamespaceScopes().get(jetNamespace), context.getDeclaringScopes().get(jetNamespace));
|
||||
}
|
||||
}
|
||||
|
||||
private void processImports(@NotNull JetNamespace namespace, @NotNull WritableScope namespaceScope, @NotNull JetScope outerScope) {
|
||||
List<JetImportDirective> importDirectives = namespace.getImportDirectives();
|
||||
for (JetImportDirective importDirective : importDirectives) {
|
||||
if (importDirective.isAbsoluteInRootNamespace()) {
|
||||
context.getTrace().report(UNSUPPORTED.on(importDirective, "TypeHierarchyResolver")); // TODO
|
||||
continue;
|
||||
}
|
||||
if (importDirective.isAllUnder()) {
|
||||
JetExpression importedReference = importDirective.getImportedReference();
|
||||
if (importedReference != null) {
|
||||
ExpressionTypingServices typeInferrerServices = context.getSemanticServices().getTypeInferrerServices(context.getTrace());
|
||||
JetType type = typeInferrerServices.getTypeWithNamespaces(namespaceScope, importedReference);
|
||||
if (type != null) {
|
||||
namespaceScope.importScope(type.getMemberScope());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
ClassifierDescriptor classifierDescriptor = null;
|
||||
NamespaceDescriptor namespaceDescriptor = null;
|
||||
JetSimpleNameExpression referenceExpression = null;
|
||||
|
||||
JetExpression importedReference = importDirective.getImportedReference();
|
||||
if (importedReference instanceof JetDotQualifiedExpression) {
|
||||
JetDotQualifiedExpression reference = (JetDotQualifiedExpression) importedReference;
|
||||
JetType type = context.getSemanticServices().getTypeInferrerServices(context.getTrace()).getTypeWithNamespaces(namespaceScope, reference.getReceiverExpression());
|
||||
JetExpression selectorExpression = reference.getSelectorExpression();
|
||||
if (selectorExpression != null) {
|
||||
referenceExpression = (JetSimpleNameExpression) selectorExpression;
|
||||
String referencedName = referenceExpression.getReferencedName();
|
||||
if (type != null && referencedName != null) {
|
||||
classifierDescriptor = type.getMemberScope().getClassifier(referencedName);
|
||||
namespaceDescriptor = type.getMemberScope().getNamespace(referencedName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert importedReference instanceof JetSimpleNameExpression;
|
||||
referenceExpression = (JetSimpleNameExpression) importedReference;
|
||||
|
||||
String referencedName = referenceExpression.getReferencedName();
|
||||
if (referencedName != null) {
|
||||
classifierDescriptor = outerScope.getClassifier(referencedName);
|
||||
namespaceDescriptor = outerScope.getNamespace(referencedName);
|
||||
}
|
||||
}
|
||||
|
||||
String aliasName = importDirective.getAliasName();
|
||||
if (aliasName == null) {
|
||||
aliasName = referenceExpression != null ? referenceExpression.getReferencedName() : null;
|
||||
}
|
||||
if (classifierDescriptor != null) {
|
||||
context.getTrace().record(BindingContext.REFERENCE_TARGET, referenceExpression, classifierDescriptor);
|
||||
|
||||
if (aliasName != null) {
|
||||
namespaceScope.importClassifierAlias(aliasName, classifierDescriptor);
|
||||
}
|
||||
}
|
||||
if (namespaceDescriptor != null) {
|
||||
if (classifierDescriptor == null) {
|
||||
context.getTrace().record(BindingContext.REFERENCE_TARGET, referenceExpression, namespaceDescriptor);
|
||||
}
|
||||
if (aliasName != null) {
|
||||
namespaceScope.importNamespaceAlias(aliasName, namespaceDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createTypeConstructors() {
|
||||
for (Map.Entry<JetClass, MutableClassDescriptor> entry : context.getClasses().entrySet()) {
|
||||
JetClass jetClass = entry.getKey();
|
||||
|
||||
@@ -13,16 +13,13 @@ import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.*;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.*;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor;
|
||||
import org.jetbrains.jet.lang.types.*;
|
||||
import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices;
|
||||
import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystem;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemSolution;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.SolutionStatus;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
|
||||
import java.util.*;
|
||||
@@ -241,6 +238,12 @@ public class CallResolver {
|
||||
@NotNull JetType expectedType,
|
||||
@NotNull final List<ResolutionTask<D>> prioritizedTasks, // high to low priority
|
||||
@NotNull final JetReferenceExpression reference) {
|
||||
|
||||
ResolutionDebugInfo.Data debugInfo = ResolutionDebugInfo.create();
|
||||
trace.record(ResolutionDebugInfo.RESOLUTION_DEBUG_INFO, call.getCallElement(), debugInfo);
|
||||
|
||||
debugInfo.set(ResolutionDebugInfo.TASKS, prioritizedTasks);
|
||||
|
||||
TracingStrategy tracing = new TracingStrategy() {
|
||||
@Override
|
||||
public <D extends CallableDescriptor> void bindReference(@NotNull BindingTrace trace, @NotNull ResolvedCallImpl<D> resolvedCall) {
|
||||
@@ -371,6 +374,9 @@ public class CallResolver {
|
||||
OverloadResolutionResults<D> results = performResolution(temporaryTrace, scope, expectedType, task, tracing);
|
||||
if (results.isSuccess()) {
|
||||
temporaryTrace.commit();
|
||||
|
||||
debugInfo.set(ResolutionDebugInfo.RESULT, results.getResult());
|
||||
|
||||
return results.getResult();
|
||||
}
|
||||
if (traceForFirstNonemptyCandidateSet == null && !task.getCandidates().isEmpty()) {
|
||||
@@ -381,6 +387,9 @@ public class CallResolver {
|
||||
if (traceForFirstNonemptyCandidateSet != null) {
|
||||
traceForFirstNonemptyCandidateSet.commit();
|
||||
if (resultsForFirstNonemptyCandidateSet.singleDescriptor()) {
|
||||
|
||||
debugInfo.set(ResolutionDebugInfo.RESULT, resultsForFirstNonemptyCandidateSet.getResult());
|
||||
|
||||
return resultsForFirstNonemptyCandidateSet.getResult();
|
||||
}
|
||||
}
|
||||
@@ -420,7 +429,10 @@ public class CallResolver {
|
||||
if (!candidate.getTypeParameters().isEmpty()) {
|
||||
// Type argument inference
|
||||
|
||||
ConstraintSystem constraintSystem = new ConstraintSystemImpl();
|
||||
ResolutionDebugInfo.Data debugInfo = trace.get(ResolutionDebugInfo.RESOLUTION_DEBUG_INFO, task.getCall().getCallElement());
|
||||
|
||||
ConstraintSystem constraintSystem = new ConstraintSystemImpl(new DebugConstraintResolutionListener(debugInfo));
|
||||
|
||||
for (TypeParameterDescriptor typeParameterDescriptor : candidate.getTypeParameters()) {
|
||||
constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package org.jetbrains.jet.lang.resolve.calls;
|
||||
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemImpl;
|
||||
import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemSolution;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.util.slicedmap.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class ResolutionDebugInfo {
|
||||
public static final WritableSlice<One, List<? extends ResolutionTask<? extends CallableDescriptor>>> TASKS = Slices.createSimpleSlice();
|
||||
public static final WritableSlice<One, ResolvedCall<? extends CallableDescriptor>> RESULT = Slices.createSimpleSlice();
|
||||
public static final WritableSlice<One, StringBuilder> ERRORS = Slices.createSimpleSlice();
|
||||
public static final WritableSlice<One, StringBuilder> LOG = Slices.createSimpleSlice();
|
||||
public static final WritableSlice<One, Map<TypeParameterDescriptor, ConstraintSystemImpl.TypeValue>> BOUNDS_FOR_UNKNOWNS = Slices.createSimpleSlice();
|
||||
public static final WritableSlice<One, Map<JetType, ConstraintSystemImpl.TypeValue>> BOUNDS_FOR_KNOWNS = Slices.createSimpleSlice();
|
||||
public static final WritableSlice<One, ConstraintSystemSolution> SOLUTION = Slices.createSimpleSlice();
|
||||
public static final WritableSlice<One, Collection<TypeParameterDescriptor>> UNKNOWNS = Slices.createSimpleSlice();
|
||||
|
||||
static {
|
||||
BasicWritableSlice.initSliceDebugNames(ResolutionDebugInfo.class);
|
||||
}
|
||||
|
||||
public static boolean RESOLUTION_DEBUG_INFO_ENABLED = false;
|
||||
|
||||
public static boolean isResolutionDebugEnabled() {
|
||||
Application application = ApplicationManager.getApplication();
|
||||
return (RESOLUTION_DEBUG_INFO_ENABLED || application.isInternal()) && !application.isUnitTestMode();
|
||||
}
|
||||
|
||||
public static final Data NO_DEBUG_INFO = new AbstractData() {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NO_DEBUG_INFO";
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> V getByKey(ReadOnlySlice<K, V> slice, K key) {
|
||||
return SlicedMap.DO_NOTHING.get(slice, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> void putByKey(WritableSlice<K, V> slice, K key, V value) {
|
||||
}
|
||||
};
|
||||
public static final WritableSlice<PsiElement, Data> RESOLUTION_DEBUG_INFO = new BasicWritableSlice<PsiElement, Data>(Slices.ONLY_REWRITE_TO_EQUAL) {
|
||||
@Override
|
||||
public boolean check(PsiElement key, Data value) {
|
||||
return isResolutionDebugEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Data computeValue(SlicedMap map, PsiElement key, Data value, boolean valueNotFound) {
|
||||
if (valueNotFound) return NO_DEBUG_INFO;
|
||||
return super.computeValue(map, key, value, valueNotFound);
|
||||
}
|
||||
};
|
||||
|
||||
public static Data create() {
|
||||
return isResolutionDebugEnabled() ? new DataImpl() : NO_DEBUG_INFO;
|
||||
}
|
||||
|
||||
public enum One { KEY }
|
||||
|
||||
public interface Data {
|
||||
<K, V> V getByKey(ReadOnlySlice<K, V> slice, K key);
|
||||
<K, V> void putByKey(WritableSlice<K, V> slice, K key, V value);
|
||||
|
||||
<V> void set(WritableSlice<One, ? super V> slice, V value);
|
||||
<V> V get(ReadOnlySlice<One, V> slice);
|
||||
}
|
||||
|
||||
private static abstract class AbstractData implements Data {
|
||||
@Override
|
||||
public <V> void set(WritableSlice<One, ? super V> slice, V value) {
|
||||
putByKey(slice, One.KEY, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V> V get(ReadOnlySlice<One, V> slice) {
|
||||
return getByKey(slice, One.KEY);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DataImpl extends AbstractData {
|
||||
private final MutableSlicedMap map = SlicedMapImpl.create();
|
||||
|
||||
@Override
|
||||
public <K, V> V getByKey(ReadOnlySlice<K, V> slice, K key) {
|
||||
return map.get(slice, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> void putByKey(WritableSlice<K, V> slice, K key, V value) {
|
||||
map.put(slice, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void println(Object message) {
|
||||
if (isResolutionDebugEnabled()) {
|
||||
System.out.println(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package org.jetbrains.jet.lang.resolve.calls.inference;
|
||||
|
||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public interface ConstraintResolutionListener {
|
||||
|
||||
void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, ConstraintSystemImpl.TypeValue typeValue);
|
||||
void constraintsForKnownType(JetType type, ConstraintSystemImpl.TypeValue typeValue);
|
||||
void done(ConstraintSystemSolution solution, Set<TypeParameterDescriptor> typeParameterDescriptors);
|
||||
|
||||
void log(Object message);
|
||||
void error(Object message);
|
||||
}
|
||||
+71
-85
@@ -44,7 +44,6 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static class LoopInTypeVariableConstraintsException extends RuntimeException {
|
||||
public LoopInTypeVariableConstraintsException() {}
|
||||
}
|
||||
@@ -80,6 +79,8 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
public abstract boolean equate(TypeValue other);
|
||||
}
|
||||
|
||||
//==========================================================================================================================================================
|
||||
|
||||
private class UnknownType extends TypeValue {
|
||||
|
||||
private final TypeParameterDescriptor typeParameterDescriptor;
|
||||
@@ -142,7 +143,7 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
}
|
||||
}
|
||||
|
||||
println("minimal solution from lowerbounds for " + this + " is " + commonSupertype);
|
||||
listener.log("minimal solution from lowerbounds for " + this + " is " + commonSupertype);
|
||||
value = new KnownType(commonSupertype);
|
||||
}
|
||||
else {
|
||||
@@ -238,16 +239,18 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
|
||||
private static final class TypeConstraintBuilderAdapter implements TypingConstraints {
|
||||
private final TypingConstraints delegate;
|
||||
private final ConstraintResolutionListener listener;
|
||||
|
||||
private TypeConstraintBuilderAdapter(TypingConstraints delegate) {
|
||||
private TypeConstraintBuilderAdapter(TypingConstraints delegate, ConstraintResolutionListener listener) {
|
||||
this.delegate = delegate;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, TypeCheckingProcedure typeCheckingProcedure) {
|
||||
boolean result = delegate.assertEqualTypes(a, b, typeCheckingProcedure);
|
||||
if (!result) {
|
||||
println("-- Failed to equate " + a + " and " + b);
|
||||
listener.error("-- Failed to equate " + a + " and " + b);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -256,7 +259,7 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
public boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
|
||||
boolean result = delegate.assertEqualTypeConstructors(a, b);
|
||||
if (!result) {
|
||||
println("-- Type constructors are not equal: " + a + " and " + b);
|
||||
listener.error("-- Type constructors are not equal: " + a + " and " + b);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -265,7 +268,7 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, TypeCheckingProcedure typeCheckingProcedure) {
|
||||
boolean result = delegate.assertSubtype(subtype, supertype, typeCheckingProcedure);
|
||||
if (!result) {
|
||||
println("-- " + subtype + " can't be a subtype of " + supertype);
|
||||
listener.error("-- " + subtype + " can't be a subtype of " + supertype);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -274,60 +277,67 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
public boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
|
||||
boolean result = delegate.noCorrespondingSupertype(subtype, supertype);
|
||||
if (!result) {
|
||||
println("-- " + subtype + " has no supertype corresponding to " + supertype);
|
||||
listener.error("-- " + subtype + " has no supertype corresponding to " + supertype);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private final TypeCheckingProcedure constraintExpander = new TypeCheckingProcedure(new TypeConstraintBuilderAdapter(new TypingConstraints() {
|
||||
@Override
|
||||
public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, TypeCheckingProcedure typeCheckingProcedure) {
|
||||
TypeValue aValue = getTypeValueFor(a);
|
||||
TypeValue bValue = getTypeValueFor(b);
|
||||
|
||||
return aValue.equate(bValue);
|
||||
}
|
||||
private final TypeCheckingProcedure constraintExpander;
|
||||
private final ConstraintResolutionListener listener;
|
||||
|
||||
@Override
|
||||
public boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
|
||||
return a.equals(b)
|
||||
|| unknownTypes.containsKey(a.getDeclarationDescriptor())
|
||||
|| unknownTypes.containsKey(b.getDeclarationDescriptor());
|
||||
}
|
||||
public ConstraintSystemImpl(ConstraintResolutionListener listener) {
|
||||
this.listener = listener;
|
||||
this.constraintExpander = createConstraintExpander();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, TypeCheckingProcedure typeCheckingProcedure) {
|
||||
TypeValue subtypeValue = getTypeValueFor(subtype);
|
||||
TypeValue supertypeValue = getTypeValueFor(supertype);
|
||||
private TypeCheckingProcedure createConstraintExpander() {
|
||||
return new TypeCheckingProcedure(new TypeConstraintBuilderAdapter(new TypingConstraints() {
|
||||
@Override
|
||||
public boolean assertEqualTypes(@NotNull JetType a, @NotNull JetType b, TypeCheckingProcedure typeCheckingProcedure) {
|
||||
TypeValue aValue = getTypeValueFor(a);
|
||||
TypeValue bValue = getTypeValueFor(b);
|
||||
|
||||
if (someUnknown(subtypeValue, supertypeValue)) {
|
||||
addSubtypingConstraintOnTypeValues(subtypeValue, supertypeValue);
|
||||
return aValue.equate(bValue);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
|
||||
// If some of the types is an unknown, the constraint must be generated, and we should carry on
|
||||
// otherwise there can be no solution, and we should fail
|
||||
TypeValue subTypeValue = getTypeValueFor(subtype);
|
||||
TypeValue superTypeValue = getTypeValueFor(supertype);
|
||||
boolean someUnknown = someUnknown(subTypeValue, superTypeValue);
|
||||
if (someUnknown) {
|
||||
addSubtypingConstraintOnTypeValues(subTypeValue, superTypeValue);
|
||||
@Override
|
||||
public boolean assertEqualTypeConstructors(@NotNull TypeConstructor a, @NotNull TypeConstructor b) {
|
||||
return a.equals(b)
|
||||
|| unknownTypes.containsKey(a.getDeclarationDescriptor())
|
||||
|| unknownTypes.containsKey(b.getDeclarationDescriptor());
|
||||
}
|
||||
return someUnknown;
|
||||
}
|
||||
|
||||
private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) {
|
||||
return subtypeValue instanceof UnknownType || supertypeValue instanceof UnknownType;
|
||||
}
|
||||
@Override
|
||||
public boolean assertSubtype(@NotNull JetType subtype, @NotNull JetType supertype, TypeCheckingProcedure typeCheckingProcedure) {
|
||||
TypeValue subtypeValue = getTypeValueFor(subtype);
|
||||
TypeValue supertypeValue = getTypeValueFor(supertype);
|
||||
|
||||
}))
|
||||
;
|
||||
if (someUnknown(subtypeValue, supertypeValue)) {
|
||||
addSubtypingConstraintOnTypeValues(subtypeValue, supertypeValue);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public ConstraintSystemImpl() {}
|
||||
@Override
|
||||
public boolean noCorrespondingSupertype(@NotNull JetType subtype, @NotNull JetType supertype) {
|
||||
// If some of the types is an unknown, the constraint must be generated, and we should carry on
|
||||
// otherwise there can be no solution, and we should fail
|
||||
TypeValue subTypeValue = getTypeValueFor(subtype);
|
||||
TypeValue superTypeValue = getTypeValueFor(supertype);
|
||||
boolean someUnknown = someUnknown(subTypeValue, superTypeValue);
|
||||
if (someUnknown) {
|
||||
addSubtypingConstraintOnTypeValues(subTypeValue, superTypeValue);
|
||||
}
|
||||
return someUnknown;
|
||||
}
|
||||
|
||||
private boolean someUnknown(TypeValue subtypeValue, TypeValue supertypeValue) {
|
||||
return subtypeValue instanceof UnknownType || supertypeValue instanceof UnknownType;
|
||||
}
|
||||
|
||||
}, listener));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private TypeValue getTypeValueFor(@NotNull JetType type) {
|
||||
@@ -368,7 +378,7 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
}
|
||||
|
||||
private void mergeUnknowns(@NotNull UnknownType a, @NotNull UnknownType b) {
|
||||
System.err.println("!!!mergeUnknowns() is not implemented!!!");
|
||||
listener.error("!!!mergeUnknowns() is not implemented!!!");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -379,7 +389,7 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
}
|
||||
|
||||
private void addSubtypingConstraintOnTypeValues(TypeValue typeValueForLower, TypeValue typeValueForUpper) {
|
||||
println(typeValueForLower + " :< " + typeValueForUpper);
|
||||
listener.log("Constraint added: " + typeValueForLower + " :< " + typeValueForUpper);
|
||||
if (typeValueForLower != typeValueForUpper) {
|
||||
typeValueForLower.addUpperBound(typeValueForUpper);
|
||||
typeValueForUpper.addLowerBound(typeValueForLower);
|
||||
@@ -424,13 +434,11 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
}
|
||||
|
||||
for (UnknownType unknownType : unknownTypes.values()) {
|
||||
println("Constraints for " + unknownType.getTypeParameterDescriptor());
|
||||
printTypeValue(unknownType);
|
||||
listener.constraintsForUnknown(unknownType.getTypeParameterDescriptor(), unknownType);
|
||||
}
|
||||
|
||||
for (KnownType knownType : knownTypes.values()) {
|
||||
println("Constraints for " + knownType.getType());
|
||||
printTypeValue(knownType);
|
||||
listener.constraintsForKnownType(knownType.getType(), knownType);
|
||||
}
|
||||
|
||||
// Find inconsistencies
|
||||
@@ -447,22 +455,11 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
// we have set some of them from equality constraints with known types
|
||||
// and thus the bounds may be violated if some of the constraints conflict
|
||||
|
||||
println("====================================");
|
||||
println("");
|
||||
println("");
|
||||
listener.done(solution, unknownTypes.keySet());
|
||||
|
||||
return solution;
|
||||
}
|
||||
|
||||
private void printTypeValue(TypeValue typeValue) {
|
||||
for (TypeValue bound : typeValue.getUpperBounds()) {
|
||||
println(" :< " + bound);
|
||||
}
|
||||
for (TypeValue bound : typeValue.getLowerBounds()) {
|
||||
println(" :> " + bound);
|
||||
}
|
||||
}
|
||||
|
||||
private void check(TypeValue typeValue, Solution solution) {
|
||||
try {
|
||||
KnownType resultingValue = typeValue.getValue();
|
||||
@@ -471,33 +468,20 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
JetType boundingType = solution.getSubstitutor().substitute(upperBound.getValue().getType(), Variance.INVARIANT);
|
||||
if (!typeChecker.isSubtypeOf(type, boundingType)) { // TODO
|
||||
solution.registerError("Constraint violation: " + type + " is not a subtype of " + boundingType);
|
||||
println("Constraint violation: " + type + " :< " + boundingType);
|
||||
listener.error("Constraint violation: " + type + " :< " + boundingType);
|
||||
}
|
||||
}
|
||||
for (TypeValue lowerBound : typeValue.getLowerBounds()) {
|
||||
JetType boundingType = solution.getSubstitutor().substitute(lowerBound.getValue().getType(), Variance.INVARIANT);
|
||||
if (!typeChecker.isSubtypeOf(boundingType, type)) {
|
||||
solution.registerError("Constraint violation: " + boundingType + " is not a subtype of " + type);
|
||||
println("Constraint violation: " + boundingType + " :< " + type);
|
||||
listener.error("Constraint violation: " + boundingType + " :< " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (LoopInTypeVariableConstraintsException e) {
|
||||
println("-------------------------------------------------------------------");
|
||||
for (Map.Entry<TypeParameterDescriptor, UnknownType> entry : unknownTypes.entrySet()) {
|
||||
println("Unknown: " + entry.getKey());
|
||||
UnknownType unknownType = entry.getValue();
|
||||
println("Lower bounds: ");
|
||||
for (TypeValue lowerBound : unknownType.getLowerBounds()) {
|
||||
println(" " + lowerBound);
|
||||
}
|
||||
println("Upper bounds: ");
|
||||
for (TypeValue lowerBound : unknownType.getUpperBounds()) {
|
||||
println(" " + lowerBound);
|
||||
}
|
||||
}
|
||||
listener.error("Loop detected");
|
||||
solution.registerError("[TODO] Loop in constraints");
|
||||
// e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,9 +528,14 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
DeclarationDescriptor declarationDescriptor = key.getDeclarationDescriptor();
|
||||
if (declarationDescriptor instanceof TypeParameterDescriptor) {
|
||||
TypeParameterDescriptor descriptor = (TypeParameterDescriptor) declarationDescriptor;
|
||||
|
||||
if (!unknownTypes.containsKey(descriptor)) return null;
|
||||
println(descriptor + " |-> " + getValue(descriptor));
|
||||
return new TypeProjection(getValue(descriptor));
|
||||
|
||||
TypeProjection typeProjection = new TypeProjection(getValue(descriptor));
|
||||
|
||||
listener.log(descriptor + " |-> " + typeProjection);
|
||||
|
||||
return typeProjection;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -588,7 +577,4 @@ public class ConstraintSystemImpl implements ConstraintSystem {
|
||||
|
||||
}
|
||||
|
||||
private static void println(String message) {
|
||||
// System.out.println(message);
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package org.jetbrains.jet.lang.resolve.calls.inference;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.resolve.calls.ResolutionDebugInfo;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.jetbrains.jet.lang.resolve.calls.ResolutionDebugInfo.*;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class DebugConstraintResolutionListener implements ConstraintResolutionListener {
|
||||
|
||||
private final ResolutionDebugInfo.Data debugInfo;
|
||||
|
||||
public DebugConstraintResolutionListener(@NotNull ResolutionDebugInfo.Data debugInfo) {
|
||||
this.debugInfo = debugInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, ConstraintSystemImpl.TypeValue typeValue) {
|
||||
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
||||
Map<TypeParameterDescriptor, ConstraintSystemImpl.TypeValue> map = debugInfo.get(BOUNDS_FOR_UNKNOWNS);
|
||||
if (map == null) {
|
||||
map = Maps.newLinkedHashMap();
|
||||
debugInfo.set(BOUNDS_FOR_UNKNOWNS, map);
|
||||
}
|
||||
map.put(typeParameterDescriptor, typeValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void constraintsForKnownType(JetType type, ConstraintSystemImpl.TypeValue typeValue) {
|
||||
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
||||
Map<JetType,ConstraintSystemImpl.TypeValue> map = debugInfo.get(BOUNDS_FOR_KNOWNS);
|
||||
if (map == null) {
|
||||
map = Maps.newLinkedHashMap();
|
||||
debugInfo.set(BOUNDS_FOR_KNOWNS, map);
|
||||
}
|
||||
map.put(type, typeValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void done(ConstraintSystemSolution solution, Set<TypeParameterDescriptor> typeParameterDescriptors) {
|
||||
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
||||
debugInfo.set(SOLUTION, solution);
|
||||
debugInfo.set(UNKNOWNS, typeParameterDescriptors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Object message) {
|
||||
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
||||
StringBuilder stringBuilder = debugInfo.get(LOG);
|
||||
if (stringBuilder == null) {
|
||||
stringBuilder = new StringBuilder();
|
||||
debugInfo.set(LOG, stringBuilder);
|
||||
}
|
||||
stringBuilder.append(message).append("\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Object message) {
|
||||
if (!ResolutionDebugInfo.isResolutionDebugEnabled()) return;
|
||||
StringBuilder stringBuilder = debugInfo.get(ERRORS);
|
||||
if (stringBuilder == null) {
|
||||
stringBuilder = new StringBuilder();
|
||||
debugInfo.set(ERRORS, stringBuilder);
|
||||
}
|
||||
stringBuilder.append(message).append("\n");
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package org.jetbrains.jet.lang.resolve.calls.inference;
|
||||
|
||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
||||
import org.jetbrains.jet.lang.resolve.calls.ResolutionDebugInfo;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public class PrintingConstraintResolutionListener implements ConstraintResolutionListener {
|
||||
|
||||
@Override
|
||||
public void constraintsForUnknown(TypeParameterDescriptor typeParameterDescriptor, ConstraintSystemImpl.TypeValue typeValue) {
|
||||
println("Constraints for " + typeParameterDescriptor);
|
||||
printTypeValue(typeValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void constraintsForKnownType(JetType type, ConstraintSystemImpl.TypeValue typeValue) {
|
||||
println("Constraints for " + type);
|
||||
printTypeValue(typeValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void done(ConstraintSystemSolution solution, Set<TypeParameterDescriptor> typeParameterDescriptors) {
|
||||
println("==================================================");
|
||||
println("");
|
||||
println("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Object message) {
|
||||
println(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Object message) {
|
||||
println(message);
|
||||
}
|
||||
|
||||
private void printTypeValue(ConstraintSystemImpl.TypeValue typeValue) {
|
||||
for (ConstraintSystemImpl.TypeValue bound : typeValue.getUpperBounds()) {
|
||||
println(" :< " + bound);
|
||||
}
|
||||
for (ConstraintSystemImpl.TypeValue bound : typeValue.getLowerBounds()) {
|
||||
println(" :> " + bound);
|
||||
}
|
||||
}
|
||||
|
||||
private static void println(Object message) {
|
||||
ResolutionDebugInfo.println(message);
|
||||
}
|
||||
}
|
||||
@@ -199,6 +199,10 @@ public class WriteThroughScope extends WritableScopeWithImports {
|
||||
allDescriptors = Lists.newArrayList();
|
||||
allDescriptors.addAll(writableWorker.getAllDescriptors());
|
||||
allDescriptors.addAll(getWorkerScope().getAllDescriptors());
|
||||
|
||||
for (JetScope imported : getImports()) {
|
||||
allDescriptors.addAll(imported.getAllDescriptors());
|
||||
}
|
||||
}
|
||||
return allDescriptors;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,37 @@
|
||||
package org.jetbrains.jet.util.slicedmap;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author abreslav
|
||||
*/
|
||||
public interface SlicedMap extends Iterable<Map.Entry<SlicedMapKey<?, ?>, ?>> {
|
||||
|
||||
SlicedMap DO_NOTHING = new SlicedMap() {
|
||||
@Override
|
||||
public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
|
||||
return slice.computeValue(this, key, null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> boolean containsKey(ReadOnlySlice<K, V> slice, K key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<SlicedMapKey<?, ?>, ?>> iterator() {
|
||||
return Collections.<Map.Entry<SlicedMapKey<?, ?>, ?>>emptySet().iterator();
|
||||
}
|
||||
};
|
||||
|
||||
<K, V> V get(ReadOnlySlice<K, V> slice, K key);
|
||||
|
||||
<K, V> boolean containsKey(ReadOnlySlice<K, V> slice, K key);
|
||||
|
||||
Reference in New Issue
Block a user