Skip to content

Commit

Permalink
[NEW TYPE INFERENCE] Attach more types to AST nodes. Also some small …
Browse files Browse the repository at this point in the history
…cleanups.

This CL is needed by a pass that runs after type inference and converts new types to old types.
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=90083179
  • Loading branch information
dimvar committed Apr 2, 2015
1 parent a9df90f commit b200ceb
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 40 deletions.
8 changes: 5 additions & 3 deletions src/com/google/javascript/jscomp/DiagnosticGroups.java
Expand Up @@ -278,7 +278,7 @@ public DiagnosticGroup forName(String name) {
// NewTypeInference.PROPERTY_ACCESS_ON_NONOBJECT,
// NewTypeInference.RETURN_NONDECLARED_TYPE,
NewTypeInference.UNKNOWN_ASSERTION_TYPE,
CheckGlobalThis.GLOBAL_THIS,
// CheckGlobalThis.GLOBAL_THIS,
// CheckMissingReturn.MISSING_RETURN_STATEMENT,
TypeCheck.CONSTRUCTOR_NOT_CALLABLE,
TypeCheck.ILLEGAL_OBJLIT_KEY,
Expand Down Expand Up @@ -322,7 +322,8 @@ public DiagnosticGroup forName(String name) {
DiagnosticGroups.registerGroup("const",
CheckAccessControls.CONST_PROPERTY_DELETED,
CheckAccessControls.CONST_PROPERTY_REASSIGNED_VALUE,
ConstCheck.CONST_REASSIGNED_VALUE_ERROR);
ConstCheck.CONST_REASSIGNED_VALUE_ERROR,
NewTypeInference.CONST_REASSIGNED);

public static final DiagnosticGroup CONSTANT_PROPERTY =
DiagnosticGroups.registerGroup("constantProperty",
Expand All @@ -339,7 +340,8 @@ public DiagnosticGroup forName(String name) {
VarCheck.VAR_MULTIPLY_DECLARED_ERROR,
TypeValidator.DUP_VAR_DECLARATION,
TypeValidator.DUP_VAR_DECLARATION_TYPE_MISMATCH,
VariableReferenceCheck.REDECLARED_VARIABLE);
VariableReferenceCheck.REDECLARED_VARIABLE,
GlobalTypeInfo.REDECLARED_PROPERTY);

public static final DiagnosticGroup ES3 =
DiagnosticGroups.registerGroup("es3",
Expand Down
18 changes: 12 additions & 6 deletions src/com/google/javascript/jscomp/GlobalTypeInfo.java
Expand Up @@ -274,17 +274,17 @@ public void process(Node externs, Node root) {
// defined in the global scope.
CollectNamedTypes rootCnt = new CollectNamedTypes(globalScope);
if (externs != null) {
new NodeTraversal(compiler, rootCnt).traverse(externs);
NodeTraversal.traverse(compiler, externs, rootCnt);
}
new NodeTraversal(compiler, rootCnt).traverse(root);
NodeTraversal.traverse(compiler, root, rootCnt);
// (2) Determine the type represented by each typedef and each enum
globalScope.resolveTypedefs(typeParser);
globalScope.resolveEnums(typeParser);
// (3) Repeat steps 1-2 for all the other scopes (outer-to-inner)
for (int i = 1; i < scopes.size(); i++) {
Scope s = scopes.get(i);
CollectNamedTypes cnt = new CollectNamedTypes(s);
new NodeTraversal(compiler, cnt).traverse(s.getBody());
NodeTraversal.traverse(compiler, s.getBody(), cnt);
s.resolveTypedefs(typeParser);
s.resolveEnums(typeParser);
if (NewTypeInference.measureMem) {
Expand All @@ -297,17 +297,17 @@ public void process(Node externs, Node root) {
// - Declare properties on types
ProcessScope rootPs = new ProcessScope(globalScope);
if (externs != null) {
new NodeTraversal(compiler, rootPs).traverse(externs);
NodeTraversal.traverse(compiler, externs, rootPs);
}
new NodeTraversal(compiler, rootPs).traverse(root);
NodeTraversal.traverse(compiler, root, rootPs);
// (5) Things that must happen after the traversal of the scope
rootPs.finishProcessingScope();

// (6) Repeat steps 4-5 for all the other scopes (outer-to-inner)
for (int i = 1; i < scopes.size(); i++) {
Scope s = scopes.get(i);
ProcessScope ps = new ProcessScope(s);
new NodeTraversal(compiler, ps).traverse(s.getBody());
NodeTraversal.traverse(compiler, s.getBody(), ps);
ps.finishProcessingScope();
if (NewTypeInference.measureMem) {
NewTypeInference.updatePeakMem();
Expand Down Expand Up @@ -1511,6 +1511,7 @@ private boolean mayWarnAboutExistingProp(RawNominalType classType,
// 1) Why is it just specific to "duplicate" and to properties?
// 2) The docs say that it's only allowed in the top level, but the code
// allows it in all scopes.
// https://github.com/google/closure-compiler/wiki/Warnings#suppress-tags
// For now, we implement it b/c it exists in the current type inference.
// But I wouldn't mind if we stopped supporting it.
private boolean suppressDupPropWarning(
Expand Down Expand Up @@ -1972,6 +1973,11 @@ String getName() {

private void setDeclaredType(DeclaredFunctionType declaredType) {
this.declaredType = declaredType;
// In NTI, we set the type of a function node after we create the summary.
// NTI doesn't analyze externs, so we set the type for extern functions here.
if (this.root.isFromExterns()) {
this.root.setTypeI(getCommonTypes().fromFunctionType(declaredType.toFunctionType()));
}
}

DeclaredFunctionType getDeclaredType() {
Expand Down
44 changes: 30 additions & 14 deletions src/com/google/javascript/jscomp/NewTypeInference.java
Expand Up @@ -887,6 +887,7 @@ private void createSummary(Scope fn) {
TypeEnv entryEnv = getEntryTypeEnv();
TypeEnv exitEnv = getFinalTypeEnv();
FunctionTypeBuilder builder = new FunctionTypeBuilder();
Node fnRoot = fn.getRoot();

DeclaredFunctionType declType = fn.getDeclaredType();
int reqArity = declType.getRequiredArity();
Expand Down Expand Up @@ -946,7 +947,7 @@ private void createSummary(Scope fn) {
if (!isAllowedToNotReturn(fn) &&
!JSType.UNDEFINED.isSubtypeOf(declRetType) &&
hasPathWithNoReturn(cfg)) {
warnings.add(JSError.make(fn.getRoot(),
warnings.add(JSError.make(fnRoot,
CheckMissingReturn.MISSING_RETURN_STATEMENT,
declRetType.toString()));
}
Expand All @@ -960,6 +961,11 @@ private void createSummary(Scope fn) {
println("Function summary for ", fn.getReadableName());
println("\t", summary);
summaries.put(fn, summary);
fnRoot.setTypeI(summary);
Node fnNameNode = NodeUtil.getFunctionNameNode(fnRoot);
if (fnNameNode != null) {
fnNameNode.setTypeI(summary);
}
}

// TODO(dimvar): To get the adjusted end-of-fwd type for objs, we must be
Expand Down Expand Up @@ -1018,6 +1024,7 @@ private TypeEnv processVarDeclaration(Node nameNode, TypeEnv inEnv) {
return inEnv;
}
if (NodeUtil.isNamespaceDecl(nameNode)) {
nameNode.setTypeI(declType);
return envPutType(inEnv, varName, declType);
}

Expand Down Expand Up @@ -1045,6 +1052,7 @@ private TypeEnv processVarDeclaration(Node nameNode, TypeEnv inEnv) {
rhsType = declType.specialize(rhsType);
}
}
nameNode.setTypeI(rhsType);
return envPutType(outEnv, varName, rhsType);
}

Expand Down Expand Up @@ -1784,17 +1792,17 @@ private EnvTypePair analyzeFunctionBindFwd(Node call, TypeEnv inEnv) {
env = analyzeCallNodeArgumentsFwd(call, bindComponents.parameters,
boundFunType, new ArrayList<JSType>(), env);
// For any formal not bound here, add it to the resulting function type.
for (int j = numArgs; j < boundFunType.getMaxArity(); j++) {
for (int j = numArgs; j < boundFunType.getMaxArityWithoutRestFormals(); j++) {
JSType formalType = boundFunType.getFormalType(j);
if (boundFunType.isRequiredArg(j)) {
builder.addReqFormal(formalType);
} else if (boundFunType.isOptionalArg(j)) {
builder.addOptFormal(formalType);
} else {
builder.addRestFormals(formalType);
break; // To avoid iterating to Integer.MAX_VALUE
builder.addOptFormal(formalType);
}
}
if (boundFunType.hasRestFormals()) {
builder.addRestFormals(boundFunType.getRestFormalsType());
}
return new EnvTypePair(env, commonTypes.fromFunctionType(
builder.addRetType(boundFunType.getReturnType()).buildFunction()));
}
Expand Down Expand Up @@ -3335,48 +3343,54 @@ private LValueResultFwd analyzeLValueFwd(

private LValueResultFwd analyzeLValueFwd(
Node expr, TypeEnv inEnv, JSType type, boolean insideQualifiedName) {
LValueResultFwd lvalResult = null;
switch (expr.getType()) {
case Token.THIS: {
if (currentScope.hasThis()) {
return new LValueResultFwd(inEnv, envGetType(inEnv, "this"),
lvalResult = new LValueResultFwd(inEnv, envGetType(inEnv, "this"),
currentScope.getDeclaredTypeOf("this"),
new QualifiedName("this"));
} else {
warnings.add(JSError.make(expr, CheckGlobalThis.GLOBAL_THIS));
return new LValueResultFwd(inEnv, JSType.UNKNOWN, null, null);
lvalResult = new LValueResultFwd(inEnv, JSType.UNKNOWN, null, null);
}
break;
}
case Token.NAME: {
String varName = expr.getString();
JSType varType = analyzeExprFwd(expr, inEnv).type;
return new LValueResultFwd(inEnv, varType,
lvalResult = new LValueResultFwd(inEnv, varType,
currentScope.getDeclaredTypeOf(varName),
varType.hasNonScalar() ? new QualifiedName(varName) : null);
break;
}
case Token.GETPROP: {
Node obj = expr.getFirstChild();
QualifiedName pname =
new QualifiedName(expr.getLastChild().getString());
return analyzePropLValFwd(obj, pname, inEnv, type, insideQualifiedName);
lvalResult = analyzePropLValFwd(obj, pname, inEnv, type, insideQualifiedName);
break;
}
case Token.GETELEM: {
Node obj = expr.getFirstChild();
Node prop = expr.getLastChild();
// (1) A getelem where the prop is a string literal is like a getprop
if (prop.isString()) {
QualifiedName pname = new QualifiedName(prop.getString());
return analyzePropLValFwd(
obj, pname, inEnv, type, insideQualifiedName);
lvalResult = analyzePropLValFwd(obj, pname, inEnv, type, insideQualifiedName);
break;
}
// (2) A getelem where the receiver is an array
LValueResultFwd lvalue =
analyzeLValueFwd(obj, inEnv, JSType.UNKNOWN, true);
if (isArrayType(lvalue.type)) {
return analyzeArrayElmLvalFwd(prop, lvalue);
lvalResult = analyzeArrayElmLvalFwd(prop, lvalue);
break;
}
// (3) All other getelems
EnvTypePair pair = analyzeExprFwd(expr, inEnv, type);
return new LValueResultFwd(pair.env, pair.type, null, null);
lvalResult = new LValueResultFwd(pair.env, pair.type, null, null);
break;
}
case Token.VAR: { // Can happen iff its parent is a for/in.
Preconditions.checkState(NodeUtil.isForIn(expr.getParent()));
Expand All @@ -3396,6 +3410,8 @@ private LValueResultFwd analyzeLValueFwd(
return new LValueResultFwd(pair.env, pair.type, null, null);
}
}
expr.setTypeI(lvalResult.type);
return lvalResult;
}

private LValueResultFwd analyzeArrayElmLvalFwd(
Expand Down
11 changes: 10 additions & 1 deletion src/com/google/javascript/jscomp/newtypes/FunctionType.java
Expand Up @@ -209,6 +209,11 @@ public boolean hasRestFormals() {
return restFormals != null;
}

public JSType getRestFormalsType() {
Preconditions.checkNotNull(restFormals);
return restFormals;
}

// 0-indexed
// Returns null if argpos indexes past the arguments
public JSType getFormalType(int argpos) {
Expand Down Expand Up @@ -250,6 +255,10 @@ public int getMaxArity() {
}
}

public int getMaxArityWithoutRestFormals() {
return requiredFormals.size() + optionalFormals.size();
}

public boolean isRequiredArg(int i) {
return i < requiredFormals.size();
}
Expand Down Expand Up @@ -370,7 +379,7 @@ public boolean isSubtypeOf(FunctionType other) {
}
// NOTE(dimvar): This is a bug. The code that triggers this should be rare
// and the fix is not trivial, so for now we decided to not fix.
// See unit tests in NewTypeInferenceTestES5OrLower#testGenericsSubtyping
// See unit tests in NewTypeInferenceES5OrLowerTest#testGenericsSubtyping
return instantiateGenericsWithUnknown(this).isSubtypeOf(other);
}

Expand Down
10 changes: 5 additions & 5 deletions src/com/google/javascript/jscomp/newtypes/JSType.java
Expand Up @@ -884,8 +884,8 @@ public FunctionType getFunType() {
return result;
}

NominalType getNominalTypeIfUnique() {
if (getObjs().isEmpty() || getObjs().size() > 1) {
public NominalType getNominalTypeIfSingletonObj() {
if (getMask() != NON_SCALAR_MASK || getObjs().size() > 1) {
return null;
}
return Iterables.getOnlyElement(getObjs()).getNominalType();
Expand Down Expand Up @@ -1104,7 +1104,7 @@ public boolean isFunctionType() {

@Override
public boolean isInterface() {
NominalType nt = getNominalTypeIfUnique();
NominalType nt = getNominalTypeIfSingletonObj();
return nt != null && nt.isInterface();
}

Expand Down Expand Up @@ -1149,11 +1149,11 @@ public int hashCode() {

final class UnionType extends JSType {
private final int mask;
// objs is null for scalar types
// objs is empty for scalar types
private final ImmutableSet<ObjectType> objs;
// typeVar is null for non-generic types
private final String typeVar;
// enums is null for types that don't have enums
// enums is empty for types that don't have enums
private final ImmutableSet<EnumType> enums;

UnionType(int mask, ImmutableSet<ObjectType> objs,
Expand Down
Expand Up @@ -385,7 +385,7 @@ public void resolveEnum(EnumType e, DeclaredTypeRegistry registry) {
private JSType getNominalTypeHelper(JSType namedType, Node n,
DeclaredTypeRegistry registry, ImmutableList<String> outerTypeParameters)
throws UnknownTypeException {
NominalType uninstantiated = namedType.getNominalTypeIfUnique();
NominalType uninstantiated = namedType.getNominalTypeIfSingletonObj();
RawNominalType rawType = uninstantiated.getRawNominalType();
if (!rawType.isGeneric() && !n.hasChildren()) {
return rawType.getInstanceAsNullableJSType();
Expand Down Expand Up @@ -499,7 +499,7 @@ private void fillInFunTypeBuilder(
private NominalType getNominalType(Node n,
DeclaredTypeRegistry registry, ImmutableList<String> typeParameters) {
return getTypeFromComment(n, registry, typeParameters)
.getNominalTypeIfUnique();
.removeType(JSType.NULL).getNominalTypeIfSingletonObj();
}

private ImmutableSet<NominalType> getImplementedInterfaces(
Expand All @@ -525,7 +525,7 @@ private ImmutableSet<NominalType> getInterfacesHelper(
JSType interfaceType =
getMaybeTypeFromComment(expRoot, registry, typeParameters);
if (interfaceType != null) {
NominalType nt = interfaceType.getNominalTypeIfUnique();
NominalType nt = interfaceType.getNominalTypeIfSingletonObj();
if (nt != null && nt.isInterface()) {
builder.add(nt);
} else {
Expand Down Expand Up @@ -815,7 +815,7 @@ private DeclaredFunctionType getFunTypeFromTypicalFunctionJsdoc(
// people use other types as well: unions, records, etc.
// Decide what to do about those.
NominalType thisTypeAsNominal = thisType == null
? null : thisType.getNominalTypeIfUnique();
? null : thisType.getNominalTypeIfSingletonObj();
builder.addReceiverType(thisTypeAsNominal);
}

Expand Down Expand Up @@ -924,7 +924,7 @@ private NominalType getMaybeParentClass(
if (extendedType == null) {
return null;
}
NominalType parentClass = extendedType.getNominalTypeIfUnique();
NominalType parentClass = extendedType.getNominalTypeIfSingletonObj();
if (parentClass != null && parentClass.isClass()) {
return parentClass;
}
Expand Down
6 changes: 3 additions & 3 deletions src/com/google/javascript/jscomp/newtypes/ObjectType.java
Expand Up @@ -661,7 +661,7 @@ private static boolean areRelatedClasses(NominalType c1, NominalType c2) {
// TODO(dimvar): handle greatest lower bound of interface types.
// If we do that, we need to normalize the output, otherwise it could contain
// two object types that are in a subtype relation, eg, see
// NewTypeInferenceTestES5OrLower#testDifficultObjectSpecialization.
// NewTypeInferenceES5OrLowerTest#testDifficultObjectSpecialization.
static ImmutableSet<ObjectType> meetSetsHelper(
boolean specializeObjs1,
Set<ObjectType> objs1, Set<ObjectType> objs2) {
Expand Down Expand Up @@ -699,7 +699,7 @@ FunctionType getFunType() {
return fn;
}

NominalType getNominalType() {
public NominalType getNominalType() {
return nominalType;
}

Expand Down Expand Up @@ -848,7 +848,7 @@ public String toString() {
return appendTo(new StringBuilder()).toString();
}

public StringBuilder appendTo(StringBuilder builder) {
StringBuilder appendTo(StringBuilder builder) {
if (props.isEmpty()
|| (props.size() == 1 && props.containsKey("prototype"))) {
if (fn != null) {
Expand Down
5 changes: 2 additions & 3 deletions src/com/google/javascript/rhino/Node.java
Expand Up @@ -2058,12 +2058,11 @@ public JSType getJSType() {
}

public void setJSType(JSType jsType) {
this.typei = jsType;
this.typei = jsType;
}

public TypeI getTypeI() {
// For the time being, we only want to return the type iff it's an old type.
return getJSType();
return this.typei;
}

public void setTypeI(TypeI type) {
Expand Down

0 comments on commit b200ceb

Please sign in to comment.