dev.cel.checker.InferenceContext Maven / Gradle / Ivy
Show all versions of validators Show documentation
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dev.cel.checker;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import dev.cel.common.annotations.Internal;
import dev.cel.common.types.CelType;
import dev.cel.common.types.TypeParamType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* An object holding a context for type inference.
*
* Consists of a type substitution and a generator for free type variables in the context, as
* well as methods to work with the context.
*
*
CEL Library Internals. Do Not Use.
*/
@Internal
public class InferenceContext {
private Map substitution = new HashMap<>();
private int freeTypeVarCounter;
/** Creates a fresh type variable in the given context. */
public CelType newTypeVar(String prefix) {
// If the prefix ends with a digit, then add an extra % as separator between
// it and the counter to prevent the two from blending into each other since
// that can cause accidental name clashes.
String separator =
(!prefix.isEmpty() && Character.isDigit(prefix.charAt(prefix.length() - 1))) ? "%" : "";
return TypeParamType.create("%" + prefix + separator + freeTypeVarCounter++);
}
/**
* Returns an instance of the given type where all given type parameters are replaced by fresh
* type variables.
*/
public CelType newInstance(Iterable typeParams, CelType type) {
Map subs = new HashMap<>();
for (String typeParam : typeParams) {
subs.put(TypeParamType.create(typeParam), newTypeVar(typeParam));
}
return Types.substitute(subs, type, false);
}
/**
* Checks whether type1 is assignable to type2 under refinement of the type substitution. Returns
* true on success, and false on failure. The substitution in the type context will not be
* modified on failure.
*/
@CanIgnoreReturnValue
public boolean isAssignable(CelType type1, CelType type2) {
Map newSubs = Types.isAssignable(substitution, type1, type2);
if (newSubs != null) {
substitution = newSubs;
return true;
} else {
return false;
}
}
/** Same as {@link #isAssignable(CelType, CelType)} for lists of types. */
public boolean isAssignable(List list1, List list2) {
Map newSubs = Types.isAssignable(substitution, list1, list2);
if (newSubs != null) {
substitution = newSubs;
return true;
} else {
return false;
}
}
/** Specializes the given type using the substitution of this context. */
public CelType specialize(CelType type) {
return Types.substitute(substitution, type, false);
}
/** Specializes using given type list of types using the substitution of this context. */
public List specialize(List types) {
List result = new ArrayList<>();
for (CelType type : types) {
result.add(specialize(type));
}
return result;
}
/**
* Finalizes the given type by applying the current type substitution and mapping all remaining
* type parameters to DYN.
*/
public CelType finalize(CelType type) {
return Types.substitute(substitution, type, true);
}
}