Java tutorial
/** * ADOBE SYSTEMS INCORPORATED * Copyright 2009-2013 Adobe Systems Incorporated * All Rights Reserved. * * NOTICE: Adobe permits you to use, modify, and distribute * this file in accordance with the terms of the MIT license, * a copy of which can be found in the LICENSE.txt file or at * http://opensource.org/licenses/MIT. */ package compile.type; import com.google.common.collect.Sets; import compile.Loc; import compile.Session; import compile.type.visit.*; import java.util.*; /** * Common base implementation for classes that represent type terms * that can be quantified. * TODO refactor quantified types pronto * * @author Basil Hosmer */ public abstract class ScopeType extends AbstractType { private final LinkedHashMap<String, TypeParam> params; private boolean paramsCommitted; public ScopeType(final Loc loc) { super(loc); this.params = new LinkedHashMap<String, TypeParam>(); this.paramsCommitted = false; } // Type public final void collectInlineParams() { new TypeBindingCollector(this).collect(); paramsCommitted = true; } public boolean hasParams() { return !params.isEmpty(); } public Map<String, TypeParam> getParams() { return params; } public TypeParam getParam(final String name) { return params.get(name); } public void addParam(final TypeParam param) { if (paramsCommitted) { Session.error(loc, "internal error in type {0}: addParam with params committed", dump()); } else if (params.containsKey(param.getName())) { Session.error(loc, "internal error in type {0}: addParam {1}, already defined", dump(), param.getName()); } else { param.setTypeScope(this); params.put(param.getName(), param); } } public void addParams(final Collection<TypeParam> params) { for (final TypeParam param : params) addParam(param); paramsCommitted = true; } public final boolean getParamsCommitted() { return paramsCommitted; } /** * */ public final Type instance(final TypeEnv env, final boolean useParamNames) { return new TypeInstantiator(this, env, useParamNames).getInstance(); } /** * */ public final Type quantify(final SubstMap newParams, final SubstMap ambientParams) { final Type applied = subst(ambientParams.compose(loc, newParams)); // add new params to our result type for (final Type type : newParams.values()) applied.addParam((TypeParam) type); return applied; } /** * Note: nameGenOffset is part of our scheme for avoiding (the appearance of) * name capture in dumps and error messages. We quantify inner-to-outer, * so the type checker passes in the number of ambient vars as a starting * offset to the name generator. That way outermost names are A, B, C, ... * Spacing is not perfect, there are gaps. It would give a prettier result to * do a consolidation pass at the end, but the important thing for round- * tripping (not that we do it currently) is that there is no overlap. * * NOTE: the relationship between a var's constraint and that of its * corresponding param is tricky--see comment in code body, and also * {@link TypeVarSubstitutor#visit(TypeVar)}. */ public SubstMap buildParamMap(final Set<TypeVar> vars, final int nameGenOffset, final TypeEnv env) { final Set<TypeVar> qvars = Sets.intersection(vars, getVars()); if (qvars.isEmpty()) return SubstMap.EMPTY; // when generating param names, avoid names of used params. final Set<String> usedNames = new HashSet<String>(); for (final TypeParam param : TypeParamCollector.collect(this)) usedNames.add(param.getName()); // begin generating names from this offset into the generated name space. int genIndex = nameGenOffset; final SubstMap substMap = new SubstMap(); for (final TypeVar v : qvars) { final String name; final TypeParam param = v.getBackingParam(usedNames, this); if (param != null) { name = param.getName(); } else { String tmp; do { tmp = nameGen(genIndex++); } while (usedNames.contains(tmp)); name = tmp; } usedNames.add(name); // NOTE: v's constraint is ignored here, because it needs to be // not just transferred to the corresponding param, but also run // through this same substitution map at quantification time. // Users of this map are responsible for doing this. // substMap.put(v, new TypeParam(v.getLoc(), name, v.getKind(), null)); } return substMap; } /** * The override here enforces the rule that two scopes * can only be equivalent if they match each other. */ public boolean equiv(final Type other) { if (other instanceof ScopeType) return equiv(other, new EquivState(this, (ScopeType) other)); return equiv(other.deref(), new EquivState()); } // Object @Override public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final ScopeType that = (ScopeType) o; if (!params.equals(that.params)) return false; return true; } @Override public int hashCode() { return params.hashCode(); } }