Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
2846 lines (2560 sloc) 98.9 KB
/**
* 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
*/
/*
* N4JS Type System.
*
* <h3>Naming conventions</h3>
*
* Rules and axioms are named using the following pattern:
* <pre>
* &lt;relationName>&lt;InputType(s)>&lt;Additional_description>
* </pre>
* If a rule has multiple input parameters, either only the first one is used (in case
* the second is not that important), or all types are used (without any separator).
* <p>
* The rules/axioms are ordered by judgment, and then according to the ("major" or first) input type,
* if possible the order is similar as the order used in the N4JS specification.
*
* <h3>Changes to the Rule Environment</h3>
*
* By convention, rules should <em>not</em> change the rule environment and can (and should) rely
* on other rules not changing the rule environment in case of nested inference. Thus:
* <ul>
* <li>before modifying the rule environment a new, derived rule environment must be created,
* usually by using extension method #wrap(). For example:
* <pre>
* val G2 = G.wrap;
* G2.add("MY_KEY",someValue);
* G2.addSubstitutions(someTypeRef);
* </pre>
* <li>when doing nested inference within a rule, no new rule environment should be created (because
* we assume it won't be changed by the nested rule calls)
* </ul>
* There are two ways of creating a new rule environment: create empty environment or wrap existing
* rule environment. When passing the newly created environment on to a nested inference, it is important
* to always derive, because otherwise recursion guards will be lost.
*
* <h3>Bibliography</h3>
* <dl>
* <dt><a name="N4JS">[N4JS]</a></dt>
* <dd>Pilgrim, Jens von et al.: N4JS Specification / NumberFour AG. Berlin, September 2013.
* Specification. <a href="https://github.com/NumberFour/specs/">https://github.com/NumberFour/specs/</a></dd>
* </dl>
*/
system org.eclipse.n4js.xsemantics.InternalTypeSystem
copyright
"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"
validatorExtends AbstractMessageAdjustingN4JSValidator // AbstractN4JSValidator
import java.util.List
import org.eclipse.emf.common.util.ECollections
import org.eclipse.emf.ecore.EObject
import org.eclipse.n4js.AnnotationDefinition
import org.eclipse.n4js.n4JS.AdditiveExpression
import org.eclipse.n4js.n4JS.AdditiveOperator
import org.eclipse.n4js.n4JS.Argument
import org.eclipse.n4js.n4JS.ArrayElement
import org.eclipse.n4js.n4JS.ArrayLiteral
import org.eclipse.n4js.n4JS.ArrayPadding
import org.eclipse.n4js.n4JS.ArrowFunction
import org.eclipse.n4js.n4JS.AssignmentExpression
import org.eclipse.n4js.n4JS.AssignmentOperator
import org.eclipse.n4js.n4JS.AwaitExpression
import org.eclipse.n4js.n4JS.BinaryBitwiseExpression
import org.eclipse.n4js.n4JS.BinaryLogicalExpression
import org.eclipse.n4js.n4JS.BindingElement
import org.eclipse.n4js.n4JS.BooleanLiteral
import org.eclipse.n4js.n4JS.CastExpression
import org.eclipse.n4js.n4JS.CatchVariable
import org.eclipse.n4js.n4JS.CommaExpression
import org.eclipse.n4js.n4JS.ConditionalExpression
import org.eclipse.n4js.n4JS.DestructureUtils
import org.eclipse.n4js.n4JS.EqualityExpression
import org.eclipse.n4js.n4JS.Expression
import org.eclipse.n4js.n4JS.ExpressionStatement
import org.eclipse.n4js.n4JS.ForStatement
import org.eclipse.n4js.n4JS.FormalParameter
import org.eclipse.n4js.n4JS.FunctionExpression
import org.eclipse.n4js.n4JS.GetterDeclaration
import org.eclipse.n4js.n4JS.IdentifierRef
import org.eclipse.n4js.n4JS.IndexedAccessExpression
import org.eclipse.n4js.n4JS.IntLiteral
import org.eclipse.n4js.n4JS.JSXElement
import org.eclipse.n4js.n4JS.JSXPropertyAttribute
import org.eclipse.n4js.n4JS.LocalArgumentsVariable
import org.eclipse.n4js.n4JS.MigrationContextVariable
import org.eclipse.n4js.n4JS.MultiplicativeExpression
import org.eclipse.n4js.n4JS.N4ClassDeclaration
import org.eclipse.n4js.n4JS.N4ClassExpression
import org.eclipse.n4js.n4JS.N4ClassifierDefinition
import org.eclipse.n4js.n4JS.N4EnumLiteral
import org.eclipse.n4js.n4JS.N4FieldDeclaration
import org.eclipse.n4js.n4JS.N4GetterDeclaration
import org.eclipse.n4js.n4JS.N4JSASTUtils
import org.eclipse.n4js.n4JS.N4JSPackage
import org.eclipse.n4js.n4JS.N4MemberDeclaration
import org.eclipse.n4js.n4JS.N4MethodDeclaration
import org.eclipse.n4js.n4JS.N4SetterDeclaration
import org.eclipse.n4js.n4JS.NewExpression
import org.eclipse.n4js.n4JS.NewTarget
import org.eclipse.n4js.n4JS.NullLiteral
import org.eclipse.n4js.n4JS.NumericLiteral
import org.eclipse.n4js.n4JS.ObjectLiteral
import org.eclipse.n4js.n4JS.ParameterizedCallExpression
import org.eclipse.n4js.n4JS.ParameterizedPropertyAccessExpression
import org.eclipse.n4js.n4JS.ParenExpression
import org.eclipse.n4js.n4JS.PostfixExpression
import org.eclipse.n4js.n4JS.PromisifyExpression
import org.eclipse.n4js.n4JS.PropertyNameValuePair
import org.eclipse.n4js.n4JS.RegularExpressionLiteral
import org.eclipse.n4js.n4JS.RelationalExpression
import org.eclipse.n4js.n4JS.RelationalOperator
import org.eclipse.n4js.n4JS.ReturnStatement
import org.eclipse.n4js.n4JS.SetterDeclaration
import org.eclipse.n4js.n4JS.ShiftExpression
import org.eclipse.n4js.n4JS.StringLiteral
import org.eclipse.n4js.n4JS.SuperLiteral
import org.eclipse.n4js.n4JS.TaggedTemplateString
import org.eclipse.n4js.n4JS.TemplateLiteral
import org.eclipse.n4js.n4JS.TemplateSegment
import org.eclipse.n4js.n4JS.ThisLiteral
import org.eclipse.n4js.n4JS.TypeDefiningElement
import org.eclipse.n4js.n4JS.UnaryExpression
import org.eclipse.n4js.n4JS.UnaryOperator
import org.eclipse.n4js.n4JS.VariableBinding
import org.eclipse.n4js.n4JS.VariableDeclaration
import org.eclipse.n4js.n4JS.YieldExpression
import org.eclipse.n4js.n4idl.versioning.N4IDLVersionResolver
import org.eclipse.n4js.n4jsx.ReactHelper
import org.eclipse.n4js.postprocessing.ASTMetaInfoUtils
import org.eclipse.n4js.scoping.members.MemberScopingHelper
import org.eclipse.n4js.ts.typeRefs.BoundThisTypeRef
import org.eclipse.n4js.ts.typeRefs.ComposedTypeRef
import org.eclipse.n4js.ts.typeRefs.ExistentialTypeRef
import org.eclipse.n4js.ts.typeRefs.FunctionTypeExprOrRef
import org.eclipse.n4js.ts.typeRefs.FunctionTypeExpression
import org.eclipse.n4js.ts.typeRefs.FunctionTypeRef
import org.eclipse.n4js.ts.typeRefs.IntersectionTypeExpression
import org.eclipse.n4js.ts.typeRefs.ParameterizedTypeRef
import org.eclipse.n4js.ts.typeRefs.StructuralTypeRef
import org.eclipse.n4js.ts.typeRefs.ThisTypeRef
import org.eclipse.n4js.ts.typeRefs.ThisTypeRefStructural
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.TypeTypeRef
import org.eclipse.n4js.ts.typeRefs.UnionTypeExpression
import org.eclipse.n4js.ts.typeRefs.UnknownTypeRef
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.ModuleNamespaceVirtualType
import org.eclipse.n4js.ts.types.NullType
import org.eclipse.n4js.ts.types.PrimitiveType
import org.eclipse.n4js.ts.types.TClass
import org.eclipse.n4js.ts.types.TClassifier
import org.eclipse.n4js.ts.types.TEnum
import org.eclipse.n4js.ts.types.TEnumLiteral
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.TInterface
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.TStructuralType
import org.eclipse.n4js.ts.types.TVariable
import org.eclipse.n4js.ts.types.TypableElement
import org.eclipse.n4js.ts.types.Type
import org.eclipse.n4js.ts.types.TypeVariable
import org.eclipse.n4js.ts.types.TypingStrategy
import org.eclipse.n4js.ts.types.UndefinedType
import org.eclipse.n4js.ts.types.VoidType
import org.eclipse.n4js.ts.types.util.AllSuperTypeRefsCollector
import org.eclipse.n4js.ts.types.util.Variance
import org.eclipse.n4js.ts.utils.TypeHelper
import org.eclipse.n4js.ts.utils.TypeUtils
import org.eclipse.n4js.typesbuilder.N4JSFunctionDefinitionTypesBuilder
import org.eclipse.n4js.typesystem.TypeSystemHelper
import org.eclipse.n4js.utils.ContainerTypesHelper
import org.eclipse.n4js.utils.DestructureHelper
import org.eclipse.n4js.utils.N4JSLanguageUtils
import org.eclipse.n4js.utils.PromisifyHelper
import org.eclipse.n4js.validation.AbstractMessageAdjustingN4JSValidator
import org.eclipse.n4js.validation.JavaScriptVariantHelper
import org.eclipse.n4js.xtext.scoping.IEObjectDescriptionWithError
import org.eclipse.xsemantics.runtime.RuleEnvironment
import org.eclipse.xtext.EcoreUtil2
import org.eclipse.xtext.naming.IQualifiedNameConverter
import static org.eclipse.n4js.typesystem.TypeSystemErrorExtensions.PRIORITY_ERROR
import static extension org.eclipse.n4js.ts.utils.TypeExtensions.*
import static extension org.eclipse.n4js.ts.utils.TypeUtils.*
import static extension org.eclipse.n4js.typesystem.RuleEnvironmentExtensions.*
import static extension org.eclipse.n4js.typesystem.TypeSystemErrorExtensions.*
import org.eclipse.n4js.n4idl.versioning.MigrationUtils
inject TypeSystemHelper typeSystemHelper
inject N4JSFunctionDefinitionTypesBuilder functionDefinitionTypesBuilder
inject TypeHelper typeHelper
inject ContainerTypesHelper containerTypesHelper
inject DestructureHelper destructureHelper
inject MemberScopingHelper memberScopingHelper
inject PromisifyHelper promisifyHelper
inject JavaScriptVariantHelper jsVariantHelper
inject N4IDLVersionResolver versionResolver
inject IQualifiedNameConverter qualifiedNameConverter
inject ReactHelper reactHelper
judgments {
/*
* Infers the type for a given typable element. Note that the judgment is overridden in subclass
* CustomInternalTypeSystem, to ensure that all type inference takes place during post-processing
* of an N4JSResource and after that this judgment will never be invoked again.
*/
type |- TypableElement element : output TypeRef
error "cannot type " + element?.eClass?.name + " " + stringRep(element)
source element
subtype |- TypeArgument left <: TypeArgument right
error stringRep(left) + " is not a subtype of " + stringRep(right)
supertype |- TypeArgument left :> TypeArgument right
error stringRep(left) + " is not a super type of " + stringRep(right)
equaltype |- TypeArgument left ~~ TypeArgument right
error stringRep(left) + " is not equal to " + stringRep(right)
/**
* Returns the expected type of an expression in a given container or <code>null</code> (i.e.
* no type expectation at all) or the top type (i.e. not particular type expected but a value
* is expected, meaning all types are fine except 'void').
*/
expectedTypeIn |- EObject container |> Expression expression : output TypeRef
upperBound |~ TypeArgument typeArgument /\ output TypeRef
lowerBound |~ TypeArgument typeArgument \/ output TypeRef
/*
* Substitutes type variables (that is, replaces type variables with actual type arguments taken from
* the rule environment).
* <p>
* The given typeArg will never be changed, instead a copy will be created to reflect the substitution.
* If nothing was substituted (i.e. given typeArg does not contain any type variable or only type
* variables without a binding in the rule environment), then no copy will be created and typeArg
* will be returned. Therefore, client code can do an identity check on the return value to find out
* if a substitution was performed.
* TODO currently unnecessary copies are created in some cases; clean-up code to make last statement valid!
* <p>
* Invariant: if you put in a TypeRef, you'll get a TypeRef back (only other case: put in a
* Wildcard and you'll get a Wildcard). But this is not true for subclasses of TypeRef, e.g.
* put in a FunctionTypeRef and you might get a FunctionTypeExpression back).
*/
substTypeVariables |- TypeArgument typeArg ~> output TypeArgument
/*
* Computes the this type at the given location. Since ECMAScript does not
* support lexical this typing, this basically is a heuristic.
*/
thisTypeRef |~ EObject location ~> output TypeRef
}
// **********************************************************************
// Judgment type
// **********************************************************************
/* ----------------------------------------------------------------------
* type model elements
* -------------------------------------------------------------------- */
/*
* We already have a type -> thus we simply wrap the existing type in a TypeRef.
* This rule is a bit odd: the client already has a Type in hand and invokes the type judgment to obtain
* its type, which seems superfluous. However, supporting this simplifies some client code, because the
* client need not check for this special case and call TypeUtils#createTypeRef() directly.
*/
axiom typeType G |- Type type: TypeUtils.wrapTypeInTypeRef(type)
// keep aligned to axiom typeN4EnumLiteral
axiom typeTEnumLiteral G |- TEnumLiteral enumLiteral : (enumLiteral.eContainer as TEnum).ref
/* @see [N4JS] 4.18.1. Fields */
rule typeTField
G |- TField tfield: TypeRef T
from {
T = tfield.typeRef
}
rule typeTGetter G |- TGetter tgetter: TypeRef T
from {
T = tgetter.declaredTypeRef ?: G.anyTypeRef
}
rule typeTSetter G |- TSetter tsetter: TypeRef T
from {
T = tsetter.declaredTypeRef ?: G.anyTypeRef
}
rule typeTVariable
G |- TVariable tvariable : TypeRef T
from {
T = tvariable.typeRef
}
/* ----------------------------------------------------------------------
* AST nodes: type defining elements (esp. declarations except VariableDeclaration)
* -------------------------------------------------------------------- */
/*
* Similar to axiom typeType, this axiom implements a trivial fact that could easily be
* handled by client code (i.e. call TypeUtils#createTypeRef() directly) but supporting it
* here relieves client code of handling special cases.
*/
axiom typeTypeDefiningElement G |- TypeDefiningElement elem : TypeUtils.wrapTypeInTypeRef(elem.definedType) ?: TypeRefsFactory.eINSTANCE.createUnknownTypeRef
/* @see [N4JS] 6.1.5. Object Literal */
rule typeObjectLiteral
G |- ObjectLiteral ol : TypeRef T
from {
val ptr = TypeRefsFactory::eINSTANCE.createParameterizedTypeRefStructural();
ptr.declaredType = G.objectType;
ptr.structuralType = ol.definedType as TStructuralType;
ptr.definedTypingStrategy = TypingStrategy.STRUCTURAL;
T = ptr
}
/* ----------------------------------------------------------------------
* AST nodes: expressions
* -------------------------------------------------------------------- */
/* @see [N4JS] 6.1.1. The this keyword */
rule typeThisKeyword
G |- ThisLiteral t : TypeRef T
from {
var TypeRef rawT; G |~ t ~> rawT
rawT = versionResolver.resolveVersion(rawT, rawT)
T = TypeUtils.enforceNominalTyping(rawT)
}
rule typeSuperLiteral
G |- SuperLiteral superLiteral: TypeRef T
from {
{
val containingMemberDecl = EcoreUtil2.getContainerOfType(superLiteral.eContainer,N4MemberDeclaration);
if (containingMemberDecl === null) {
// super at wrong location, will be checked in Expression Validator
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef
} else {
val containingClass = (containingMemberDecl.eContainer as N4ClassDeclaration).definedType as TClass;
val superClass = G.getDeclaredOrImplicitSuperType(containingClass)
var effectiveSuperClass = superClass
if( containingClass.isStaticPolyfill ) { // IDE-1735, static-polyfills replacing original constructor.
effectiveSuperClass = G.getDeclaredOrImplicitSuperType( superClass as TClass )
}
// Note: to avoid nasty special cases in rules expectedTypeOfArgumentInCallExpression, expectedTypeOfArgumentInNewExpression,
// typePropertyAccessExpression, typeIndexedAccessExpression, etc. we make the type of keyword super depend on where/how it is used.
{
// case 1: super member access, i.e. super.foo() OR super['foo']
superLiteral.eContainer instanceof ParameterizedPropertyAccessExpression || superLiteral.eContainer instanceof IndexedAccessExpression
if(containingMemberDecl.static)
T = effectiveSuperClass?.createConstructorTypeRef
else
T = effectiveSuperClass?.createTypeRef
if (T !== null)
T = TypeUtils.enforceNominalTyping(T)
}
or
{
// case 2: super call, i.e. super()
superLiteral.eContainer instanceof ParameterizedCallExpression
if(containingMemberDecl instanceof N4MethodDeclaration && containingMemberDecl.name == 'constructor') {
// super() is used in a constructor
val ctor = containerTypesHelper.fromContext(superLiteral.eResource).findConstructor(effectiveSuperClass);
T = ctor?.createTypeRef
}
else {
// super() used in a normal method, getter or setter (not in a constructor)
// --> this is an error case, but error will be produced by validation rule
// --> make sure no error is produced in the type system to avoid duplicate errors
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef
}
}
or
{
// case 3: super with new keyword, i.e. new super OR new super(<args>)
superLiteral.eContainer instanceof NewExpression
// not yet supported
}
}
or
{
// super at wrong location, will be checked in Expression Validator
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef
}
}
}
/* @see [N4JS] 6.1.2. Identifier */
rule typeIdentifierRef G |- IdentifierRef idref: TypeRef T
from {
G |- idref.id : T
T = versionResolver.resolveVersion(T, idref);
if(idref.eContainer instanceof ParameterizedCallExpression
&& idref.eContainmentFeature===N4JSPackage.eINSTANCE.getParameterizedCallExpression_Target()) {
val callableCtorFunction = typeSystemHelper.getCallableClassConstructorFunction(G, T);
if(callableCtorFunction!==null) {
T = callableCtorFunction.ref;
}
}
}
/* @see [N4JS] 6.1.3. Literals */
axiom typeNullLiteral
G |- NullLiteral l : G.nullTypeRef
/* @see [N4JS] 6.1.3. Literals */
axiom typeBooleanLiteral
G |- BooleanLiteral l : G.booleanTypeRef
/* @see [N4JS] 6.1.3. Literals */
rule typeNumericLiteral G |- NumericLiteral l : TypeRef T
from {
T = if(N4JSLanguageUtils.isIntLiteral(l)) G.intTypeRef else G.numberTypeRef;
}
/* @see [N4JS] 6.1.3. Literals */
axiom typeStringLiteral
G |- StringLiteral l : G.stringTypeRef
/* @see [N4JS] 6.1.3. Literals */
axiom typeRegExpLiteral
G |- RegularExpressionLiteral l : G.regexpTypeRef
axiom typeTaggedTemplateString
G |- TaggedTemplateString l : G.stringTypeRef
/* @see [N4JS] 6.1.3. Literals */
axiom typeTemplateLiteral
G |- TemplateLiteral l : G.stringTypeRef
/* @see [N4JS] 6.1.3. Literals */
axiom typeTemplateSegment
G |- TemplateSegment l : G.stringTypeRef
// keep aligned to axiom typeTEnumLiteral
axiom typeN4EnumLiteral
G |- N4EnumLiteral enumLiteral: (enumLiteral.definedLiteral.eContainer as TEnum).ref
/* @see [N4JS] 6.1.4. Array Literal */
rule typeArrayLiteral
G |- ArrayLiteral al : TypeRef T
from {
fail error "rule typeArrayLiteral should never be invoked (PolyComputer is responsible for typing ArrayLiterals)"
}
// FIXME the following should probably be obsolete as well:
/* @see [N4JS] 6.1.4. Array Literal */
axiom typeArrayPadding
G |- ArrayPadding p: G.undefinedTypeRef
rule typeArrayElement
G |- ArrayElement e: TypeRef T
from {
G |- e.expression: T
}
/*
* This rule is usually called by typeRecordElement, as property name value pairs are internally represented as
* record elements.
* @see [N4JS] 6.1.5. Object Literal
*/
rule typePropertyNameValuePair
G |- PropertyNameValuePair property: TypeRef T
from {
// note: keep this rule aligned with rules typeN4FieldDeclaration and typeVariableDeclaration
if (property.declaredTypeRef!==null) {
T = property.declaredTypeRef
} else if (property.expression !==null) {
G |- property.expression: var TypeRef E
G |~ E /\ E // take upper bound to get rid of ExistentialTypeRef (if any)
if (E.declaredType===G.undefinedType || E.declaredType===G.nullType || E.declaredType===G.voidType) {
T = G.anyTypeRef
} else {
T = E
}
} else {
T = G.anyTypeRef
}
}
rule typeN4FieldDeclaration
G |- N4FieldDeclaration fieldDecl: TypeRef T
from {
// note: keep this rule aligned with rules typePropertyNameValuePair and typeVariableDeclaration
if (fieldDecl.declaredTypeRef!==null) {
T = fieldDecl.declaredTypeRef
} else if (fieldDecl.expression !==null) {
G |- fieldDecl.expression : var TypeRef E
G |~ E /\ E // take upper bound to get rid of ExistentialTypeRef (if any)
if (E.declaredType===G.undefinedType || E.declaredType===G.nullType || E.declaredType===G.voidType) {
T = G.anyTypeRef
} else {
T = E
}
} else {
T = G.anyTypeRef
}
}
/*
* This rule is usually called by typeTGetter, as getters are internally represented as
* TGetter.
* @see [N4JS] 6.1.5. Object Literal
*/
rule typeGetterDeclaration
G |- GetterDeclaration getter: TypeRef T
from {
if (getter.declaredTypeRef!==null) {
T = getter.declaredTypeRef
} else {
if (getter.definedGetter?.declaredTypeRef!==null) {
T = getter.definedGetter.declaredTypeRef
} else {
T = G.anyTypeRef
}
}
}
/*
* This rule is rarely called, as setters are internally represented as
* TSetter.
* @see [N4JS] 6.1.5. Object Literal
*/
rule typeSetterDeclaration
G |- SetterDeclaration setter: TypeRef T
from {
if (setter.declaredTypeRef!==null) {
T = setter.declaredTypeRef
}
if (T===null) {
T = G.anyTypeRef
}
}
/* @see [N4JS] 6.1.6. Parenthesised Expression and Grouping Operator */
rule typeParenExpression
G |- ParenExpression e: TypeRef T
from {
G |- e.expression: T
}
rule typeYieldExpression
G |- YieldExpression y : TypeRef T
from {
var TypeRef t;
if (y.isMany()) {
val yieldValue = y.getExpression();
G |- yieldValue : var TypeRef yieldValueTypeRef;
val scope = G.getPredefinedTypes().builtInTypeScope;
if (TypeUtils.isGenerator(yieldValueTypeRef, scope)) {
t = typeSystemHelper.getGeneratorTReturn(G, yieldValueTypeRef);
} else {
val itTypeRef = G.iterableTypeRef(TypeUtils.createWildcard);
val isIterable = {G |- yieldValueTypeRef <: itTypeRef};
if (isIterable) {
// In case a custom iterable is given, it might return an
// entry like {done=true, value=...} where value can have
// any type and would be returned. Hence, the return type
// is 'any' here.
t = scope.anyTypeRef;
}
}
} else {
val actualGenTypeRef = typeSystemHelper.getActualGeneratorReturnType(G, y);
if (actualGenTypeRef !== null) {
t = typeSystemHelper.getGeneratorTNext(G, actualGenTypeRef);
}
}
if (t === null) {
t = G.anyTypeRef;
}
T = t;
}
/* @see [N4JS] 6.4.1. Asynchronous Functions.
* Summary: given expr of type E, await expr has type:
* E if E is not a Promise ("pass through" case)
* the success type of the promise otherwise.
*/
rule typeAwaitExpression
G |- AwaitExpression e : TypeRef T
from {
G |- e.expression : var TypeRef exprType
if (exprType.declaredType === G.promiseType) {
// standard case: use await on a promise
G |~ exprType.typeArgs.get(0) /\ T // result will be upper bound of first type argument
} else if(promisifyHelper.isPromisifiableExpression(e.expression)) {
// "auto-promisify" case (i.e. an "await <expr>" that is a short-syntax for "await @Promisify <expr>")
val promisifiedReturnTypeRef = promisifyHelper.extractPromisifiedReturnType(e.expression);
if (promisifiedReturnTypeRef.declaredType === G.promiseType) {
G |~ promisifiedReturnTypeRef.typeArgs.get(0) /\ T // result will be upper bound of first type argument
} else {
T = promisifiedReturnTypeRef;
}
} else {
// "pass through" case
T = exprType
}
}
rule typePromisifyExpression
G |- PromisifyExpression e : TypeRef T
from {
T = promisifyHelper.extractPromisifiedReturnType(e.expression);
}
/* @see [N4JS] 6.1.7. Property Accessors */
rule typeIndexedAccessExpression
G |- IndexedAccessExpression expr: TypeRef T
from {
{
// broken AST
expr.target === null || expr.index === null;
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef
}
or
{
// index access on super keyword is disallowed
expr.target instanceof SuperLiteral
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef
}
or
{
G |- expr.target : var TypeRef targetTypeRef
targetTypeRef = typeSystemHelper.resolveType(G, targetTypeRef);
G |- expr.index : var TypeRef indexTypeRef;
val targetDeclType = targetTypeRef.declaredType;
val targetIsLiteralOfStringBasedEnum = targetDeclType instanceof TEnum && AnnotationDefinition.STRING_BASED.hasAnnotation(targetDeclType);
val indexIsNumeric = { G |- indexTypeRef <: G.numberTypeRef };
val indexValue = ASTMetaInfoUtils.getCompileTimeValue(expr.index);
val memberName = N4JSLanguageUtils.derivePropertyNameFromCompileTimeValue(indexValue);
if (indexIsNumeric && (targetTypeRef.isArrayLike || targetIsLiteralOfStringBasedEnum)) {
if (targetDeclType.generic && targetTypeRef.typeArgs.isEmpty) {
T = G.anyTypeRef // later: evaluate name if possible, we may even want to return smth like intersect(allProperties)
} else {
val G2 = G.wrap
typeSystemHelper.addSubstitutions(G2, targetTypeRef)
G2.addThisType(targetTypeRef)
val elementTypeRef = if(targetIsLiteralOfStringBasedEnum) {
G.stringType.elementType
} else {
targetDeclType.elementType
};
G2 |- elementTypeRef ~> T
}
} else if (memberName!==null) {
// indexing via constant computed-name, sub-cases: static or instance member, for the latter nominally-typed or structurally-typed receiver
val staticAccess = (targetTypeRef instanceof TypeTypeRef)
val structFieldInitMode = targetTypeRef.typingStrategy === TypingStrategy.STRUCTURAL_FIELD_INITIALIZER
val checkVisibility = false // access modifiers checked in validation
val scope = memberScopingHelper.createMemberScope(targetTypeRef, expr, checkVisibility, staticAccess, structFieldInitMode)
val memberDesc = if(memberName!==null && !memberName.isEmpty()) {
scope.getSingleElement(qualifiedNameConverter.toQualifiedName(memberName))
};
val member = if(memberDesc!==null && !IEObjectDescriptionWithError.isErrorDescription(memberDesc)) {
memberDesc.getEObjectOrProxy()
};
if(member instanceof TMember && !member.eIsProxy) {
G |- (member as TMember) : var TypeRef memberTypeRef
val G2 = G.wrap
typeSystemHelper.addSubstitutions(G2,targetTypeRef)
G2.addThisType(targetTypeRef)
G2 |- memberTypeRef ~> T
} else if (targetTypeRef.dynamic) {
T = G.anyTypeRefDynamic
} else {
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef
}
} else if (targetTypeRef.dynamic) {
T = G.anyTypeRefDynamic
} else {
T = G.anyTypeRef // later: evaluate name if possible, we may even want to return smth like intersect(allProperties)
}
}
}
/* @see [N4JS] 6.1.7. Property Accessors */
rule typePropertyAccessExpression
G |- ParameterizedPropertyAccessExpression expr: TypeRef T
from {
{
// avoid loops in type inference
T = env(G, GUARD_TYPE_PROPERTY_ACCESS_EXPRESSION -> expr, TypeRef)
}
or
{
// record that we are inferring the type of expr
val G2 = G.wrap
G2.add(GUARD_TYPE_PROPERTY_ACCESS_EXPRESSION -> expr, G2.anyTypeRef)
G2 |- expr.target : var TypeRef receiverTypeRef
typeSystemHelper.addSubstitutions(G2,receiverTypeRef)
G2.addThisType(receiverTypeRef)
// add parameterization stemming from super types, e.g., "class C extends G<string>",
// in case of super or this literals
// TODO can/should this be moved to one of the #addSubstitutions() methods?
if (! (receiverTypeRef instanceof UnknownTypeRef)
&& (expr.target instanceof SuperLiteral || expr.target instanceof ThisLiteral)
) {
// we may be in Object Literals etc., as we also handle this, so check for failures:
var containingClass = EcoreUtil2.getContainerOfType(expr,N4ClassDeclaration)?.definedType;
if (containingClass instanceof TClass) {
// In case of static polyfill (filler), replace defined type with filled type:
if (containingClass.isStaticPolyfill) {
containingClass = containingClass.superClassRef?.declaredType
}
if (containingClass instanceof TClass) {
if (containingClass?.superClassRef!==null) {
typeSystemHelper.addSubstitutions(G2, containingClass.superClassRef)
}
}
}
}
val prop = expr.property;
var TypeRef propTypeRef;
if(prop instanceof TMethod && (prop as TMethod).isConstructor) {
// accessing the built-in constructor property ...
val TypeArgument ctorTypeArg = switch(receiverTypeRef) {
TypeTypeRef: // case "C.constructor"
G.functionTypeRef
ParameterizedTypeRef, BoundThisTypeRef: { // case "c.constructor" or "this.constructor"
val declType = if(receiverTypeRef instanceof BoundThisTypeRef) {
receiverTypeRef.actualThisTypeRef?.declaredType
} else {
receiverTypeRef.declaredType
};
val finalCtorSig = if(declType instanceof TClassifier) N4JSLanguageUtils.hasCovariantConstructor(declType);
if(finalCtorSig) {
declType.ref
} else if(declType!==null) {
TypeUtils.createWildcardExtends(declType.ref)
} else {
null // will boil down to UnknownTypeRef (see below)
}
}
};
propTypeRef = if(ctorTypeArg!==null) {
TypeUtils.createTypeTypeRef(ctorTypeArg, true)
} else {
TypeRefsFactory.eINSTANCE.createUnknownTypeRef
};
}
else if(receiverTypeRef.dynamic && prop!==null && prop.eIsProxy) {
// access to an unknown property of a dynamic type
propTypeRef = G.anyTypeRefDynamic;
}
else {
// TODO: Is wrapping really required here?
G2.wrap |- prop : propTypeRef
if(expr.parameterized) {
typeSystemHelper.addSubstitutions(G2,expr);
}
}
G2 |- propTypeRef ~> T
T = versionResolver.resolveVersion(T, receiverTypeRef);
if (expr.target instanceof SuperLiteral && T instanceof FunctionTypeExprOrRef ) { // super.foo(): this; cf. GHOLD-95
val F = T as FunctionTypeExprOrRef;
if ((T as FunctionTypeExprOrRef).returnTypeRef instanceof BoundThisTypeRef) {
var TypeRef rawT; G |~ expr ~> rawT;
val thisTypeRef = TypeUtils.enforceNominalTyping(rawT);
if (T instanceof FunctionTypeExpression && T.eContainer===null) { // avoid creation of new instance
val fte = T as FunctionTypeExpression
fte.returnTypeRef = TypeUtils.copyIfContained(thisTypeRef);
} else {
T = TypeUtils.createFunctionTypeExpression(null,
F.typeVars, F.fpars, thisTypeRef);
}
}
}
}
}
/* @see [N4JS] 6.1.8. The new Operator */
rule typeNewExpression G |- NewExpression e: TypeRef T
from {
G |- e.callee: T
if(T instanceof TypeTypeRef) {
T = typeSystemHelper.createTypeRefFromStaticType(G, T, e.typeArgs)
}
}
rule typeNewTarget G |- NewTarget nt : TypeRef T
from {
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef
}
/* @see [N4JS] 6.1.9. Function Calls */
rule typeCallExpression
G |- ParameterizedCallExpression expr: TypeRef T
from {
G |- expr.target : var TypeRef targetTypeRef
if (targetTypeRef instanceof FunctionTypeExprOrRef)
{
val F = targetTypeRef
val tFunction = F.functionType;
{
// avoid loops in type inference
val inferring = env(G, GUARD_TYPE_CALL_EXPRESSION -> expr, TypeRef)
G |- inferring ~> T
// TODO redesign GUARDs: instead of returning a preliminary (i.e. incorrect!) result, an error value should be returned
// but what error value to use?
// fail (-> does not work, because next 'or' block will be executed then!)
// T = TypesFactory.eINSTANCE.createUnknownTypeRef
// T = null
}
or
{
// record that we are inferring the type of expr
val G2 = G.wrap;
G2.add(GUARD_TYPE_CALL_EXPRESSION -> expr, F.returnTypeRef)
// get the return type of F
if(expr.eContainer instanceof AwaitExpression
&& expr.eContainmentFeature === N4JSPackage.eINSTANCE.getAwaitExpression_Expression()
&& tFunction!==null
&& AnnotationDefinition.PROMISIFIABLE.hasAnnotation(tFunction)) {
// special case: automatic @Promisify
// given a @Promisifiable function foo(), the following two should be equivalent:
// await foo() <=> await @Promisify foo()
T = promisifyHelper.extractPromisifiedReturnType(expr);
} else {
// standard case:
T = F.returnTypeRef ?: G.anyTypeRef;
}
typeSystemHelper.addSubstitutions(G2, expr, targetTypeRef);
G2 |- T ~> T
T = versionResolver.resolveVersion(T, F);
if (T instanceof BoundThisTypeRef && !(expr.receiver instanceof ThisLiteral || expr.receiver instanceof SuperLiteral)) {
// we've got something like "this[C]" as return type of a call expression of the form
// "new C().method()" but *not* "this.method()" or "super.method()"
// -> detach 'this'-type from its 'this'-context to make sure it won't be confused with the current
// 'this'-context or other this contexts, e.g.
// class C {
// method(): this {
// return new C().method(); // must lead to an error!
// }
// }
G2 |~ T /\ T // taking upper bound turns this[C] into C
}
}
} else if(targetTypeRef?.declaredType===G.functionType) {
T = G.anyTypeRef
} else if(targetTypeRef.dynamic) {
T = G.anyTypeRefDynamic
} else if(MigrationUtils.isMigrateCall(expr) && targetTypeRef instanceof UnknownTypeRef) {
// type unresolved 'migrate'-calls as any+
T = G.anyTypeRefDynamic
} else {
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef
}
}
rule typeArgument
G |- Argument arg : TypeRef T
from {
G |- arg.expression: T
}
/* @see [N4JS] 6.1.11. Postfix Expression */
axiom typePostfixExpression
G |- PostfixExpression e : G.numberTypeRef
/* @see [N4JS] 6.1.12. Unary Expression */
rule typeUnaryExpression
G |- UnaryExpression e: TypeRef T
from {
if((e.op===UnaryOperator.NEG || e.op===UnaryOperator.POS) && e.expression instanceof IntLiteral) {
// special case: negative/positive numeric literals with radix 10 (not for hexadecimal or octal literals!)
// (asymmetry of int32 range is taken care of in rule 'typeNumericLiteral')
G |- e.expression : T
} else {
// standard cases:
switch (e.op) {
case UnaryOperator.DELETE: T= G.booleanTypeRef
case UnaryOperator.VOID: T= G.undefinedTypeRef
case UnaryOperator.TYPEOF: T= G.stringTypeRef
case UnaryOperator.NOT: T= G.booleanTypeRef
default: // INC, DEC, POS, NEG, INV
T = G.numberTypeRef
}
}
}
/* @see [N4JS] 6.1.13. Multiplicative Expression */
axiom typeMultiplicativeExpression
G |- MultiplicativeExpression e : G.numberTypeRef
/* @see [N4JS] 6.1.14 Additive Expression */
rule typeAdditiveExpression
G |- AdditiveExpression expr : TypeRef T
from {
if (expr.op == AdditiveOperator.ADD) {
G |- expr.lhs: var TypeRef l
G |- expr.rhs: var TypeRef r
val lunknown = l instanceof UnknownTypeRef
val runknown = r instanceof UnknownTypeRef
if (lunknown && runknown) {
T = typeSystemHelper.createUnionType(G, G.numberTypeRef, G.stringTypeRef);
} else {
val lnum = G.isNumericOperand(l);
val rnum = G.isNumericOperand(r);
if (lnum && rnum) {
T = G.numberTypeRef
} else {
if ( (lunknown || runknown) && (lnum || rnum) ) {
// one is numeric, the other unknown -->
T = typeSystemHelper.createUnionType(G, G.numberTypeRef, G.stringTypeRef);
} else {
val lMayNum = lnum || G.containsNumericOperand(l) || G.isAny(l) || G.isSymbol(l);
val rMayNum = rnum || G.containsNumericOperand(r) || G.isAny(r) || G.isSymbol(r);
if (lMayNum && rMayNum) {
T = typeSystemHelper.createUnionType(G, G.numberTypeRef, G.stringTypeRef);
}
else {
T = G.stringTypeRef
}
}
}
}
} else {
T = G.numberTypeRef
}
}
/* @see [N4JS] 6.1.15. Bitwise Shift Expression */
axiom typeShiftExpression
G |- ShiftExpression e : G.numberTypeRef
/* @see [N4JS] 6.1.16. Relational Expression */
axiom typeRelationalExpression
G |- RelationalExpression e : G.booleanTypeRef
/* @see [N4JS] 6.1.17. Equality Expression */
axiom typeEqualityExpression
G |- EqualityExpression e : G.booleanTypeRef
/* @see [N4JS] 6.1.18. Binary Bitwise Expression */
axiom typeBinaryBitwiseExpression
G |- BinaryBitwiseExpression e : G.numberTypeRef
/* @see [N4JS] 6.1.19. Binary Logical Expression */
rule typeBinaryLogicalExpression
G |- BinaryLogicalExpression e : TypeRef T
from {
val lhs = e.lhs;
val rhs = e.rhs;
val lhsIsEmptyArrayLiteral = if(lhs instanceof ArrayLiteral) lhs.elements.isEmpty;
val rhsIsEmptyArrayLiteral = if(rhs instanceof ArrayLiteral) rhs.elements.isEmpty;
G |- e.lhs : var TypeRef L
G |- e.rhs : var TypeRef R
if(lhsIsEmptyArrayLiteral && R?.declaredType===G.arrayType) {
// case: [] || someArray
T = R;
} else if(rhsIsEmptyArrayLiteral && L?.declaredType===G.arrayType) {
// case: someArray || []
T = L;
} else {
T = typeSystemHelper.createUnionType(G, L,R)
}
}
/* @see [N4JS] 6.1.20. Conditional Expression */
rule typeConditionalExpression
G |- ConditionalExpression expr : TypeRef T
from {
G |- expr.trueExpression : var TypeRef left
G |- expr.falseExpression : var TypeRef right
T = typeSystemHelper.createUnionType(G, left, right)
}
/* @see [N4JS] 6.1.21. Assignment Expression */
rule typeAssignmentExpression
G |- AssignmentExpression expr: TypeRef T
from {
{
expr.op===AssignmentOperator.ASSIGN;
G |- expr.rhs: T
} or {
// see typeAdditiveExpression
expr.op===AssignmentOperator.ADD_ASSIGN
G |- expr.lhs: var ParameterizedTypeRef l
G |- expr.rhs: var ParameterizedTypeRef r
val lnum = l.declaredType == G.booleanType || G.isNumeric(l.declaredType);
val rnum = r.declaredType == G.booleanType || G.isNumeric(r.declaredType);
val undef = l.declaredType == G.undefinedType || l.declaredType == G.nullType
|| r.declaredType == G.undefinedType || r.declaredType == G.nullType;
!(lnum && rnum);
!(undef && (lnum || rnum));
T = G.stringTypeRef
} or {
// MUL_ASSIGN, DIV_ASSIGN, MOD_ASSIGN: 6.1.13. Multiplicative Expression
// SUB_ASSIGN, ADD_ASSIGN (of numbers/boolean): 6.1.14 Additive Expression
// SHL_ASSIGN, SHR_ASSIGN, USHR_ASSIGN: 6.1.15. Bitwise Shift Expression
// AND_ASSIGN, XOR_ASSIGN, OR_ASSIGN: 6.1.18. Binary Bitwise Expression
T = G.numberTypeRef
}
}
/* @see [N4JS] 6.1.22. Comma Expression */
rule typeCommaExpression
G |- CommaExpression e: TypeRef T
from {
G |- e.exprs.last: T
}
/* @see [N4JS] 6.2.4 Cast Expression */
rule typeCastExpression
G |- CastExpression e: TypeRef T
from {
T = e.targetTypeRef
}
/* This is needed to remove the ambiguity:
* N4ClassExpression is both a TypeDefininingElement and an Expression,
* thus the dispatcher needs the exact type.
* (same pattern as rules upperBoundFunctionTypeRef, lowerBoundFunctionTypeRef)
*/
rule typeN4ClassExpression
G |- N4ClassExpression e: TypeRef T
from {
T = this.applyRuleTypeTypeDefiningElement(G, _trace_, e).value;
}
/* This is needed to remove the ambiguity:
* FunctionExpression is both a TypeDefininingElement and an Expression,
* thus the dispatcher needs the exact type.
* (same pattern as rules upperBoundFunctionTypeRef, lowerBoundFunctionTypeRef)
*/
rule typeFunctionExpression
G |- FunctionExpression e: TypeRef T
from {
T = this.applyRuleTypeTypeDefiningElement(G, _trace_, e).value;
}
/* ----------------------------------------------------------------------
* AST nodes: statements
* -------------------------------------------------------------------- */
/** @see [N4JS] 7.1.1. Variable Statement */
rule typeVariableDeclaration
G |- VariableDeclaration vdecl: TypeRef T
from {
// note: keep this rule aligned with rules typeN4FieldDeclaration and typePropertyNameValuePair
if (vdecl.declaredTypeRef !== null) {
T = vdecl.declaredTypeRef
} else if(vdecl.eContainer instanceof BindingElement) {
// guard against infinite recursion, e.g. for vars like this:
// var [a,b] = b; / var {a,b} = b;
if(G.get(GUARD_VARIABLE_DECLARATION->vdecl.expression)===null) {
val G2 = G.wrap;
G2.add(GUARD_VARIABLE_DECLARATION->vdecl.expression,Boolean.TRUE);
// compute the value type at this location in the destructuring pattern
T = destructureHelper.getTypeOfVariableDeclarationInDestructuringPattern(G2,vdecl) ?: G.anyTypeRef;
}
else {
T = G.anyTypeRef
}
} else if(vdecl.eContainer instanceof ForStatement && (vdecl.eContainer as ForStatement).forOf) {
val forOfStmnt = vdecl.eContainer as ForStatement;
// we have a situation like this: for(var x of myList) { ... }
// --> infer type of 'x' to the first type argument of the type of 'myList'
if(G.get(GUARD_VARIABLE_DECLARATION->vdecl.eContainer)===null) {
val G2 = G.wrap;
G2.add(GUARD_VARIABLE_DECLARATION->vdecl.eContainer,Boolean.TRUE);
{
G2 |- forOfStmnt.expression : var TypeRef ofPartTypeRef
val elemType = destructureHelper.extractIterableElementType(G2, ofPartTypeRef)
elemType!==null
G2 |~ elemType /\ T // result will be upper bound of first type argument
}
or
{
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef
}
}
else {
T = G.anyTypeRef
}
} else if(vdecl.eContainer instanceof ForStatement && (vdecl.eContainer as ForStatement).forIn) {
// we have a situation like this: for(var x in obj) { ... }
// --> infer type of 'x' to string
T = G.stringTypeRef
} else if (vdecl.expression !== null) {
// guard against infinite recursion e.g. for vars like this:
// var x = x || {};
if(G.get(GUARD_VARIABLE_DECLARATION->vdecl.expression)===null) {
val G2 = G.wrap;
G2.add(GUARD_VARIABLE_DECLARATION->vdecl.expression,Boolean.TRUE);
// compute the expression type
G2 |- vdecl.expression: var TypeRef E
if( (E instanceof BoundThisTypeRef)
||
( (E instanceof TypeTypeRef)
&& ((E as TypeTypeRef).getTypeArg instanceof BoundThisTypeRef)
)
) {
// N.T.D. on purpose:
// in case of ThisTypeRefs, there should be no existential type-refs (so there is no use in getting rid of it :-/)
// as part of IDE-785 leave the BoundThisTypeRef in place for variables w/o defined type.
} else {
G2 |~ E /\ E // take upper bound to get rid of ExistentialTypeRef (if any)
}
if (E.declaredType===G.undefinedType || E.declaredType===G.nullType || E.declaredType===G.voidType) {
T = G.anyTypeRef
} else {
T = E
}
}
else {
T = G.anyTypeRef
}
} else {
T = G.anyTypeRef
}
if (jsVariantHelper.enforceDynamicTypes(vdecl)) {
T = typeSystemHelper.makeDynamic(T);
}
}
rule typeFormalParameter
G |- FormalParameter fpar: TypeRef T
from {
val fparTypeRef = fpar.declaredTypeRef;
if (fparTypeRef!==null) {
// check for valid cases of this in type of a fpar
if(
// case 1: structural this type in constructor
fparTypeRef instanceof ThisTypeRefStructural // no need to assert that we are in a constructor (we have a validation for that)
||
// case 2: this nested within a function type expression on an fpar
(fparTypeRef instanceof FunctionTypeExpression
&& (fparTypeRef as FunctionTypeExpression).eAllContents.filter(TFormalParameter).exists[currFpar|currFpar.typeRef instanceof ThisTypeRef])
) {
T = typeSystemHelper.bindAndSubstituteThisTypeRef(G, fparTypeRef, fparTypeRef);
} else {
// note: it's a bit cleaner to return the type from the TModule, if one was already determined
T = if(fpar?.definedTypeElement?.typeRef!==null) fpar.definedTypeElement.typeRef else fpar.declaredTypeRef
// T = fparTypeRef
}
}
else if (fpar.hasInitializerAssignment) {
if (fpar.initializer !== null) {
G |- fpar.initializer : var TypeRef E
T = typeSystemHelper.sanitizeTypeOfVariableFieldProperty(G, E);
} else {
T = G.anyTypeRef
}
}
else {
if (jsVariantHelper.enforceDynamicTypes(fpar)) {// e.g. plain ECMAScript
T = G.anyTypeRefDynamic
} else { // e.g., N4JS
T = env(G, fpar, TypeRef)
or
T = G.anyTypeRef
}
}
T = TypeUtils.wrapIfVariadic(G.getPredefinedTypes().builtInTypeScope, T, fpar);
}
axiom typeCatchVariable G |- CatchVariable catchVariable:
if (jsVariantHelper.enforceDynamicTypes(catchVariable)) {// e.g. plain ECMAScript
G.anyTypeRefDynamic
} else { // e.g. N4JS
G.anyTypeRef
}
axiom typeLocalArgumentsVariable G |- LocalArgumentsVariable lArgumentsVar : G.argumentsTypeRef
rule typeModuleNamespace
G |- ModuleNamespaceVirtualType t: TypeRef T
from {
T = t.createTypeRef
}
rule typeJSXElement
G |- JSXElement expr: TypeRef T
from {
val classifierReactElement = reactHelper.lookUpReactElement(expr);
if (classifierReactElement !== null) {
T = classifierReactElement.ref;
} else {
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef;
}
}
axiom typeMigrationContextVariable G |- MigrationContextVariable lmigrationContextVar : G.migrationContextTypeRef
// **********************************************************************
// Judgment subtype
// **********************************************************************
rule subtypeTypeArgument
G |- TypeArgument left <: TypeArgument right
from {
G |~ left /\ var TypeRef leftUpper
G |~ right \/ var TypeRef rightLower
if (leftUpper===left && rightLower===right) {
fail("Prevent endless loop, check rules, see subtypeTypeArgument")
} else {
G |- leftUpper <: rightLower
}
}
rule subtypeTypeRef
G |- TypeRef left <: TypeRef right
from {
// top type right
right.declaredType instanceof AnyType
// bottom type(s) left
|| left.declaredType instanceof NullType || left.declaredType instanceof UndefinedType
}
rule subtypeUnknownTypeRef_Left
G |- UnknownTypeRef left <: TypeRef right
from {
true
}
rule subtypeUnknownTypeRef_Right
G |- TypeRef left <: UnknownTypeRef right
from {
true
}
/*
* @see [N4JS] 4.3.3. Parameterized Types, also
* @see [N4JS] 4.15. Enums
*/
rule subtypeParameterizedTypeRef
G |- ParameterizedTypeRef leftOriginal <: ParameterizedTypeRef rightOriginal
from {
val left = G.getReplacement(leftOriginal)
val right = G.getReplacement(rightOriginal)
val leftDeclType = left.declaredType;
val rightDeclType = right.declaredType;
if (leftDeclType === null || rightDeclType === null) {
true;
} else if (leftDeclType.eIsProxy || rightDeclType.eIsProxy) {
true;
} else if (leftDeclType instanceof VoidType
|| rightDeclType instanceof VoidType) {
leftDeclType instanceof VoidType && rightDeclType instanceof VoidType
} else if (leftDeclType instanceof UndefinedType
|| (leftDeclType instanceof NullType && !(rightDeclType instanceof UndefinedType))
|| rightDeclType instanceof AnyType) {
true
} else if ((leftDeclType === G.intType && rightDeclType === G.numberType)
|| (leftDeclType === G.numberType && rightDeclType === G.intType)) {
true // int <: number AND number <: int (for now, int and number are synonymous)
} else if (leftDeclType instanceof TEnum && (rightDeclType===G.n4EnumType || rightDeclType===G.objectType)) { // @see [N4JS] 4.13. Enums
!AnnotationDefinition.STRING_BASED.hasAnnotation( leftDeclType )
} else if (leftDeclType instanceof TEnum && (rightDeclType===G.n4StringBasedEnumType || rightDeclType===G.stringType || rightDeclType===G.stringObjectType)) { // @see [N4JS] 4.15.2 String based Enums, IDEBUG-294
AnnotationDefinition.STRING_BASED.hasAnnotation( leftDeclType )
} else if (leftDeclType===G.n4StringBasedEnumType && (rightDeclType===G.stringType || rightDeclType===G.stringObjectType)) {
true
} else if (leftDeclType instanceof PrimitiveType && (leftDeclType as PrimitiveType).assignmentCompatible===rightDeclType) {
true
} else if (rightDeclType instanceof PrimitiveType && leftDeclType===(rightDeclType as PrimitiveType).assignmentCompatible) {
true
} else if (leftDeclType instanceof TInterface && !(rightDeclType instanceof TInterface) && (right.typingStrategy==TypingStrategy.NOMINAL)
&& !(rightDeclType===G.n4ObjectType || rightDeclType===G.objectType || rightDeclType===G.anyType)) {
false
} else {
var structuralTyping = false;
if (right.isUseSiteStructuralTyping()) { // e.g. foo(~A right){..} foo(left) --> left ~<: right
val result = typeSystemHelper.isStructuralSubtype(G, left, right);
if (! result.isValue) {
fail
error result.message
data PRIORITY_ERROR
}
structuralTyping = true;
} else if (right.isDefSiteStructuralTyping()) {
// avoid recursion
val guard = G.get(GUARD_SUBTYPE_PARAMETERIZED_TYPE_REF__STRUCT->(left->right)) as Boolean
if (guard===null || !guard) {
val result = typeSystemHelper.isStructuralSubtype(G, left, right);
if (! result.isValue) {
if (result.n4ObjectOnLeftWithDefSite &&
// check if left is declared/nominal subtype of right
{G.wrap, (GUARD_SUBTYPE_PARAMETERIZED_TYPE_REF__STRUCT->(left->right))<-true |- left <: right} ) {
structuralTyping = true;
} else {
fail
error result.message
data PRIORITY_ERROR
}
}
structuralTyping = result.isValue
}
}
if (! structuralTyping) { // do nominal, right is not a structural type
// enforce that ~T !<: S except for S = Object and T <: Object and for primitive types on the left
if( (left.isUseSiteStructuralTyping || left.isDefSiteStructuralTyping)
&& !(rightDeclType == G.objectType && leftDeclType instanceof TClassifier)
&& !(leftDeclType instanceof PrimitiveType))
{
fail
error "Structural type " + left.typeRefAsString + " is not a subtype of non-structural type " + right.typeRefAsString
data PRIORITY_ERROR
}
// nominal typing (the default behavior)
if (leftDeclType instanceof TypeVariable || rightDeclType instanceof TypeVariable) {
// we have a type variable on one or both sides
if (leftDeclType == rightDeclType) {
true
} else if(leftDeclType instanceof TypeVariable) {
// a case like: (T extends B) <: A (with T being an unbound type variable and A,B two classes with B <: A)
val ub = leftDeclType.declaredUpperBound ?: N4JSLanguageUtils.getTypeVariableImplicitUpperBound(G);
G |- ub <: right
} else {
// all other cases are always false, for example:
// B <: (T extends A) is always false, even if B <: A
// (T extends A) <: (S extends B) is always false, even if A === B (note the difference to existential types)
false
}
} else if (leftDeclType == rightDeclType) {
if (left.typeArgs.size>0 && left.typeArgs.size <= right.typeArgs.size) { // ignore raw types
val len = Math.min(Math.min(left.typeArgs.size, right.typeArgs.size), rightDeclType.typeVars.size);
for(var i=0;i<len;i++) {
val leftArg = left.typeArgs.get(i)
val rightArg = right.typeArgs.get(i)
val variance = rightDeclType.getVarianceOfTypeVar(i)
var TypeRef leftArgUpper; G |~ leftArg /\ leftArgUpper
var TypeRef leftArgLower; G |~ leftArg \/ leftArgLower
var TypeRef rightArgUpper; G |~ rightArg /\ rightArgUpper
var TypeRef rightArgLower; G |~ rightArg \/ rightArgLower
// guard against infinite recursion due to recursive implicit upper bounds, such as in
//
// class A<T extends A<?>> {}
//
// and
//
// class X<T extends B<?>> {}
// class Y<T extends X<?>> {}
// class B<T extends Y<?>> {}
//
var RuleEnvironment G2;
if(rightArg instanceof Wildcard && (rightArg as Wildcard).isImplicitUpperBoundInEffect) {
// we're dealing with implicit upper bounds -> need to guard against infinite loop
val isGuarded = G.get(GUARD_SUBTYPE_PARAMETERIZED_TYPE_REF__ARGS->(rightArg))!==null;
if(!isGuarded) {
// first time here for wildcard 'rightArg'
// -> continue as usual but add guard to rule environment
G2 = G.wrap;
G2.add(GUARD_SUBTYPE_PARAMETERIZED_TYPE_REF__ARGS->(rightArg), Boolean.TRUE);
} else {
// returned here for the same wildcard!
// -> ignore implicit upper bound on right-hand side to break infinite loop
rightArgUpper = G.topTypeRef;
G2 = G; // won't add another guard, so no need to wrap G
}
} else {
// not dealing with implicit upper bounds -> just continue as usual without guarding
G2 = G;
}
// TODO IDE-1653 reconsider this logic
// seems bogus because it allows G<?> <: G<E> (with E being a closed existential, i.e. ExistentialTypeRef)
// why not just require type equality, i.e. L<:R && R<:L (without taking upper/lower bounds) and let the
// subtype rules for WildCards / ExistentialTypeRefs do the heavy lifting?
{
// require leftArg <: rightArg, except we have contravariance
if(variance!=Variance.CONTRA) {
G2 |- leftArgUpper <: rightArgUpper
}
// require rightArg <: leftArg, except we have covariance
if(variance!=Variance.CO) {
G2 |- rightArgLower <: leftArgLower
}
}
or
{
if(previousFailure.isOrCausedByPriorityError) {
fail
error stringRep(left) + " is not a subtype of " + stringRep(right)
+ " due to incompatible type arguments: "
+ previousFailure.compileMessage
data PRIORITY_ERROR
}
else {
fail // with default message
}
}
}
}
} else {
val allSuperTypeRefs = if(leftDeclType instanceof ContainerType<?>) AllSuperTypeRefsCollector.collect(leftDeclType) else newArrayList;
val superTypeRefs = allSuperTypeRefs + G.collectAllImplicitSuperTypes(left);
// Note: rightDeclType might appear in superTypes several times in case of multiple implementation
// of the same interface, which is allowed in case of definition-site co-/contravariance.
// To support such cases without duplicating any logic, we will use judgment 'substTypeVariables' below.
if(superTypeRefs.exists[it.declaredType===rightDeclType]) {
// at this point we have 1..* type references in superTypeRefs with a declared type of rightDeclType
// (more than one possible in case of multiple implementation of the same interface, which is legal
// in case of definition-site co-/contravariance)
// (a) these type references may contain unbound type variables from lower level of the inheritance
// hierarchy and (b) in case of more than 1 type reference we have to combine them into a single
// type reference
// --> use type variable substitution on a synthetic type reference with a declared type of
// rightDeclType to solve all those cases without duplicating any logic:
val localG_left = G.wrap;
typeSystemHelper.addSubstitutions(localG_left,left);
val syntheticTypeRef = rightDeclType.ref(rightDeclType.typeVars.map[it.ref]);
localG_left |- syntheticTypeRef ~> var TypeRef effectiveSuperTypeRef // substitute type variables
G |- effectiveSuperTypeRef <: right
} else {
false
}
}
} // end nominal
}
}
/* @see [N4JS] 4.12. Union Type */
rule subtypeUnion_Left
G|- UnionTypeExpression U <: TypeRef S
from {
U.typeRefs.forall[T|
G |- T <: S
]
}
/* @see [N4JS] 4.12. Union Type */
rule subtypeUnion_Right
G|- TypeRef S <: UnionTypeExpression U
from {
U.typeRefs.exists[T|
G |- S <: T
]
}
// this rule is a copy of rule 'subtypeIntersection_Right'
// (only purpose is to give rule 'subtypeIntersection_Right' priority over rule 'subtypeIntersection_Left')
rule subtypeIntersection_LeftRight
G|- IntersectionTypeExpression S <: IntersectionTypeExpression I
from {
I.typeRefs.forall[T|
G |- S <: T
]
}
/* @see [N4JS] 4.13. Intersection Type */
rule subtypeIntersection_Left
G|- IntersectionTypeExpression I <: TypeRef S
from {
I.typeRefs.exists[T|
G |- T <: S
]
}
/* @see [N4JS] 4.13. Intersection Type */
rule subtypeIntersection_Right
G|- TypeRef S <: IntersectionTypeExpression I
from {
I.typeRefs.forall[T|
G |- S <: T
]
}
/*
* Inside a function, when this keyword is used, the this type declared in a method signature
* is not bound. This may happen only for structural this types used in constructor.
*/
rule subtypeBoundThisTypeRef
G |-BoundThisTypeRef left <: BoundThisTypeRef right
from {
// also see subtypeParameterizedTypeRef, simplified here as less cases are possible
if (right.isUseSiteStructuralTyping()) { // e.g. foo(~A right){..} foo(left) --> left ~<: right
val result = typeSystemHelper.isStructuralSubtype(G, left, right);
if (! result.isValue) {
fail
error result.message
data PRIORITY_ERROR
}
// def site structural typing not possible in N4Objects constructor
} else {
left.useSiteStructuralTyping === right.useSiteStructuralTyping
G |- left.actualThisTypeRef <: right.actualThisTypeRef
}
}
/*
* Bound this type is a subtype of the type to which it is bound.
*/
rule subtypeBoundThisTypeRefTypeRef
G |- BoundThisTypeRef boundThisTypeRef <: TypeRef right
from {
boundThisTypeRef === right
or
{
G |~ boundThisTypeRef /\ var TypeRef upperExt
G |- upperExt <: right
}
}
/*
* Bound this type is similar to an existential type, but it can only define an upper bound.
*/
rule subtypeTypeRefBoundThisTypeRef
G |- TypeRef left <: BoundThisTypeRef boundThisTypeRef
from {
left===boundThisTypeRef
or {
val leftType = left.declaredType
leftType===G.undefinedType || leftType===G.nullType
}
// or
// {
// val leftBound = left as BoundThisTypeRef
// G|- leftBound.actualThisTypeRef <: boundThisTypeRef.actualThisTypeRef
// G|- boundThisTypeRef.actualThisTypeRef <: boundThisTypeRef.actualThisTypeRef
// }
or {
if (boundThisTypeRef.isUseSiteStructuralTyping
|| (null !== boundThisTypeRef.actualThisTypeRef
&& null !== boundThisTypeRef.actualThisTypeRef.declaredType
&& boundThisTypeRef.actualThisTypeRef.declaredType.final)) {
// special cases: in the following cases, a subtype check X <: this[C] boils down to X <: C
// (1) if the rhs is structurally typed
// NOTE: structurally typed 'this' on rhs will only occur when type checking NewExpression
// arguments when referring to a constructor using [~]~this as parameter type.
// (2) if the actual type on rhs is final (i.e. if C in above example is final)
// -->
// instead of copying all the logic from rule subtypeParameterizedTypeRef,
// we simply create a corresponding ParameterizedTypeRef for the bound this type,
// i.e. we treat X <: [~]~this{C} like X <: [~]~C (in case 1)
// and X <: this{C} like X <: C (in case 2 with C being final)
val resolvedTypeRef = TypeUtils.createResolvedThisTypeRef(boundThisTypeRef);
G |- left <: resolvedTypeRef
} else {
fail
}
}
}
rule subtypeExistentialTypeRef_Right
G |- TypeRef left <: ExistentialTypeRef existentialTypeRef
from {
if(G.isExistentialTypeToBeReopened(existentialTypeRef)) {
// special case: open existential
// --> we may pick any valid type we want for 'existentialTypeRef'
// --> only check that 'left' lies within the bounds
// obtain wildcard from which existentialTypeRef was created
val wildThing = existentialTypeRef.wildcard;
// check upper and lower bounds
G |~ wildThing /\ var TypeRef upperBound
G |~ wildThing \/ var TypeRef lowerBound
G |- left <: upperBound
G |- lowerBound <: left
}
else {
// standard case: closed existential
// --> the type has been picked but we don't know it
// --> all we know is the picked type lies within the bounds
// --> subtype check succeeds if and only if: 'left'<:P for *ALL* types P that may have been picked for the existential
left===existentialTypeRef // performance tweak
or
{
left instanceof ParameterizedTypeRef &&
(left as ParameterizedTypeRef).declaredType instanceof NullType
}
or
{
G |~ existentialTypeRef \/ var TypeRef lowerExt
G |- left <: lowerExt
}
}
}
rule subtypeExistentialTypeRef_Left
G |- ExistentialTypeRef existentialTypeRef <: TypeRef right
from {
if(G.isExistentialTypeToBeReopened(existentialTypeRef)) {
// special case: open existential
// --> we may pick any valid type we want for 'existentialTypeRef'
// --> only check that 'left' lies within the bounds
// obtain wildcard from which existentialTypeRef was created
val wildThing = existentialTypeRef.wildcard;
// check upper and lower bounds
G |~ wildThing /\ var TypeRef upperBound
G |~ wildThing \/ var TypeRef lowerBound
G |- right <: upperBound
G |- lowerBound <: right
}
else {
// standard case: closed existential
// --> the type has been picked but we don't know it
// --> all we know is the picked type lies within the bounds
// --> subtype check succeeds if and only if: P<:'right' for *ALL* types P that may have been picked for the existential
existentialTypeRef===right // performance tweak
or
{
G |~ existentialTypeRef /\ var TypeRef upperExt
G |- upperExt <: right
}
}
}
rule subtypeTypeTypeRef
G |- TypeTypeRef left <: TypeTypeRef right
from {
val leftTypeArg = left.typeArg;
val rightTypeArg = right.typeArg;
val leftIsCtorRef = left.isConstructorRef;
val rightIsCtorRef = right.isConstructorRef;
val rightHasTypeRef = rightTypeArg instanceof TypeRef;
if(!leftIsCtorRef && rightIsCtorRef) { // type{} <: constructor{}
fail
} else if(rightHasTypeRef && !rightIsCtorRef) { // type|constructor{} <: type{} AND right doesn't contain wildcard
// check type arguments
G |- leftTypeArg <: rightTypeArg
} else if (rightHasTypeRef && rightIsCtorRef) { // constructor{} <: constructor{} AND right doesn't contain wildcard
val left_staticType = typeSystemHelper.getStaticType(G, left);
val right_staticType = typeSystemHelper.getStaticType(G, right);
val leftHasCovariantConstructor = left_staticType instanceof TClassifier
&& N4JSLanguageUtils.hasCovariantConstructor(left_staticType as TClassifier);
// left must not contain a wildcard, (closed) existential type, this type
// (except we have a @CovariantConstructor)
(
!(leftTypeArg instanceof Wildcard || leftTypeArg instanceof ExistentialTypeRef || leftTypeArg instanceof ThisTypeRef)
|| leftHasCovariantConstructor
)
// check type arguments
G |- leftTypeArg <: rightTypeArg
// check constructors
if( left_staticType instanceof TypeVariable || right_staticType instanceof TypeVariable) {
// special case: we have a type variable on one or both sides
// left <: right if and only if the type variables are identical
left_staticType === right_staticType
// (for a type variable itself we cannot get the constructor, we could only get the constructor
// of the upper/lower bound; however, in contrast to other type rules, we cannot say
// constructor{upperBound(T)} <: constructor{X} implies constructor{T} <: constructor{X}
// because the subtype relation for ConstructorTypeRefs is not transitive; so we need
// the identical type - in this case: the identical type variable - on both sides)
} else {
val leftCtor = containerTypesHelper.fromContext(G.contextResource).findConstructor(left_staticType as ContainerType<?>);
val rightCtor = containerTypesHelper.fromContext(G.contextResource).findConstructor(right_staticType as ContainerType<?>);
leftCtor!==null && rightCtor!==null
// we need the type of the two constructors (i.e. their signature)
// DO NOT USE "var leftCtorRef = TypeUtils.createTypeRef(leftCtor)", because this would by-pass the handling
// of forward references during AST traversal! Instead, obtain the type via the type judgment:
G |- leftCtor : var TypeRef leftCtorRef;
G |- rightCtor : var TypeRef rightCtorRef;
// support for type variables and [~]~this as type of fpars in constructors
val G_left = G.wrap;
val G_right = G.wrap;
typeSystemHelper.addSubstitutions(G_left, left_staticType.ref);
G_left.addThisType(left_staticType.ref)
typeSystemHelper.addSubstitutions(G_right, right_staticType.ref);
G_right.addThisType(right_staticType.ref)
G_left |- leftCtorRef ~> var TypeRef leftCtorRefSubst
G_right |- rightCtorRef ~> var TypeRef rightCtorRefSubst
G |- leftCtorRefSubst <: rightCtorRefSubst
}
} else { // any combination except type{} <: constructor{} AND right contains wildcard
G |~ leftTypeArg /\ var TypeRef upperBoundLeft
G |~ leftTypeArg \/ var TypeRef lowerBoundLeft
G |~ rightTypeArg /\ var TypeRef upperBoundRight
G |~ rightTypeArg \/ var TypeRef lowerBoundRight
G |- upperBoundLeft <: upperBoundRight
G |- lowerBoundRight <: lowerBoundLeft
}
}
/*
* Constructors are functions (also see GHOLD-227)
*/
rule subtypeTypeTypeRef__ParameterizedTypeRef
G |- TypeTypeRef left <: ParameterizedTypeRef right
from {
right.declaredType === G.anyType
||
right.declaredType === G.objectType
||
(
right.declaredType === G.functionType
&&
left.isConstructorRef
)
}
/* @see [N4JS] 4.7.2. Functions and FunctionType */
rule subtypeFunctionTypeExprOrRef
G |- FunctionTypeExprOrRef left <: FunctionTypeExprOrRef right
from {
typeSystemHelper.isSubtypeFunction(G,left,right)
}
// exact copy of rule subtypeFunctionTypeExprOrRef, but that's needed to avoid the ambiguity
// since a FunctionTypeRef is both a FunctionTypeExprOrRef and a ParametererizedTypeRef
/* @see [N4JS] 4.7.2. Functions and FunctionType */
rule subtypeFunctionTypeRef
G |- FunctionTypeRef left <: FunctionTypeRef right
from {
typeSystemHelper.isSubtypeFunction(G,left,right)
}
/*
* Function type expressions are functions
* (no such special rule required for FunctionTypeRef, because that is also a
* ParameterizedTypeRef and therefore it will be handled by rule subtypeParameterizedTypeRef)
*/
rule subtypeFunctionTypeExpression_ParameterizedTypeRef
G |- FunctionTypeExpression left <: ParameterizedTypeRef right
from {
if(right instanceof FunctionTypeExprOrRef) {
typeSystemHelper.isSubtypeFunction(G,left,right)
}
else {
right.declaredType instanceof AnyType
||
right.declaredType === G.functionType
}
}
// **********************************************************************
// Judgment supertype
// **********************************************************************
rule supertypeTypeRef
G |- TypeRef left :> TypeRef right
from {
G |- right <: left
}
// **********************************************************************
// Judgment equalTypeRef
// **********************************************************************
rule equalTypeTypeRef
G |- TypeRef left ~~ TypeRef right
from {
G |- left <: right
G |- right <: left
}
// **********************************************************************
// Judgment expectedTypeIn
// **********************************************************************
//This axiom is commented out because its logic is now delegated to the method expectedExpressionTypeInContainer of {@link UnsupportedExpressionTypeHelper}
//axiom expectedTypeNone
// G |- EObject o |> Expression e : (null as TypeRef) // null is the default and means 'no type expectation' (even void is allowed!)
rule expectedTypeInFormalParameter
G |- FormalParameter formalParam |> Expression initializer: TypeRef T
from {
if (formalParam.declaredTypeRef !== null) {
T = formalParam.declaredTypeRef;
} else if (formalParam.definedTypeElement?.typeRef !== null) {
// Other than in expectedTypeOfRightSideInVariableDeclaration,
// here we need to return the typeRef of the TModule, since it
// might have been inferred by the PolyProcessor.
T = formalParam.definedTypeElement.typeRef;
} else {
T = G.anyTypeRef;
}
}
rule expectedTypeOfArgument
G |- Argument argument |> Expression argumentExpression: TypeRef T
from {
val expr = argument.eContainer;
if(expr instanceof NewExpression) {
// ############################################################################################################
// CODE FROM OLD RULE expectedTypeOfArgumentInNewExpression:
// since the callee is contained in the NewExpression
// the validator calls this method also passing the callee as argument
if(!expr.arguments.contains(argument)) {
// expected type of the callee
// callee must be any constructor
// --> no type available for this
// --> must be checked as a validation
}
else {
// expected type of argument
// compute ctor
G |- expr.callee : var TypeTypeRef ctorTypeRef
// add type variable mappings based on the type
// of the instance to be created
// --> for this, create a ParameterizedTypeRef taking the staticType from
// the CtorTypeRef and the type arguments from the NewExpression
var typeRefOfInstanceToCreate = typeSystemHelper.createTypeRefFromStaticType(G, ctorTypeRef, expr.typeArgs)
var typeOfInstanceToCreate = typeRefOfInstanceToCreate.declaredType as ContainerType<?>
val G2 = G.wrap
typeSystemHelper.addSubstitutions(G2,typeRefOfInstanceToCreate)
G2.addThisType(typeRefOfInstanceToCreate) // required if we refer to a ctor with a parameter of type [~]~this (esp. default ctor)
var TMethod ctor = containerTypesHelper.fromContext(expr.eResource).findConstructor(typeOfInstanceToCreate)
val fpar = ctor?.getFparForArgIdx(ECollections.indexOf(expr.arguments, argument, 0));
if (fpar===null) {
// consequential error or ignored:
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef;
} else {
val paramType = fpar.typeRef;
if (paramType===null) {
T = G2.anyTypeRef
} else {
// bind type variables in function type's parameter type
G2 |- paramType ~> T
}
}
}
// ############################################################################################################
} else if(expr instanceof ParameterizedCallExpression) {
// ############################################################################################################
// CODE FROM OLD RULE expectedTypeOfArgumentInCallExpression:
// since the target is contained in the CallExpression
// the validator calls this method also passing the target as argument
// but this rule is intended only for arguments of the call expression
expr.arguments.contains(argument)
G |- expr.target : var TypeRef targetTypeRef
if (targetTypeRef instanceof FunctionTypeExprOrRef)
{
val F = targetTypeRef
val argIndex = ECollections.indexOf(expr.arguments, argument, 0);
val fpar = F.getFparForArgIdx(argIndex);
if (fpar===null) {
// consequential error or ignored:
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef;
} else {
val paramType = fpar.typeRef;
if (paramType===null) {
T = G.anyTypeRef
} else {
val G2 = G.wrap;
typeSystemHelper.addSubstitutions(G2, expr, F);
if(expr.target instanceof SuperLiteral) {
// required only in case of super(...) referring to a ctor with a parameter of type [~]~this (esp. default ctor)
// although we must be inside a TClass, implemented robustly in order to minimize errors
// TODO can/should this be moved to one of the #addSubstitutions() methods?
val containingClass = EcoreUtil2.getContainerOfType(expr,N4ClassDeclaration)?.definedType;
if (containingClass instanceof TClass) {
G2.addThisType(containingClass.ref)
if (containingClass.superClassRef!==null) {
typeSystemHelper.addSubstitutions(G2, containingClass.superClassRef)
}
// IDEBUG-262 special handling of spec-style constructor fpars:
if (paramType instanceof ThisTypeRefStructural) {
// needs this-type Binding to parent-class
G2.addThisType(containingClass.superClassRef)
}
}
}
// bind type variables in function type's parameter type
G2 |- paramType ~> T
}
}
}
else {
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef
}
// ############################################################################################################
}
}
/* @see [N4JS] 6.1.11. Postfix Expression */
rule expectedTypeInPostfixExpression
G |- PostfixExpression e |> Expression expression : TypeRef T
from {
if (jsVariantHelper.isTypeAware(e)) {
T = G.numberTypeRef
} else {
T = G.anyTypeRef
}
}
/* @see [N4JS] 6.1.12. Unary Expression */
rule expectedTypeInUnaryExpression
G |- UnaryExpression e |> Expression expression : TypeRef T
from {
T =
if ( jsVariantHelper.isTypeAware(e) ) { // e.g. in N4JS
switch (e.op) {
case UnaryOperator.DELETE: TypeUtils.createNonSimplifiedUnionType(G.anyTypeRef,G.voidTypeRef)
case UnaryOperator.VOID: TypeUtils.createNonSimplifiedUnionType(G.anyTypeRef,G.voidTypeRef)
case UnaryOperator.TYPEOF: TypeUtils.createNonSimplifiedUnionType(G.anyTypeRef,G.voidTypeRef)
case UnaryOperator.INC: G.numberTypeRef
case UnaryOperator.DEC: G.numberTypeRef
case UnaryOperator.POS: G.numberTypeRef
case UnaryOperator.NEG: G.numberTypeRef
case UnaryOperator.INV: G.numberTypeRef
case UnaryOperator.NOT: G.anyTypeRef
default: G.anyTypeRef
}
} else { // e.g. plain ECMAScript
switch (e.op) {
case UnaryOperator.DELETE: TypeUtils.createNonSimplifiedUnionType(G.anyTypeRef,G.voidTypeRef)
case UnaryOperator.VOID: TypeUtils.createNonSimplifiedUnionType(G.anyTypeRef,G.voidTypeRef)
case UnaryOperator.TYPEOF: TypeUtils.createNonSimplifiedUnionType(G.anyTypeRef,G.voidTypeRef)
default: G.anyTypeRef
}
}
}
/* @see [N4JS] 6.1.13. Multiplicative Expression
* Note: symbol type is not allowed, this is checked in validation
*/
rule expectedTypeInMultiplicativeExpression
G |- MultiplicativeExpression e |> Expression expression : TypeRef T
from {
T = G.anyTypeRef
}
/* @see [N4JS] 6.1.14 Additive Expression
* Note: symbol type is not allowed, this is checked in validation
*/
rule expectedTypeInAdditiveExpression
G |- AdditiveExpression e |> Expression expression : TypeRef T
from {
T = G.anyTypeRef
}
/* @see [N4JS] 6.1.15. Shift Expression
* Note: symbol type is not allowed, this is checked in validation
* */
rule expectedTypeInShiftExpression
G |- ShiftExpression e |> Expression expression : TypeRef T
from {
T = G.anyTypeRef
}
/* @see [N4JS] 6.1.16. Relational Expression */
rule expectedTypeInRelationalExpression
G |- RelationalExpression e |> Expression expression : TypeRef T
from {
switch (e.op) {
case RelationalOperator.INSTANCEOF: {
if (expression===e.rhs) {
T = TypeUtils.createNonSimplifiedUnionType(G.functionTypeRef,
G.objectTypeRef.createTypeTypeRef(false), G.n4EnumTypeRef.createTypeTypeRef(false))
} else {
T = G.anyTypeRef
}
}
case RelationalOperator.IN: {
if (expression===e.rhs) {
T = G.objectTypeRef
} else { // lhs:
if (jsVariantHelper.isTypeAware(e)) {
T = TypeUtils.createNonSimplifiedUnionType(G.numberTypeRef, G.stringTypeRef);
} else {
T = G.anyTypeRef
}
}
}
default: { // <, <=, >, >=
if (jsVariantHelper.isTypeAware(e)) {
// TODO this looks expensive...
val primsTR = TypeUtils.createNonSimplifiedUnionType(G.numberTypeRef, G.stringTypeRef, G.booleanTypeRef);
val otherSide = if (expression===e.lhs) { e.rhs; } else { e.lhs; }
G |- otherSide : var TypeRef otherSideTR
if ( { G |- otherSideTR <: primsTR } && ! { G |- otherSideTR <: G.nullTypeRef } ) {
T = otherSideTR;
} else {
T = primsTR;
}
} else {
T = G.anyTypeRef
}
}
}// end switch
}
/*
* Always returns any. Note that is is not possible to require both sides to have the same type,
* as e.g., in {@code Object === any} the concrete types for both sides could be subtypes
* of {@code Object}.
*
* However, in the validation there should be a warning created if the two types are not
* compatible, that is, if neither lhs &lt;: rhs nor rhs &lt;: lhs.
*
* @see [N4JS] 6.1.17. Equality Expression
* */
axiom expectedTypeInEqualityExpression
G |- EqualityExpression e |> Expression expression : G.anyTypeRef
/* @see [N4JS] 6.1.18. Binary Bitwise Expression */
rule expectedTypeInBinaryBitwiseExpression
G |- BinaryBitwiseExpression e |> Expression expression : TypeRef T
from {
if (jsVariantHelper.isTypeAware(e)) {
T = G.numberTypeRef
} else {
T = G.anyTypeRef
}
}
/* @see [N4JS] 6.1.19. Binary Logical Expression */
axiom expectedTypeInBinaryLogicalExpression
G |- BinaryLogicalExpression e |> Expression expression : G.anyTypeRef
/* @see [N4JS] 6.1.21. Assignment Expression */
rule expectedTypeInAssignmentExpression
G |- AssignmentExpression expr |> Expression operand : TypeRef T
from {
{
! jsVariantHelper.isTypeAware(expr)
if (operand===expr.lhs) {
// left-hand side:
T = G.bottomTypeRef // no expectation
} else {
// right-hand side:
T = G.topTypeRef // no expectation
}
}
or
{
DestructureUtils.isTopOfDestructuringAssignment(expr)
if (operand===expr.lhs) {
// left-hand side:
T = G.bottomTypeRef // no expectation
} else {
// right-hand side:
// we do have an expectation of Iterable<?> (for array destructuring) and Object (for object destructuring), but
// this is checked in N4JSDestructureValidator in separate code to better support nesting of destructuring patterns
T = G.topTypeRef // no expectation
}
}
or
{
expr.op===AssignmentOperator.ASSIGN;
if (operand===expr.lhs) {
// left-hand side:
T = G.bottomTypeRef // no expectation
} else {
// right-hand side:
// right-hand side is expected to be of same type (or subtype) as left-hand side
G |- expr.lhs : T // note: this gives us the type for write access on LHS
}
}
or
{
expr.op===AssignmentOperator.ADD_ASSIGN
if (operand===expr.lhs) {
// left-hand side:
// NOTE: we would actually have to return two different types here:
// 1) the expected type for write access (for storing the result of the add operation)
// 2) the expected type for read access (to obtaining the first operand of the add operation)
// Usually these types are identical, but can be different if we have a getter/setter pair.
// For consistency with the type judgment, we here only return the expected type for write
// access (case 1 above); case 2 will be handled by a special validation method, see
// org.eclipse.n4js.validation.validators.N4JSTypeValidator#checkCompoundAssignmentGetterSetterClashOnLhs(AssignmentExpression)
T = TypeUtils.createNonSimplifiedIntersectionType(G.numberTypeRef, G.stringTypeRef);
} else {
// right hand side:
G |- expr.lhs : var ParameterizedTypeRef lhsTypeRef
if (lhsTypeRef.declaredType === G.stringType) {
T = G.anyTypeRef // assume string concatenation: any type can be turned into a string
} else if(G.isNumeric(lhsTypeRef.declaredType)) {
T = G.numberTypeRef
} else {
// assume string concatenation, so same as for string above
T = G.anyTypeRef
}
}
}
or
{
// MUL_ASSIGN, DIV_ASSIGN, MOD_ASSIGN: 6.1.13. Multiplicative Expression
// SUB_ASSIGN, ADD_ASSIGN (of numbers/boolean): 6.1.14 Additive Expression
// SHL_ASSIGN, SHR_ASSIGN, USHR_ASSIGN: 6.1.15. Bitwise Shift Expression
// AND_ASSIGN, XOR_ASSIGN, OR_ASSIGN: 6.1.18. Binary Bitwise Expression
// in principle, we have the same distinction of cases as above,
// but because the type is 'number' in all cases (LHS, RHS, etc.)
// it all boils down to:
T = G.numberTypeRef
}
}
rule expectedTypeOfRightSideInVariableDeclaration
G |- VariableDeclaration vdecl |> Expression rhs : TypeRef T
from {
// only an explicitly declared type may serve as expected type
// (inferred types do not introduce a type expectation because they were derived from the initializer expression, etc.)
if (vdecl.declaredTypeRef!==null) {
T = vdecl.declaredTypeRef
}
else {
T = G.topTypeRef // no expectation
}
}
rule expectedTypeOfRightSideInVariableBinding
G |- VariableBinding binding |> Expression initExpr : TypeRef T
from {
// we do have an expectation of Iterable<?> (for array destructuring) and Object (for object destructuring), but
// this is checked in N4JSDestructureValidator in separate code to better support nesting of destructuring patterns
T = G.topTypeRef // no expectation
}
rule expectedTypeOfRightSideInN4FieldDeclaration
G |- N4FieldDeclaration fdecl |> Expression rhs : TypeRef T
from {
if (fdecl.declaredTypeRef!==null) {
T = fdecl.declaredTypeRef
}
else {
T = G.topTypeRef // no expectation
}
}
rule expectedTypeOfRightSideInPropertyNameValuePair
G |- PropertyNameValuePair pnvp |> Expression rhs : TypeRef T
from {
if (pnvp.declaredTypeRef!==null) {
T = pnvp.declaredTypeRef
}
else {
T = G.topTypeRef // no expectation
}
}
/* @see [N4JS] 7.1.4. Return Statement */
rule expectedTypeInReturnStatement
G |- ReturnStatement stmt |> Expression expression : TypeRef T
from {
T = typeSystemHelper.getExpectedTypeOfReturnValueExpression(G, expression);
}
/* @see [N4JS] 6.3.1. Generator Functions */
rule expectedTypeInYieldStatement
G |- YieldExpression yieldExpr |> Expression expression : TypeRef T
from {
G |- expression : var TypeRef exprTypeRef;
T = typeSystemHelper.getExpectedTypeOfYieldValueExpression(G, yieldExpr, exprTypeRef);
}
/*
* Same as rule 'expectedTypeInReturnStatement', but for the special case of an implicit return in a single-expression
* arrow function.
*/
rule expectedTypeInExpressionStatement
G |- ExpressionStatement exprStmnt |> Expression expression : TypeRef T
from {
if (N4JSASTUtils.getContainingSingleExpressionArrowFunction(expression)!==null) {
// special case: we are in a single-expression arrow function
// --> so 'expression' is to be treated as if it were the expression of a return statement
T = typeSystemHelper.getExpectedTypeOfReturnValueExpression(G, expression);
} else {
// default case:
T = null; // null means: no type expectation
}
}
/* @see [N4JS] 9.2.1. Constraints 111.1 */
rule expectedTypeInForStatement
G |- ForStatement forStmnt |> Expression expression : TypeRef T
from {
if(forStmnt.forOf && expression === forStmnt.expression) {
// expected type of the 'of' part in a for...of statement:
// is "Iterable<?>" and *not* "union{Iterable<?>,Iterator<?>}"
// (see http://www.2ality.com/2013/06/iterators-generators.html:
// "ECMAScript 6 has a new loop, for-of. That loop works with iterables.
// Before we can use it with createArrayIterator(), we need to turn the
// result into an iterable.")
val wildThing = TypeRefsFactory.eINSTANCE.createWildcard;
if(DestructureUtils.isTopOfDestructuringForStatement(forStmnt)) {
// special case: destructuring pattern in for..of
// e.g. for(var [a,b] of myListOfArrays) {}
// -> do nothing (keep wildThing on the default)
}
else {
// obtain expected element type
// note:
// - if the variable is declared in the for..of, ONLY derive an expected upper bound if the var-decl has a declared type
// - if the variable is declared outside, we always derive an expected upper bound from the variable type (declared or inferred)
val varDeclInFor = // case: for(var x of myList) {}
if(!forStmnt.varDecl.^empty) {
forStmnt.varDecl.get(0); // note: syntax does not allow more than 1 varDecl here
};
val varDeclOutside = // case: var x; for(x of myList) {}
if(forStmnt.initExpr instanceof IdentifierRef && (forStmnt.initExpr as IdentifierRef).id instanceof VariableDeclaration) {
(forStmnt.initExpr as IdentifierRef).id as VariableDeclaration
};
if(varDeclInFor?.declaredTypeRef!==null || varDeclOutside!==null) {
val varDecl = if(varDeclOutside!==null) varDeclOutside else varDeclInFor;
G |- varDecl : var TypeRef varTypeRef
wildThing.declaredUpperBound = TypeUtils.copyIfContained(varTypeRef);
}
}
T = G.iterableTypeRef(wildThing)
} else if (forStmnt.forIn && expression === forStmnt.expression) {
// expected type of the 'in' part in a for...in statement: object
T = TypeUtils.createNonSimplifiedUnionType(G.objectTypeRef, G.stringTypeRef, G.argumentsTypeRef);
}
}
// await expecting any type of value (need not be a Promise<?,?>)
rule expectedTypeInAwaitExpression
G |- AwaitExpression await |> Expression expr : TypeRef T
from {
if(promisifyHelper.isAutoPromisify(await)) {
// special case: short syntax of
T = null // no type expectation at all (even void is acceptable)
} else {
T = G.anyTypeRef
}
}
rule expectedTypeInJSXPropertyAttribute
G |- JSXPropertyAttribute container |> Expression expr: TypeRef T
from {
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef;
val jsxElem = container.eContainer();
if (jsxElem instanceof JSXElement) {
val propsTypeRef = reactHelper.getPropsType(jsxElem);
if (propsTypeRef !== null) {
val G2 = G.wrap;
typeSystemHelper.addSubstitutions(G2, propsTypeRef);
G2.addThisType(propsTypeRef);
G2 |- container.getProperty() : var TypeRef propertyTypeRef
G2 |- propertyTypeRef ~> T
}
}
}
// **********************************************************************
// Judgment upperBound (extends)
// **********************************************************************
/* @see [N4JS] 3.2 Type Variables */
axiom upperBoundTypeRef
G |~ TypeRef typeRef /\ typeRef
/* @see [N4JS] 3.2 Type Variables */
rule upperBoundWildcardTypeRef
G |~ Wildcard wildcard /\ TypeRef T
from {
val ub = wildcard.getDeclaredOrImplicitUpperBound();
if (ub !==null) {
T=ub
} else {
T=G.anyTypeRef
}
}
/* @see [N4JS] 3.3 Parameterized Types */
rule upperBoundExistentialTypeRef
G |~ ExistentialTypeRef existentialTypeRef /\ TypeRef T
from {
G |~ existentialTypeRef.wildcard /\ T
// retain undef- & nullable-modifier
T = TypeUtils.copy( T )
TypeUtils.copyTypeModifiers(T, existentialTypeRef)
}
/* @see [N4JS] 4.10, Constraints 22 */
rule upperBoundUnionTypeExpression
G |~ UnionTypeExpression U /\ TypeRef T
from {
T = TypeUtils.createNonSimplifiedUnionType(
U.typeRefs.map[var TypeRef E; G|~ it /\ E; E]
)
TypeUtils.copyTypeModifiers(T,U)
}
/* @see [N4JS] 4.11, Constraints 28 */
rule upperBoundIntersectionTypeExpression
G |~ IntersectionTypeExpression I /\ TypeRef T
from {
T = TypeUtils.createNonSimplifiedIntersectionType(
I.typeRefs.map[var TypeRef E; G|~ it /\ E; E]
)
TypeUtils.copyTypeModifiers(T,I)
}
rule upperBoundParameterizedTypeRef
G |~ ParameterizedTypeRef ptr /\ TypeRef T
from {
if (ptr.declaredType instanceof TypeVariable) {
T = ptr // do not return the declaredUpperBounds here! (a type variable is not an existential type)
} else {
T = ptr
}
}
// required due to multiple inheritance (see rule: substTypeVariablesInFunctionTypeRef)
rule upperBoundFunctionTypeRef
G |~ FunctionTypeRef F /\ TypeRef T
from {
T = this.applyRuleUpperBoundFunctionTypeExprOrRef(G, _trace_, F).value;
}
rule upperBoundFunctionTypeExprOrRef
G |~ FunctionTypeExprOrRef F /\ TypeRef T
from {
T = typeSystemHelper.createUpperBoundOfFunctionTypeExprOrRef(G,F);
}
/* @see [N4JS] 4.15. This Type */
rule upperBoundThisTypeRef
G |~ BoundThisTypeRef boundThisTypeRef /\ TypeRef T
from {
T = TypeUtils.createResolvedThisTypeRef(boundThisTypeRef)
TypeUtils.copyTypeModifiers(T, boundThisTypeRef)
}
rule upperBoundTypeTypeRef
G |~ TypeTypeRef ct /\ TypeRef T
from {
val typeArg = ct.typeArg;
if(typeArg instanceof BoundThisTypeRef) {
val typeArgNew = TypeUtils.createResolvedThisTypeRef(typeArg);
T = TypeUtils.createTypeTypeRef(typeArgNew, ct.isConstructorRef);
} else {
T = ct;
}
}
// **********************************************************************
// Judgment lowerBound (super)
// **********************************************************************
/* @see [N4JS] 3.2 Type Variables */
axiom lowerBoundTypeRef
G |~ TypeRef typeRef \/ typeRef
/* @see [N4JS] 3.2 Type Variables */
rule lowerBoundWildcard
G |~ Wildcard wildcard \/ TypeRef T
from {
if (wildcard.declaredLowerBound !==null) {
T = wildcard.declaredLowerBound
} else {
T = G.bottomTypeRef
}
}
/* @see [N4JS] 3.3 Parameterized Types */
rule lowerBoundExistentialTypeRef
G |~ ExistentialTypeRef existentialTypeRef \/ TypeRef T
from {
G |~ existentialTypeRef.wildcard \/ T
// retain undef- & nullable-modifier
T = TypeUtils.copy( T )
TypeUtils.copyTypeModifiers(T, existentialTypeRef)
}
/* @see [N4JS] 4.10, Constraints 22 */
rule lowerBoundUnionTypeExpression
G |~ UnionTypeExpression U \/ TypeRef T
from {
T = TypeUtils.createNonSimplifiedUnionType(
U.typeRefs.map[var TypeRef E; G|~ it \/ E; E]
)
TypeUtils.copyTypeModifiers(T,U)
}
/* @see [N4JS] 4.11, Constraints 28 */
rule lowerBoundIntersectionTypeExpression
G |~ IntersectionTypeExpression I \/ TypeRef T
from {
T = TypeUtils.createNonSimplifiedIntersectionType(
I.typeRefs.map[var TypeRef E; G|~ it \/ E; E]
)
TypeUtils.copyTypeModifiers(T,I)
}
rule lowerBoundParameterizedTypeRef
G |~ ParameterizedTypeRef ptr \/ TypeRef T
from {
if (ptr.declaredType instanceof TypeVariable) {
T = ptr
} else {
T = ptr
}
}
// required due to multiple inheritance (see rule: substTypeVariablesInFunctionTypeRef)
rule lowerBoundFunctionTypeRef
G |~ FunctionTypeRef F \/ TypeRef T
from {
T = this.applyRuleLowerBoundFunctionTypeExprOrRef(G, _trace_, F).value
}
rule lowerBoundFunctionTypeExprOrRef
G |~ FunctionTypeExprOrRef F \/ TypeRef T
from {
T = typeSystemHelper.createLowerBoundOfFunctionTypeExprOrRef(G,F);
}
/* @see [N4JS] 4.15. This Type */
rule lowerBoundThisTypeRef
G |~ BoundThisTypeRef boundThisTypeRef \/ TypeRef T
from {
T = G.undefinedTypeRef
TypeUtils.copyTypeModifiers(T, boundThisTypeRef)
}
// **********************************************************************
// Type variable substitution
// **********************************************************************
axiom substTypeVariablesBaseCase
G |- TypeArgument type ~> type
rule substTypeVariablesWildcard
G |- Wildcard wildcard ~> Wildcard T
from {
var ub = wildcard.declaredUpperBound;
if(ub!==null) {
G |- ub ~> ub
}
var lb = wildcard.declaredLowerBound;
if(lb!==null) {
G |- lb ~> lb
}
if(ub!==wildcard.declaredUpperBound || lb!==wildcard.declaredLowerBound) {
T = TypeUtils.copy(wildcard);
T.declaredUpperBound = TypeUtils.copyIfContained(ub);
T.declaredLowerBound = TypeUtils.copyIfContained(lb);
}
else {
T = wildcard;
}
}
rule substTypeVariablesThisTypeRef
G |- ThisTypeRef thisTypeRef ~> ThisTypeRef T
from {
{
val BoundThisTypeRef boundRefFromEnv = G.getThisType() as BoundThisTypeRef;
if (boundRefFromEnv !== null) {
val boundRef = TypeUtils.createBoundThisTypeRef(boundRefFromEnv.actualThisTypeRef);
boundRef.setTypingStrategy(thisTypeRef.typingStrategy); // note: must take use-site typing-strategy from 'thisTypeRef', not the one stored in the environment
TypeUtils.copyTypeModifiers(boundRef, thisTypeRef); // note: must take use-site type modifiers (e.g. optional)
T = boundRef;
} else {
T = thisTypeRef
}
}
or
{
T = thisTypeRef
}
}
rule substTypeVariablesThisTypeRefStructural
G |- ThisTypeRefStructural thisTypeRef ~> ThisTypeRef T
from {
{
val BoundThisTypeRef boundRefFromEnv = G.getThisType() as BoundThisTypeRef;
val boundRef = TypeUtils.createBoundThisTypeRefStructural(boundRefFromEnv.actualThisTypeRef, thisTypeRef);
TypeUtils.copyTypeModifiers(boundRef, thisTypeRef); // note: must take use-site type modifiers (e.g. optional)
T = boundRef;
}
or
{
T = thisTypeRef
}
}
/* This is needed to remove the ambiguity:
* FunctionTypeRef is both a FunctionTypeExprOrRef and a ParameterizedTypeRef,
* thus the dispatcher needs the exact type.
* (same pattern as rules upperBoundFunctionTypeRef, lowerBoundFunctionTypeRef)
*/
rule substTypeVariablesInFunctionTypeRef
G |- FunctionTypeRef typeRef ~> TypeRef result
from {
result = this.applyRuleSubstTypeVariablesInFunctionTypeExprOrRef(G, _trace_, typeRef).value as TypeRef;
}
rule substTypeVariablesInFunctionTypeExprOrRef
G |- FunctionTypeExprOrRef typeRef ~> TypeRef result
from {
result = typeSystemHelper.createSubstitutionOfFunctionTypeExprOrRef(G,typeRef);
}
rule substTypeVariablesInComposedTypeRef
G |- ComposedTypeRef typeRef ~> ComposedTypeRef result
from {
var boolean haveReplacement = false;
val substTypeRefs = newArrayList
for(currTypeRef : typeRef.typeRefs) {
G |- currTypeRef ~> var TypeRef substTypeRef
substTypeRefs.add(substTypeRef);
haveReplacement = haveReplacement || substTypeRef!==currTypeRef;
}
if(haveReplacement) {
result = TypeUtils.copy(typeRef);
result.typeRefs.clear();
result.typeRefs.addAll(TypeUtils.copyAll(substTypeRefs));
}
else {
result = typeRef;
}
}
/**
* Substitute in TypeTypeRef.
*/
rule substTypeVariablesInTypeTypeRef
G |- TypeTypeRef typeRef ~> TypeTypeRef result
from {
// Substitute in Parameterized Typeref.
G |- typeRef.getTypeArg ~> var TypeArgument tResult
if( typeRef.getTypeArg !== tResult ) {
// changes.
tResult = TypeUtils.copyIfContained( tResult )
result = TypeUtils.copyIfContained( typeRef )
result.typeArg = tResult
} else {
// nothing changed.
result = typeRef
}
}
rule substTypeVariablesInParameterizedTypeRef
G |- ParameterizedTypeRef typeRef ~> TypeRef result
from {
// (1) start with unchanged typeRef as result (will be copied and changed below if needed)
result = typeRef
// (2) substitute type variables in declared type
// TODO GHOLD-43 clean up the following (simplify + resolve redundancy with handling of recursive mappings in GenericsComputer#addSubstitutions())
if (typeRef.declaredType instanceof TypeVariable) {
val typeVar = typeRef.declaredType as TypeVariable
{
// ---- NOTE: this code was copied in rule 'substTypeVariablesInClassifierTypeRef' above (keep in sync!) ----
// do we have a substitution?
val tempFromEnv = env(G, typeVar, TypeRef)
val temp = TypeUtils.mergeTypeModifiers(tempFromEnv, typeRef);
val tempDeclaredType = temp.declaredType
if (typeVar !== tempDeclaredType
&& (TypeUtils.isOrContainsRefToTypeVar(temp) || (tempDeclaredType !== null && tempDeclaredType.generic))
&& G.get(GUARD_SUBST_TYPE_VARS -> temp) === null) {
val G2 = G.wrap;
G2.add(GUARD_SUBST_TYPE_VARS -> temp, Boolean.TRUE)
G2 |- temp ~> result
result = TypeUtils.copy(result); // always copy! (the subst-judgment might return 'temp' unchanged; see next comment why we have to copy 'temp')
} else {
result = TypeUtils.copy(temp); // always copy! ('temp' is lying in the rule env, don't wanna change content of rule env!)
// (note: TypeUtils#copyIfContained() would fail in previous lines, because 'temp' will not be "contained" in the sense of EMF containment)
}
}
or
{
// do we have multiple substitutions?
val List<TypeRef> l_raw = env(G, typeVar, List)
val l = newArrayList;
for(var i=0;i<l_raw.size;i++) {
val temp = l_raw.get(i);
val tempDeclaredType = temp.declaredType;
if(typeVar !== tempDeclaredType
&& (TypeUtils.isOrContainsRefToTypeVar(temp) || (tempDeclaredType !== null && tempDeclaredType.generic))
&& G.get(GUARD_SUBST_TYPE_VARS -> temp) === null) {
val G2 = G.wrap;
G2.add(GUARD_SUBST_TYPE_VARS -> temp, Boolean.TRUE)
G2 |- temp ~> var TypeRef tempResult
tempResult = TypeUtils.copy(tempResult); // always copy! (methods #createUnion/IntersectionType() below will do a #cloneIfContained() which fails here, see above)
l += tempResult;
} else {
l += TypeUtils.copy(temp); // always copy! (methods #createUnion/IntersectionType() below will do a #cloneIfContained() which fails here, see above)
}
}
result = if(typeVar.declaredCovariant) {
typeSystemHelper.createIntersectionType(G,l)
} else if(typeVar.declaredContravariant) {
typeSystemHelper.createUnionType(G,l)
} else {
G.addInconsistentSubstitutions(typeVar, l); // will have no effect unless recording was turned on
// by a validation (see method RuleEnvironmentExtensions#recordInconsistentSubstitutions(RuleEnvironment))
TypeRefsFactory.eINSTANCE.createUnknownTypeRef
};
TypeUtils.copyTypeModifiers(result, typeRef)
}
or
{
// no need to change anything here
}
}
// (3) substitute type variables in type arguments
if (typeRef?.declaredType !== null && typeRef.declaredType.generic) {
val len = typeRef.typeArgs.size;
// (a) without changing 'result', perform substitution on all type arguments and remember if anyone has changed
var boolean haveSubstitution = false;
val argsChanged = <TypeArgument>newArrayOfSize(len);
for(var i=0;i<len;i++) {
val arg = typeRef.typeArgs.get(i);
G |- arg ~> var TypeArgument argSubst
if(argSubst!==arg) {
// n.b.: will only add to argsChanged if changed! (otherwise argsChanged[i] will remain null)
argsChanged.set(i,argSubst);
haveSubstitution = true;
}
}
// (b) update 'result' with changed type arguments iff(!) one or more have changed
if(haveSubstitution) {
if(result===typeRef)
result = TypeUtils.copy(typeRef);
for(var i=0;i<len;i++) {
val argCh = argsChanged.get(i);
if(argCh!==null)
result.typeArgs.set(i, argCh);
}
}
}
// (4) substitution in structural members
if(result instanceof StructuralTypeRef) {
result = typeSystemHelper.substTypeVariablesInStructuralMembers(G, result);
}
}
// **********************************************************************
// Judgment this type ref
// **********************************************************************
axiom thisTypeRefParameterizedTypeRef
G |~ ParameterizedTypeRef type ~> TypeUtils.createBoundThisTypeRef(type)
rule thisTypeRefEObject
G |~ EObject location ~> TypeRef T
from {
val containingFunctionOrAccessor = N4JSASTUtils.getContainingFunctionOrAccessor(location)
switch containingFunctionOrAccessor {
ArrowFunction: {
G |~ containingFunctionOrAccessor ~> T
}
default: {
// searching for declaredTypeRefs introduced through @This
val containingTFunctionOrAccessor = containingFunctionOrAccessor?.definedFunctionOrAccessor;
val TypeRef declaredThisType = TypeSystemHelper.declaredThisType(containingTFunctionOrAccessor);
if (declaredThisType!==null) {
if ( declaredThisType instanceof ParameterizedTypeRef) {
G |~ declaredThisType ~> T;
} else {
T = declaredThisType
}
} else {
val thisTarget = N4JSASTUtils.getProbableThisTarget(location);
switch (thisTarget) {
ObjectLiteral: {
// call rule, type may be created on the fly
G |- thisTarget: T
}
N4ClassifierDefinition: {
var thisTargetDEFTYPE = thisTarget.definedType;
// In case of static polyfill (filler), replace defined type with filled type:
if (thisTarget instanceof N4ClassDeclaration) {
val clazz = thisTarget.definedTypeAsClass;
if (clazz !== null && clazz.isStaticPolyfill) {
val actualClazz = clazz.superClassRef.declaredType;
if (actualClazz !== null)
thisTargetDEFTYPE = actualClazz;
}
}
if (thisTargetDEFTYPE !== null) {
val containingFunction = N4JSASTUtils.getContainingFunction(location)
if (containingFunction instanceof N4MethodDeclaration &&
(containingFunction as N4MemberDeclaration).static) {
if( isInReturnDeclaration_Of_StaticMethod(location, containingFunction as N4MethodDeclaration)
) {
G |~ thisTargetDEFTYPE.ref ~> T;
} else if( isInBody_Of_StaticMethod(location, containingFunction as N4MethodDeclaration)) {
T = TypeUtils.createClassifierBoundThisTypeRef( TypeUtils.createTypeTypeRef( thisTargetDEFTYPE.ref, false));
} else {
T = TypeUtils.createConstructorTypeRef(thisTargetDEFTYPE);
}
} else {
val n4Field = EcoreUtil2.getContainerOfType(location, N4FieldDeclaration);
if (n4Field !== null && n4Field.static) {
// this case has been disallowed as of GHOLD-263
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef
// old:
// T = TypeUtils.createConstructorTypeRef(thisTargetDEFTYPE);
} else {
val n4Getter = EcoreUtil2.getContainerOfType(location, N4GetterDeclaration);
if (n4Getter !== null && n4Getter.static) {
T = TypeUtils.createConstructorTypeRef(thisTargetDEFTYPE);
} else {
val n4Setter = EcoreUtil2.getContainerOfType(location, N4SetterDeclaration);
if (n4Setter !== null && n4Setter.static) {
T = TypeUtils.createConstructorTypeRef(thisTargetDEFTYPE);
} else {
G |~ thisTargetDEFTYPE.ref ~> T;
}
}
}
}
} else {
T = G.anyTypeRefDynamic
}
}
default: {
// if (unrestricted.isActive(location)) {
if (jsVariantHelper.hasGlobalObject(location)) {
T = G.globalObjectTypeRef
} else {
T = G.undefinedTypeRef
}
}
} // end switch
}
}
}
}