/*
* Copyright 2003-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.newTypesystem;
import jetbrains.mps.lang.pattern.IMatchingPattern;
import jetbrains.mps.lang.typesystem.runtime.ComparisonRule_Runtime;
import jetbrains.mps.lang.typesystem.runtime.InequationReplacementRule_Runtime;
import jetbrains.mps.lang.typesystem.runtime.IsApplicable2Status;
import jetbrains.mps.lang.typesystem.runtime.IsApplicableStatus;
import jetbrains.mps.lang.typesystem.runtime.SubtypingRule_Runtime;
import jetbrains.mps.languageScope.LanguageScopeExecutor;
import jetbrains.mps.smodel.ModelDependencyScanner;
import jetbrains.mps.typesystem.TypeSystemReporter;
import jetbrains.mps.typesystem.inference.EquationInfo;
import jetbrains.mps.typesystem.inference.SubtypingManager;
import jetbrains.mps.typesystem.inference.TypeChecker;
import jetbrains.mps.typesystem.inference.TypeCheckingContext;
import jetbrains.mps.typesystem.inference.util.StructuralNodeSet;
import jetbrains.mps.util.Computable;
import jetbrains.mps.util.Pair;
import org.jetbrains.mps.openapi.language.SLanguage;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SNodeUtil;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class SubTypingManagerNew extends SubtypingManager {
private final CoercionManager myCoercionManager;
public SubTypingManagerNew(TypeChecker typeChecker) {
super(typeChecker);
myCoercionManager = new CoercionManager(typeChecker, this);
}
@Override
public boolean isSubtype(SNode subType, SNode superType) {
return isSubtype(subType, superType, true);
}
@Override
public boolean isSubtype(final SNode subType, final SNode superType, final boolean isWeak) {
if (null == subType || null == superType) return false;
if (subType == superType) return true;
if (TypesUtil.isVariable(subType)) return false;
if (TypesUtil.isVariable(superType)) return false;
return LanguageScopeExecutor.execWithMultiLanguageScope(
collectLanguagesRecursively(subType, superType),
new Computable<Boolean>() {
@Override
public Boolean compute() {
SubtypingResolver subtypingResolver = new SubtypingResolver(isWeak);
return subtypingResolver.calcIsSubType(subType, superType);
}
});
}
@Override
public boolean isSubTypeByReplacementRules(final SNode subType, final SNode superType, final boolean isWeak) {
return isSubTypeByReplacementRulesAuth(subType, superType, isWeak).o1;
}
/**
* Affirmative: true iff is subtype.
* Authoritative: true iff can answer yes or no.
* Affirmative implies Authoritative.
* @return a pair of booleans: affirmative and authoritative
*/
@Override
public Pair<Boolean, Boolean> isSubTypeByReplacementRulesAuth(final SNode subType, final SNode superType, final boolean isWeak) {
return LanguageScopeExecutor.execWithMultiLanguageScope(
collectLanguagesRecursively(subType, superType),
// two booleans: affirmative, authoritative
new Computable<Pair<Boolean, Boolean>>() {
@Override
public Pair<Boolean, Boolean> compute() {
for (Pair<InequationReplacementRule_Runtime, IsApplicable2Status> pair : myTypeChecker.getRulesManager().getReplacementRules(subType, superType)) {
InequationReplacementRule_Runtime rule = pair.o1;
IsApplicable2Status status = pair.o2;
boolean affirmative = rule.checkInequation(subType, superType, new EquationInfo(null, null), status, isWeak);
return new Pair<Boolean, Boolean>(affirmative, true);
}
return new Pair<Boolean, Boolean>(false, false);
}
});
}
@Override
public boolean isSuperType(SNode superType, Set<SNode> possibleSubTypes) {
for (SNode subType : possibleSubTypes) {
if (isSubtype(subType, superType, true)) {
return true;
}
}
return false;
}
@Override
public StructuralNodeSet<?> collectImmediateSupertypes(SNode term) {
return collectImmediateSupertypes(term, true);
}
@Override
public StructuralNodeSet collectImmediateSupertypes(SNode term, boolean isWeak) {
StructuralNodeSet result = new StructuralNodeSet();
collectImmediateSuperTypes(term, isWeak, result, null);
return result;
}
@Override
public void collectImmediateSuperTypes(final SNode term, final boolean isWeak, final StructuralNodeSet result, final TypeCheckingContext context) {
if (term == null) {
return;
}
// use global language scope as the context is unknown
LanguageScopeExecutor.execWithLanguageScope(null, new Computable<Object>() {
@Override
public Object compute() {
List<Pair<SubtypingRule_Runtime, IsApplicableStatus>> subtypingRule_runtimes = myTypeChecker.getRulesManager().getSubtypingRules(term, isWeak);
if (subtypingRule_runtimes != null) {
for (final Pair<SubtypingRule_Runtime, IsApplicableStatus> subtypingRule : subtypingRule_runtimes) {
List<SNode> superTypes = subtypingRule.o1.getSubOrSuperTypes(term, context, subtypingRule.o2);
if (superTypes != null) {
result.addAll(superTypes);
}
}
}
return result;
}
});
}
@Override
public Set<SNode> mostSpecificTypes(Set<SNode> nodes) {
return SubtypingUtil.mostSpecificTypes(nodes);
}
@Override
public boolean isComparable(SNode left, SNode right, boolean isWeak) {
return isComparableByRules(left, right, isWeak) ||
isSubTypeByReplacementRules(left, right, isWeak) ||
isSubTypeByReplacementRules(right, left, isWeak) ||
isSubtype(left, right, isWeak) ||
isSubtype(right, left, isWeak);
}
@Override
public boolean isComparableByRules(final SNode left, final SNode right, final boolean isWeak) {
if (left == null || right == null) {
return false;
}
return LanguageScopeExecutor.execWithMultiLanguageScope(
collectLanguagesRecursively(left, right),
new Computable<Boolean>() {
@Override
public Boolean compute() {
List<Pair<ComparisonRule_Runtime, IsApplicable2Status>> comparisonRule_runtimes = myTypeChecker.getRulesManager().getComparisonRules(left, right, isWeak);
if (comparisonRule_runtimes != null) {
for (Pair<ComparisonRule_Runtime, IsApplicable2Status> comparisonRule_runtime : comparisonRule_runtimes) {
if (comparisonRule_runtime.o1.areComparable(left, right, comparisonRule_runtime.o2)) return true;
}
}
comparisonRule_runtimes = myTypeChecker.getRulesManager().getComparisonRules(right, left, isWeak);
if (comparisonRule_runtimes != null) {
for (Pair<ComparisonRule_Runtime, IsApplicable2Status> comparisonRule_runtime : comparisonRule_runtimes) {
if (comparisonRule_runtime.o1.areComparable(right, left, comparisonRule_runtime.o2)) return true;
}
}
return false;
}
});
}
@Override
public Set<SNode> leastCommonSupertypes(Set<SNode> types, boolean isWeak) {
if (types.size() <= 1) {
return types;
}
List<SNode> typesList = SubtypingUtil.eliminateSubTypes(types);
return new HashSet<SNode>(SubtypingUtil.leastCommonSuperTypes(typesList, null));
}
public static Collection<SLanguage> collectLanguagesRecursively(SNode... type) {
ModelDependencyScanner scan = new ModelDependencyScanner().crossModelReferences(false);
for (SNode t : type) {
scan.walk(SNodeUtil.getDescendants(t));
}
return scan.getUsedLanguages();
}
// TODO: wtf
/*package*/ SNode coerceSubTypingNew(final SNode subtype, final IMatchingPattern pattern, final boolean isWeak, final TypeCheckingContext context) {
if (subtype == null) return null;
long start = System.nanoTime();
SNode sNode = myCoercionManager.coerceSubTypingNew(subtype, pattern, isWeak, context);
TypeSystemReporter.getInstance().reportCoerce(subtype, pattern.getConcept(), System.nanoTime()-start);
return sNode;
}
}