Refactoring. Move specificity relations into TypeSpecificityComparator component.
This commit is contained in:
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2010-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 org.jetbrains.kotlin.resolve.jvm
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.flexibility
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
|
||||
object JvmTypeSpecificityComparator : TypeSpecificityComparator {
|
||||
|
||||
override fun isDefinitelyLessSpecific(specific: KotlinType, general: KotlinType): Boolean {
|
||||
if (!specific.isFlexible() || general.isFlexible()) return false
|
||||
|
||||
// general is inflexible
|
||||
val flexibility = specific.flexibility()
|
||||
|
||||
// For primitive types we have to take care of the case when there are two overloaded methods like
|
||||
// foo(int) and foo(Integer)
|
||||
// if we do not discriminate one of them, any call to foo(kotlin.Int) will result in overload resolution ambiguity
|
||||
// so, for such cases, we discriminate Integer in favour of int
|
||||
if (!KotlinBuiltIns.isPrimitiveType(general) || !KotlinBuiltIns.isPrimitiveType(flexibility.lowerBound)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Int? >< Int!
|
||||
if (general.isMarkedNullable) return false
|
||||
// Int! lessSpecific Int
|
||||
return true
|
||||
}
|
||||
}
|
||||
+3
@@ -18,11 +18,13 @@ package org.jetbrains.kotlin.resolve.jvm.platform
|
||||
|
||||
import org.jetbrains.kotlin.container.StorageComponentContainer
|
||||
import org.jetbrains.kotlin.container.useImpl
|
||||
import org.jetbrains.kotlin.container.useInstance
|
||||
import org.jetbrains.kotlin.jvm.RuntimeAssertionsTypeChecker
|
||||
import org.jetbrains.kotlin.load.kotlin.JavaAnnotationCallChecker
|
||||
import org.jetbrains.kotlin.load.kotlin.nativeDeclarations.NativeFunChecker
|
||||
import org.jetbrains.kotlin.resolve.PlatformConfigurator
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmOverloadFilter
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmTypeSpecificityComparator
|
||||
import org.jetbrains.kotlin.resolve.jvm.checkers.*
|
||||
import org.jetbrains.kotlin.synthetic.JavaSyntheticScopes
|
||||
import org.jetbrains.kotlin.types.DynamicTypesSettings
|
||||
@@ -79,5 +81,6 @@ object JvmPlatformConfigurator : PlatformConfigurator(
|
||||
|
||||
container.useImpl<ReflectionAPICallChecker>()
|
||||
container.useImpl<JavaSyntheticScopes>()
|
||||
container.useInstance(JvmTypeSpecificityComparator)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,17 +21,14 @@ import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.results.FlatSignature
|
||||
import org.jetbrains.kotlin.resolve.calls.results.SpecificityComparisonCallbacks
|
||||
import org.jetbrains.kotlin.resolve.calls.results.isSignatureNotLessSpecific
|
||||
import org.jetbrains.kotlin.resolve.calls.results.varargParameterPosition
|
||||
import org.jetbrains.kotlin.resolve.calls.results.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasLowPriorityInOverloadResolution
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||
|
||||
class OverloadChecker {
|
||||
class OverloadChecker(val specificityComparator: TypeSpecificityComparator) {
|
||||
/**
|
||||
* Does not check names.
|
||||
*/
|
||||
@@ -64,8 +61,8 @@ class OverloadChecker {
|
||||
val aSignature = FlatSignature.createFromCallableDescriptor(a)
|
||||
val bSignature = FlatSignature.createFromCallableDescriptor(b)
|
||||
|
||||
val aIsNotLessSpecificThanB = isSignatureNotLessSpecific(aSignature, bSignature, OverloadabilitySpecificityCallbacks)
|
||||
val bIsNotLessSpecificThanA = isSignatureNotLessSpecific(bSignature, aSignature, OverloadabilitySpecificityCallbacks)
|
||||
val aIsNotLessSpecificThanB = isSignatureNotLessSpecific(aSignature, bSignature, OverloadabilitySpecificityCallbacks, specificityComparator)
|
||||
val bIsNotLessSpecificThanA = isSignatureNotLessSpecific(bSignature, aSignature, OverloadabilitySpecificityCallbacks, specificityComparator)
|
||||
|
||||
return !(aIsNotLessSpecificThanB && bIsNotLessSpecificThanA)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-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.
|
||||
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.ModuleParameters
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.*
|
||||
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator
|
||||
import org.jetbrains.kotlin.resolve.scopes.SyntheticScopes
|
||||
import org.jetbrains.kotlin.resolve.validation.DeprecatedSymbolValidator
|
||||
import org.jetbrains.kotlin.resolve.validation.InfixValidator
|
||||
@@ -50,6 +51,7 @@ abstract class TargetPlatform(
|
||||
override fun configure(container: StorageComponentContainer) {
|
||||
super.configure(container)
|
||||
container.useInstance(SyntheticScopes.Empty)
|
||||
container.useInstance(TypeSpecificityComparator.NONE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-13
@@ -20,8 +20,6 @@ import org.jetbrains.kotlin.resolve.calls.inference.CallHandle
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystem
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilderImpl
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.valueParameterPosition
|
||||
import org.jetbrains.kotlin.types.Flexibility
|
||||
import org.jetbrains.kotlin.types.Flexibility.SpecificityRelation
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
@@ -31,10 +29,19 @@ interface SpecificityComparisonCallbacks {
|
||||
fun isNonSubtypeNotLessSpecific(specific: KotlinType, general: KotlinType): Boolean
|
||||
}
|
||||
|
||||
interface TypeSpecificityComparator {
|
||||
fun isDefinitelyLessSpecific(specific: KotlinType, general: KotlinType): Boolean
|
||||
|
||||
object NONE: TypeSpecificityComparator {
|
||||
override fun isDefinitelyLessSpecific(specific: KotlinType, general: KotlinType) = false
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> isSignatureNotLessSpecific(
|
||||
specific: FlatSignature<T>,
|
||||
general: FlatSignature<T>,
|
||||
callbacks: SpecificityComparisonCallbacks,
|
||||
specificityComparator: TypeSpecificityComparator,
|
||||
callHandle: CallHandle = CallHandle.NONE
|
||||
): Boolean {
|
||||
if (specific.hasExtensionReceiver != general.hasExtensionReceiver) return false
|
||||
@@ -48,7 +55,7 @@ fun <T> isSignatureNotLessSpecific(
|
||||
for ((specificType, generalType) in specific.valueParameterTypes.zip(general.valueParameterTypes)) {
|
||||
if (specificType == null || generalType == null) continue
|
||||
|
||||
if (isDefinitelyLessSpecificByTypeSpecificity(specificType, generalType)) {
|
||||
if (specificityComparator.isDefinitelyLessSpecific(specificType, generalType)) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -69,13 +76,3 @@ fun <T> isSignatureNotLessSpecific(
|
||||
val constraintSystem = constraintSystemBuilder.build()
|
||||
return !constraintSystem.status.hasContradiction()
|
||||
}
|
||||
|
||||
private fun KotlinType.getSpecificityRelationTo(otherType: KotlinType) =
|
||||
this.getCapability(Flexibility::class.java)?.getSpecificityRelationTo(otherType) ?: Flexibility.SpecificityRelation.DONT_KNOW
|
||||
|
||||
private fun isDefinitelyLessSpecificByTypeSpecificity(specific: KotlinType, general: KotlinType): Boolean {
|
||||
val sThanG = specific.getSpecificityRelationTo(general)
|
||||
val gThanS = general.getSpecificityRelationTo(specific)
|
||||
return sThanG == SpecificityRelation.LESS_SPECIFIC &&
|
||||
gThanS != SpecificityRelation.LESS_SPECIFIC
|
||||
}
|
||||
+7
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-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.
|
||||
@@ -32,7 +32,10 @@ import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
|
||||
class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
|
||||
class OverloadingConflictResolver(
|
||||
private val builtIns: KotlinBuiltIns,
|
||||
private val specificityComparator: TypeSpecificityComparator
|
||||
) {
|
||||
|
||||
fun <D : CallableDescriptor> findMaximallySpecific(
|
||||
candidates: Set<MutableResolvedCall<D>>,
|
||||
@@ -157,7 +160,7 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
|
||||
if (isGeneric1 && isGeneric2) return false
|
||||
}
|
||||
|
||||
return isSignatureNotLessSpecific(call1, call2, SpecificityComparisonWithNumerics, call1.callHandle())
|
||||
return isSignatureNotLessSpecific(call1, call2, SpecificityComparisonWithNumerics, specificityComparator, call1.callHandle())
|
||||
}
|
||||
|
||||
private val SpecificityComparisonWithNumerics = object : SpecificityComparisonCallbacks {
|
||||
@@ -243,7 +246,7 @@ class OverloadingConflictResolver(private val builtIns: KotlinBuiltIns) {
|
||||
|
||||
val fSignature = FlatSignature.createFromCallableDescriptor(f)
|
||||
val gSignature = FlatSignature.createFromCallableDescriptor(g)
|
||||
return isSignatureNotLessSpecific(fSignature, gSignature, SpecificityComparisonWithNumerics)
|
||||
return isSignatureNotLessSpecific(fSignature, gSignature, SpecificityComparisonWithNumerics, specificityComparator)
|
||||
}
|
||||
|
||||
private fun isNotLessSpecificCallableReference(f: CallableDescriptor, g: CallableDescriptor): Boolean =
|
||||
|
||||
+4
-1
@@ -82,7 +82,10 @@ public class ExpressionTypingUtils {
|
||||
|
||||
@NotNull
|
||||
public static LexicalWritableScope newWritableScopeImpl(
|
||||
@NotNull ExpressionTypingContext context, @NotNull LexicalScopeKind scopeKind, @NotNull OverloadChecker overloadChecker) {
|
||||
@NotNull ExpressionTypingContext context,
|
||||
@NotNull LexicalScopeKind scopeKind,
|
||||
@NotNull OverloadChecker overloadChecker
|
||||
) {
|
||||
return new LexicalWritableScope(context.scope, context.scope.getOwnerDescriptor(), false, null,
|
||||
new TraceBasedLocalRedeclarationChecker(context.trace, overloadChecker), scopeKind);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.psi.KtNamedFunction;
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactoryKt;
|
||||
import org.jetbrains.kotlin.resolve.FunctionDescriptorResolver;
|
||||
import org.jetbrains.kotlin.resolve.OverloadChecker;
|
||||
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator;
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind;
|
||||
@@ -34,7 +35,7 @@ import org.jetbrains.kotlin.tests.di.InjectionKt;
|
||||
public class KotlinOverloadTest extends KotlinTestWithEnvironment {
|
||||
private final ModuleDescriptor root = KotlinTestUtils.createEmptyModule("<test_root>");
|
||||
private FunctionDescriptorResolver functionDescriptorResolver;
|
||||
private final OverloadChecker overloadChecker = new OverloadChecker();
|
||||
private final OverloadChecker overloadChecker = new OverloadChecker(TypeSpecificityComparator.NONE.INSTANCE);
|
||||
|
||||
@Override
|
||||
protected KotlinCoreEnvironment createEnvironment() {
|
||||
@@ -155,8 +156,6 @@ public class KotlinOverloadTest extends KotlinTestWithEnvironment {
|
||||
FunctionDescriptor a = makeFunction(funA);
|
||||
FunctionDescriptor b = makeFunction(funB);
|
||||
|
||||
|
||||
|
||||
boolean aOverloadableWithB = overloadChecker.isOverloadable(a, b);
|
||||
assertEquals(expectedIsError, !aOverloadableWithB);
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.psi.KtPsiFactoryKt;
|
||||
import org.jetbrains.kotlin.psi.KtTypeReference;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||
import org.jetbrains.kotlin.resolve.*;
|
||||
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator;
|
||||
import org.jetbrains.kotlin.resolve.lazy.LazyResolveTestUtil;
|
||||
import org.jetbrains.kotlin.resolve.scopes.*;
|
||||
import org.jetbrains.kotlin.resolve.scopes.utils.ScopeUtilsKt;
|
||||
@@ -90,7 +91,8 @@ public class TypeSubstitutorTest extends KotlinTestWithEnvironment {
|
||||
LexicalScope topLevelScope = trace.get(BindingContext.LEXICAL_SCOPE, jetFile);
|
||||
final ClassifierDescriptor contextClass = ScopeUtilsKt.findClassifier(topLevelScope, Name.identifier("___Context"), NoLookupLocation.FROM_TEST);
|
||||
assert contextClass instanceof ClassDescriptor;
|
||||
LocalRedeclarationChecker redeclarationChecker = new ThrowingLocalRedeclarationChecker(new OverloadChecker());
|
||||
LocalRedeclarationChecker redeclarationChecker = new ThrowingLocalRedeclarationChecker(new OverloadChecker(
|
||||
TypeSpecificityComparator.NONE.INSTANCE));
|
||||
LexicalScope typeParameters = new LexicalScopeImpl(topLevelScope, module, false, null, LexicalScopeKind.SYNTHETIC,
|
||||
redeclarationChecker,
|
||||
new Function1<LexicalScopeImpl.InitializeHandler, Unit>() {
|
||||
|
||||
-18
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.load.java.lazy.types
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
@@ -34,9 +33,7 @@ import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.Flexibility.SpecificityRelation
|
||||
import org.jetbrains.kotlin.types.Variance.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
import org.jetbrains.kotlin.types.typeUtil.createProjection
|
||||
import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations
|
||||
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
|
||||
@@ -315,21 +312,6 @@ class LazyJavaTypeResolver(
|
||||
else create(replacement, TypeUtils.makeNullable(replacement))
|
||||
}
|
||||
|
||||
override fun getSpecificityRelationTo(otherType: KotlinType): SpecificityRelation {
|
||||
// For primitive types we have to take care of the case when there are two overloaded methods like
|
||||
// foo(int) and foo(Integer)
|
||||
// if we do not discriminate one of them, any call to foo(kotlin.Int) will result in overload resolution ambiguity
|
||||
// so, for such cases, we discriminate Integer in favour of int
|
||||
if (!KotlinBuiltIns.isPrimitiveType(otherType) || !KotlinBuiltIns.isPrimitiveType(lowerBound)) {
|
||||
return SpecificityRelation.DONT_KNOW
|
||||
}
|
||||
// Int! >< Int?
|
||||
if (otherType.isFlexible()) return SpecificityRelation.DONT_KNOW
|
||||
// Int? >< Int!
|
||||
if (otherType.isMarkedNullable) return SpecificityRelation.DONT_KNOW
|
||||
// Int! lessSpecific Int
|
||||
return SpecificityRelation.LESS_SPECIFIC
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.kotlin.types
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.types.Flexibility.SpecificityRelation
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
|
||||
@@ -53,10 +52,6 @@ object DynamicTypeFactory : FlexibleTypeFactory {
|
||||
|
||||
override val delegateType: KotlinType get() = upperBound
|
||||
|
||||
override fun getSpecificityRelationTo(otherType: KotlinType): SpecificityRelation {
|
||||
return if (!otherType.isDynamic()) SpecificityRelation.LESS_SPECIFIC else SpecificityRelation.DONT_KNOW
|
||||
}
|
||||
|
||||
override fun makeNullableAsSpecified(nullable: Boolean): KotlinType {
|
||||
// Nullability has no effect on dynamics
|
||||
return createDynamicType(delegateType.builtIns)
|
||||
|
||||
@@ -49,13 +49,6 @@ interface Flexibility : TypeCapability, SubtypingRepresentatives {
|
||||
|
||||
fun makeNullableAsSpecified(nullable: Boolean): KotlinType
|
||||
|
||||
enum class SpecificityRelation {
|
||||
LESS_SPECIFIC,
|
||||
MORE_SPECIFIC,
|
||||
DONT_KNOW
|
||||
}
|
||||
|
||||
fun getSpecificityRelationTo(otherType: KotlinType): SpecificityRelation
|
||||
}
|
||||
|
||||
fun KotlinType.isFlexible(): Boolean = this.getCapability(Flexibility::class.java) != null
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
* Copyright 2010-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.
|
||||
@@ -43,5 +43,6 @@ object JsPlatformConfigurator : PlatformConfigurator(
|
||||
|
||||
container.useImpl<JsCallChecker>()
|
||||
container.useInstance(SyntheticScopes.Empty)
|
||||
container.useInstance(JsTypeSpecificityComparator)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2010-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 org.jetbrains.kotlin.js.resolve
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.isDynamic
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
|
||||
object JsTypeSpecificityComparator: TypeSpecificityComparator {
|
||||
|
||||
private fun checkOnlyDynamicFlexibleType(type: KotlinType) {
|
||||
if (type.isFlexible()) {
|
||||
assert(type.isDynamic()) {
|
||||
"Unexpected flexible type in Js: $type"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isDefinitelyLessSpecific(specific: KotlinType, general: KotlinType): Boolean {
|
||||
checkOnlyDynamicFlexibleType(specific)
|
||||
checkOnlyDynamicFlexibleType(general)
|
||||
|
||||
return specific.isDynamic() && !general.isDynamic()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user