rebase: update kubernetes to latest

updating the kubernetes release to the
latest in main go.mod

Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
Madhu Rajanna
2024-08-19 10:01:33 +02:00
committed by mergify[bot]
parent 63c4c05b35
commit 5a66991bb3
2173 changed files with 98906 additions and 61334 deletions

View File

@ -60,7 +60,6 @@ go_test(
"//test:go_default_library",
"//test/proto2pb:go_default_library",
"//test/proto3pb:go_default_library",
"@com_github_antlr_antlr4_runtime_go_antlr_v4//:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)

View File

@ -18,6 +18,7 @@ package checker
import (
"fmt"
"reflect"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
@ -25,139 +26,98 @@ import (
"github.com/google/cel-go/common/decls"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/types"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
"github.com/google/cel-go/common/types/ref"
)
type checker struct {
*ast.AST
ast.ExprFactory
env *Env
errors *typeErrors
mappings *mapping
freeTypeVarCounter int
sourceInfo *exprpb.SourceInfo
types map[int64]*types.Type
references map[int64]*ast.ReferenceInfo
}
// Check performs type checking, giving a typed AST.
// The input is a ParsedExpr proto and an env which encapsulates
// type binding of variables, declarations of built-in functions,
// descriptions of protocol buffers, and a registry for errors.
// Returns a CheckedExpr proto, which might not be usable if
// there are errors in the error registry.
func Check(parsedExpr *exprpb.ParsedExpr, source common.Source, env *Env) (*ast.CheckedAST, *common.Errors) {
//
// The input is a parsed AST and an env which encapsulates type binding of variables,
// declarations of built-in functions, descriptions of protocol buffers, and a registry for
// errors.
//
// Returns a type-checked AST, which might not be usable if there are errors in the error
// registry.
func Check(parsed *ast.AST, source common.Source, env *Env) (*ast.AST, *common.Errors) {
errs := common.NewErrors(source)
typeMap := make(map[int64]*types.Type)
refMap := make(map[int64]*ast.ReferenceInfo)
c := checker{
AST: ast.NewCheckedAST(parsed, typeMap, refMap),
ExprFactory: ast.NewExprFactory(),
env: env,
errors: &typeErrors{errs: errs},
mappings: newMapping(),
freeTypeVarCounter: 0,
sourceInfo: parsedExpr.GetSourceInfo(),
types: make(map[int64]*types.Type),
references: make(map[int64]*ast.ReferenceInfo),
}
c.check(parsedExpr.GetExpr())
c.check(c.Expr())
// Walk over the final type map substituting any type parameters either by their bound value or
// by DYN.
m := make(map[int64]*types.Type)
for id, t := range c.types {
m[id] = substitute(c.mappings, t, true)
// Walk over the final type map substituting any type parameters either by their bound value
// or by DYN.
for id, t := range c.TypeMap() {
c.SetType(id, substitute(c.mappings, t, true))
}
return &ast.CheckedAST{
Expr: parsedExpr.GetExpr(),
SourceInfo: parsedExpr.GetSourceInfo(),
TypeMap: m,
ReferenceMap: c.references,
}, errs
return c.AST, errs
}
func (c *checker) check(e *exprpb.Expr) {
func (c *checker) check(e ast.Expr) {
if e == nil {
return
}
switch e.GetExprKind().(type) {
case *exprpb.Expr_ConstExpr:
literal := e.GetConstExpr()
switch literal.GetConstantKind().(type) {
case *exprpb.Constant_BoolValue:
c.checkBoolLiteral(e)
case *exprpb.Constant_BytesValue:
c.checkBytesLiteral(e)
case *exprpb.Constant_DoubleValue:
c.checkDoubleLiteral(e)
case *exprpb.Constant_Int64Value:
c.checkInt64Literal(e)
case *exprpb.Constant_NullValue:
c.checkNullLiteral(e)
case *exprpb.Constant_StringValue:
c.checkStringLiteral(e)
case *exprpb.Constant_Uint64Value:
c.checkUint64Literal(e)
switch e.Kind() {
case ast.LiteralKind:
literal := ref.Val(e.AsLiteral())
switch literal.Type() {
case types.BoolType, types.BytesType, types.DoubleType, types.IntType,
types.NullType, types.StringType, types.UintType:
c.setType(e, literal.Type().(*types.Type))
default:
c.errors.unexpectedASTType(e.ID(), c.location(e), "literal", literal.Type().TypeName())
}
case *exprpb.Expr_IdentExpr:
case ast.IdentKind:
c.checkIdent(e)
case *exprpb.Expr_SelectExpr:
case ast.SelectKind:
c.checkSelect(e)
case *exprpb.Expr_CallExpr:
case ast.CallKind:
c.checkCall(e)
case *exprpb.Expr_ListExpr:
case ast.ListKind:
c.checkCreateList(e)
case *exprpb.Expr_StructExpr:
case ast.MapKind:
c.checkCreateMap(e)
case ast.StructKind:
c.checkCreateStruct(e)
case *exprpb.Expr_ComprehensionExpr:
case ast.ComprehensionKind:
c.checkComprehension(e)
default:
c.errors.unexpectedASTType(e.GetId(), c.location(e), e)
c.errors.unexpectedASTType(e.ID(), c.location(e), "unspecified", reflect.TypeOf(e).Name())
}
}
func (c *checker) checkInt64Literal(e *exprpb.Expr) {
c.setType(e, types.IntType)
}
func (c *checker) checkUint64Literal(e *exprpb.Expr) {
c.setType(e, types.UintType)
}
func (c *checker) checkStringLiteral(e *exprpb.Expr) {
c.setType(e, types.StringType)
}
func (c *checker) checkBytesLiteral(e *exprpb.Expr) {
c.setType(e, types.BytesType)
}
func (c *checker) checkDoubleLiteral(e *exprpb.Expr) {
c.setType(e, types.DoubleType)
}
func (c *checker) checkBoolLiteral(e *exprpb.Expr) {
c.setType(e, types.BoolType)
}
func (c *checker) checkNullLiteral(e *exprpb.Expr) {
c.setType(e, types.NullType)
}
func (c *checker) checkIdent(e *exprpb.Expr) {
identExpr := e.GetIdentExpr()
func (c *checker) checkIdent(e ast.Expr) {
identName := e.AsIdent()
// Check to see if the identifier is declared.
if ident := c.env.LookupIdent(identExpr.GetName()); ident != nil {
if ident := c.env.LookupIdent(identName); ident != nil {
c.setType(e, ident.Type())
c.setReference(e, ast.NewIdentReference(ident.Name(), ident.Value()))
// Overwrite the identifier with its fully qualified name.
identExpr.Name = ident.Name()
e.SetKindCase(c.NewIdent(e.ID(), ident.Name()))
return
}
c.setType(e, types.ErrorType)
c.errors.undeclaredReference(e.GetId(), c.location(e), c.env.container.Name(), identExpr.GetName())
c.errors.undeclaredReference(e.ID(), c.location(e), c.env.container.Name(), identName)
}
func (c *checker) checkSelect(e *exprpb.Expr) {
sel := e.GetSelectExpr()
func (c *checker) checkSelect(e ast.Expr) {
sel := e.AsSelect()
// Before traversing down the tree, try to interpret as qualified name.
qname, found := containers.ToQualifiedName(e)
if found {
@ -170,31 +130,26 @@ func (c *checker) checkSelect(e *exprpb.Expr) {
// variable name.
c.setType(e, ident.Type())
c.setReference(e, ast.NewIdentReference(ident.Name(), ident.Value()))
identName := ident.Name()
e.ExprKind = &exprpb.Expr_IdentExpr{
IdentExpr: &exprpb.Expr_Ident{
Name: identName,
},
}
e.SetKindCase(c.NewIdent(e.ID(), ident.Name()))
return
}
}
resultType := c.checkSelectField(e, sel.GetOperand(), sel.GetField(), false)
if sel.TestOnly {
resultType := c.checkSelectField(e, sel.Operand(), sel.FieldName(), false)
if sel.IsTestOnly() {
resultType = types.BoolType
}
c.setType(e, substitute(c.mappings, resultType, false))
}
func (c *checker) checkOptSelect(e *exprpb.Expr) {
func (c *checker) checkOptSelect(e ast.Expr) {
// Collect metadata related to the opt select call packaged by the parser.
call := e.GetCallExpr()
operand := call.GetArgs()[0]
field := call.GetArgs()[1]
call := e.AsCall()
operand := call.Args()[0]
field := call.Args()[1]
fieldName, isString := maybeUnwrapString(field)
if !isString {
c.errors.notAnOptionalFieldSelection(field.GetId(), c.location(field), field)
c.errors.notAnOptionalFieldSelection(field.ID(), c.location(field), field)
return
}
@ -204,7 +159,7 @@ func (c *checker) checkOptSelect(e *exprpb.Expr) {
c.setReference(e, ast.NewFunctionReference("select_optional_field"))
}
func (c *checker) checkSelectField(e, operand *exprpb.Expr, field string, optional bool) *types.Type {
func (c *checker) checkSelectField(e, operand ast.Expr, field string, optional bool) *types.Type {
// Interpret as field selection, first traversing down the operand.
c.check(operand)
operandType := substitute(c.mappings, c.getType(operand), false)
@ -222,7 +177,7 @@ func (c *checker) checkSelectField(e, operand *exprpb.Expr, field string, option
// Objects yield their field type declaration as the selection result type, but only if
// the field is defined.
messageType := targetType
if fieldType, found := c.lookupFieldType(e.GetId(), messageType.TypeName(), field); found {
if fieldType, found := c.lookupFieldType(e.ID(), messageType.TypeName(), field); found {
resultType = fieldType
}
case types.TypeParamKind:
@ -236,7 +191,7 @@ func (c *checker) checkSelectField(e, operand *exprpb.Expr, field string, option
// Dynamic / error values are treated as DYN type. Errors are handled this way as well
// in order to allow forward progress on the check.
if !isDynOrError(targetType) {
c.errors.typeDoesNotSupportFieldSelection(e.GetId(), c.location(e), targetType)
c.errors.typeDoesNotSupportFieldSelection(e.ID(), c.location(e), targetType)
}
resultType = types.DynType
}
@ -248,35 +203,34 @@ func (c *checker) checkSelectField(e, operand *exprpb.Expr, field string, option
return resultType
}
func (c *checker) checkCall(e *exprpb.Expr) {
func (c *checker) checkCall(e ast.Expr) {
// Note: similar logic exists within the `interpreter/planner.go`. If making changes here
// please consider the impact on planner.go and consolidate implementations or mirror code
// as appropriate.
call := e.GetCallExpr()
fnName := call.GetFunction()
call := e.AsCall()
fnName := call.FunctionName()
if fnName == operators.OptSelect {
c.checkOptSelect(e)
return
}
args := call.GetArgs()
args := call.Args()
// Traverse arguments.
for _, arg := range args {
c.check(arg)
}
target := call.GetTarget()
// Regular static call with simple name.
if target == nil {
if !call.IsMemberFunction() {
// Check for the existence of the function.
fn := c.env.LookupFunction(fnName)
if fn == nil {
c.errors.undeclaredReference(e.GetId(), c.location(e), c.env.container.Name(), fnName)
c.errors.undeclaredReference(e.ID(), c.location(e), c.env.container.Name(), fnName)
c.setType(e, types.ErrorType)
return
}
// Overwrite the function name with its fully qualified resolved name.
call.Function = fn.Name()
e.SetKindCase(c.NewCall(e.ID(), fn.Name(), args...))
// Check to see whether the overload resolves.
c.resolveOverloadOrError(e, fn, nil, args)
return
@ -287,6 +241,7 @@ func (c *checker) checkCall(e *exprpb.Expr) {
// target a.b.
//
// Check whether the target is a namespaced function name.
target := call.Target()
qualifiedPrefix, maybeQualified := containers.ToQualifiedName(target)
if maybeQualified {
maybeQualifiedName := qualifiedPrefix + "." + fnName
@ -295,15 +250,14 @@ func (c *checker) checkCall(e *exprpb.Expr) {
// The function name is namespaced and so preserving the target operand would
// be an inaccurate representation of the desired evaluation behavior.
// Overwrite with fully-qualified resolved function name sans receiver target.
call.Target = nil
call.Function = fn.Name()
e.SetKindCase(c.NewCall(e.ID(), fn.Name(), args...))
c.resolveOverloadOrError(e, fn, nil, args)
return
}
}
// Regular instance call.
c.check(call.Target)
c.check(target)
fn := c.env.LookupFunction(fnName)
// Function found, attempt overload resolution.
if fn != nil {
@ -312,11 +266,11 @@ func (c *checker) checkCall(e *exprpb.Expr) {
}
// Function name not declared, record error.
c.setType(e, types.ErrorType)
c.errors.undeclaredReference(e.GetId(), c.location(e), c.env.container.Name(), fnName)
c.errors.undeclaredReference(e.ID(), c.location(e), c.env.container.Name(), fnName)
}
func (c *checker) resolveOverloadOrError(
e *exprpb.Expr, fn *decls.FunctionDecl, target *exprpb.Expr, args []*exprpb.Expr) {
e ast.Expr, fn *decls.FunctionDecl, target ast.Expr, args []ast.Expr) {
// Attempt to resolve the overload.
resolution := c.resolveOverload(e, fn, target, args)
// No such overload, error noted in the resolveOverload call, type recorded here.
@ -330,7 +284,7 @@ func (c *checker) resolveOverloadOrError(
}
func (c *checker) resolveOverload(
call *exprpb.Expr, fn *decls.FunctionDecl, target *exprpb.Expr, args []*exprpb.Expr) *overloadResolution {
call ast.Expr, fn *decls.FunctionDecl, target ast.Expr, args []ast.Expr) *overloadResolution {
var argTypes []*types.Type
if target != nil {
@ -362,8 +316,8 @@ func (c *checker) resolveOverload(
for i, argType := range argTypes {
if !c.isAssignable(argType, types.BoolType) {
c.errors.typeMismatch(
args[i].GetId(),
c.locationByID(args[i].GetId()),
args[i].ID(),
c.locationByID(args[i].ID()),
types.BoolType,
argType)
resultType = types.ErrorType
@ -408,29 +362,29 @@ func (c *checker) resolveOverload(
for i, argType := range argTypes {
argTypes[i] = substitute(c.mappings, argType, true)
}
c.errors.noMatchingOverload(call.GetId(), c.location(call), fn.Name(), argTypes, target != nil)
c.errors.noMatchingOverload(call.ID(), c.location(call), fn.Name(), argTypes, target != nil)
return nil
}
return newResolution(checkedRef, resultType)
}
func (c *checker) checkCreateList(e *exprpb.Expr) {
create := e.GetListExpr()
func (c *checker) checkCreateList(e ast.Expr) {
create := e.AsList()
var elemsType *types.Type
optionalIndices := create.GetOptionalIndices()
optionalIndices := create.OptionalIndices()
optionals := make(map[int32]bool, len(optionalIndices))
for _, optInd := range optionalIndices {
optionals[optInd] = true
}
for i, e := range create.GetElements() {
for i, e := range create.Elements() {
c.check(e)
elemType := c.getType(e)
if optionals[int32(i)] {
var isOptional bool
elemType, isOptional = maybeUnwrapOptional(elemType)
if !isOptional && !isDyn(elemType) {
c.errors.typeMismatch(e.GetId(), c.location(e), types.NewOptionalType(elemType), elemType)
c.errors.typeMismatch(e.ID(), c.location(e), types.NewOptionalType(elemType), elemType)
}
}
elemsType = c.joinTypes(e, elemsType, elemType)
@ -442,32 +396,24 @@ func (c *checker) checkCreateList(e *exprpb.Expr) {
c.setType(e, types.NewListType(elemsType))
}
func (c *checker) checkCreateStruct(e *exprpb.Expr) {
str := e.GetStructExpr()
if str.GetMessageName() != "" {
c.checkCreateMessage(e)
} else {
c.checkCreateMap(e)
}
}
func (c *checker) checkCreateMap(e *exprpb.Expr) {
mapVal := e.GetStructExpr()
func (c *checker) checkCreateMap(e ast.Expr) {
mapVal := e.AsMap()
var mapKeyType *types.Type
var mapValueType *types.Type
for _, ent := range mapVal.GetEntries() {
key := ent.GetMapKey()
for _, e := range mapVal.Entries() {
entry := e.AsMapEntry()
key := entry.Key()
c.check(key)
mapKeyType = c.joinTypes(key, mapKeyType, c.getType(key))
val := ent.GetValue()
val := entry.Value()
c.check(val)
valType := c.getType(val)
if ent.GetOptionalEntry() {
if entry.IsOptional() {
var isOptional bool
valType, isOptional = maybeUnwrapOptional(valType)
if !isOptional && !isDyn(valType) {
c.errors.typeMismatch(val.GetId(), c.location(val), types.NewOptionalType(valType), valType)
c.errors.typeMismatch(val.ID(), c.location(val), types.NewOptionalType(valType), valType)
}
}
mapValueType = c.joinTypes(val, mapValueType, valType)
@ -480,25 +426,28 @@ func (c *checker) checkCreateMap(e *exprpb.Expr) {
c.setType(e, types.NewMapType(mapKeyType, mapValueType))
}
func (c *checker) checkCreateMessage(e *exprpb.Expr) {
msgVal := e.GetStructExpr()
func (c *checker) checkCreateStruct(e ast.Expr) {
msgVal := e.AsStruct()
// Determine the type of the message.
resultType := types.ErrorType
ident := c.env.LookupIdent(msgVal.GetMessageName())
ident := c.env.LookupIdent(msgVal.TypeName())
if ident == nil {
c.errors.undeclaredReference(
e.GetId(), c.location(e), c.env.container.Name(), msgVal.GetMessageName())
e.ID(), c.location(e), c.env.container.Name(), msgVal.TypeName())
c.setType(e, types.ErrorType)
return
}
// Ensure the type name is fully qualified in the AST.
typeName := ident.Name()
msgVal.MessageName = typeName
c.setReference(e, ast.NewIdentReference(ident.Name(), nil))
if msgVal.TypeName() != typeName {
e.SetKindCase(c.NewStruct(e.ID(), typeName, msgVal.Fields()))
msgVal = e.AsStruct()
}
c.setReference(e, ast.NewIdentReference(typeName, nil))
identKind := ident.Type().Kind()
if identKind != types.ErrorKind {
if identKind != types.TypeKind {
c.errors.notAType(e.GetId(), c.location(e), ident.Type().DeclaredTypeName())
c.errors.notAType(e.ID(), c.location(e), ident.Type().DeclaredTypeName())
} else {
resultType = ident.Type().Parameters()[0]
// Backwards compatibility test between well-known types and message types
@ -509,7 +458,7 @@ func (c *checker) checkCreateMessage(e *exprpb.Expr) {
} else if resultType.Kind() == types.StructKind {
typeName = resultType.DeclaredTypeName()
} else {
c.errors.notAMessageType(e.GetId(), c.location(e), resultType.DeclaredTypeName())
c.errors.notAMessageType(e.ID(), c.location(e), resultType.DeclaredTypeName())
resultType = types.ErrorType
}
}
@ -517,37 +466,38 @@ func (c *checker) checkCreateMessage(e *exprpb.Expr) {
c.setType(e, resultType)
// Check the field initializers.
for _, ent := range msgVal.GetEntries() {
field := ent.GetFieldKey()
value := ent.GetValue()
for _, f := range msgVal.Fields() {
field := f.AsStructField()
fieldName := field.Name()
value := field.Value()
c.check(value)
fieldType := types.ErrorType
ft, found := c.lookupFieldType(ent.GetId(), typeName, field)
ft, found := c.lookupFieldType(f.ID(), typeName, fieldName)
if found {
fieldType = ft
}
valType := c.getType(value)
if ent.GetOptionalEntry() {
if field.IsOptional() {
var isOptional bool
valType, isOptional = maybeUnwrapOptional(valType)
if !isOptional && !isDyn(valType) {
c.errors.typeMismatch(value.GetId(), c.location(value), types.NewOptionalType(valType), valType)
c.errors.typeMismatch(value.ID(), c.location(value), types.NewOptionalType(valType), valType)
}
}
if !c.isAssignable(fieldType, valType) {
c.errors.fieldTypeMismatch(ent.GetId(), c.locationByID(ent.GetId()), field, fieldType, valType)
c.errors.fieldTypeMismatch(f.ID(), c.locationByID(f.ID()), fieldName, fieldType, valType)
}
}
}
func (c *checker) checkComprehension(e *exprpb.Expr) {
comp := e.GetComprehensionExpr()
c.check(comp.GetIterRange())
c.check(comp.GetAccuInit())
accuType := c.getType(comp.GetAccuInit())
rangeType := substitute(c.mappings, c.getType(comp.GetIterRange()), false)
func (c *checker) checkComprehension(e ast.Expr) {
comp := e.AsComprehension()
c.check(comp.IterRange())
c.check(comp.AccuInit())
accuType := c.getType(comp.AccuInit())
rangeType := substitute(c.mappings, c.getType(comp.IterRange()), false)
var varType *types.Type
switch rangeType.Kind() {
@ -564,32 +514,32 @@ func (c *checker) checkComprehension(e *exprpb.Expr) {
// Set the range iteration variable to type DYN as well.
varType = types.DynType
default:
c.errors.notAComprehensionRange(comp.GetIterRange().GetId(), c.location(comp.GetIterRange()), rangeType)
c.errors.notAComprehensionRange(comp.IterRange().ID(), c.location(comp.IterRange()), rangeType)
varType = types.ErrorType
}
// Create a scope for the comprehension since it has a local accumulation variable.
// This scope will contain the accumulation variable used to compute the result.
c.env = c.env.enterScope()
c.env.AddIdents(decls.NewVariable(comp.GetAccuVar(), accuType))
c.env.AddIdents(decls.NewVariable(comp.AccuVar(), accuType))
// Create a block scope for the loop.
c.env = c.env.enterScope()
c.env.AddIdents(decls.NewVariable(comp.GetIterVar(), varType))
c.env.AddIdents(decls.NewVariable(comp.IterVar(), varType))
// Check the variable references in the condition and step.
c.check(comp.GetLoopCondition())
c.assertType(comp.GetLoopCondition(), types.BoolType)
c.check(comp.GetLoopStep())
c.assertType(comp.GetLoopStep(), accuType)
c.check(comp.LoopCondition())
c.assertType(comp.LoopCondition(), types.BoolType)
c.check(comp.LoopStep())
c.assertType(comp.LoopStep(), accuType)
// Exit the loop's block scope before checking the result.
c.env = c.env.exitScope()
c.check(comp.GetResult())
c.check(comp.Result())
// Exit the comprehension scope.
c.env = c.env.exitScope()
c.setType(e, substitute(c.mappings, c.getType(comp.GetResult()), false))
c.setType(e, substitute(c.mappings, c.getType(comp.Result()), false))
}
// Checks compatibility of joined types, and returns the most general common type.
func (c *checker) joinTypes(e *exprpb.Expr, previous, current *types.Type) *types.Type {
func (c *checker) joinTypes(e ast.Expr, previous, current *types.Type) *types.Type {
if previous == nil {
return current
}
@ -599,7 +549,7 @@ func (c *checker) joinTypes(e *exprpb.Expr, previous, current *types.Type) *type
if c.dynAggregateLiteralElementTypesEnabled() {
return types.DynType
}
c.errors.typeMismatch(e.GetId(), c.location(e), previous, current)
c.errors.typeMismatch(e.ID(), c.location(e), previous, current)
return types.ErrorType
}
@ -633,41 +583,41 @@ func (c *checker) isAssignableList(l1, l2 []*types.Type) bool {
return false
}
func maybeUnwrapString(e *exprpb.Expr) (string, bool) {
switch e.GetExprKind().(type) {
case *exprpb.Expr_ConstExpr:
literal := e.GetConstExpr()
switch literal.GetConstantKind().(type) {
case *exprpb.Constant_StringValue:
return literal.GetStringValue(), true
func maybeUnwrapString(e ast.Expr) (string, bool) {
switch e.Kind() {
case ast.LiteralKind:
literal := e.AsLiteral()
switch v := literal.(type) {
case types.String:
return string(v), true
}
}
return "", false
}
func (c *checker) setType(e *exprpb.Expr, t *types.Type) {
if old, found := c.types[e.GetId()]; found && !old.IsExactType(t) {
c.errors.incompatibleType(e.GetId(), c.location(e), e, old, t)
func (c *checker) setType(e ast.Expr, t *types.Type) {
if old, found := c.TypeMap()[e.ID()]; found && !old.IsExactType(t) {
c.errors.incompatibleType(e.ID(), c.location(e), e, old, t)
return
}
c.types[e.GetId()] = t
c.SetType(e.ID(), t)
}
func (c *checker) getType(e *exprpb.Expr) *types.Type {
return c.types[e.GetId()]
func (c *checker) getType(e ast.Expr) *types.Type {
return c.TypeMap()[e.ID()]
}
func (c *checker) setReference(e *exprpb.Expr, r *ast.ReferenceInfo) {
if old, found := c.references[e.GetId()]; found && !old.Equals(r) {
c.errors.referenceRedefinition(e.GetId(), c.location(e), e, old, r)
func (c *checker) setReference(e ast.Expr, r *ast.ReferenceInfo) {
if old, found := c.ReferenceMap()[e.ID()]; found && !old.Equals(r) {
c.errors.referenceRedefinition(e.ID(), c.location(e), e, old, r)
return
}
c.references[e.GetId()] = r
c.SetReference(e.ID(), r)
}
func (c *checker) assertType(e *exprpb.Expr, t *types.Type) {
func (c *checker) assertType(e ast.Expr, t *types.Type) {
if !c.isAssignable(t, c.getType(e)) {
c.errors.typeMismatch(e.GetId(), c.location(e), t, c.getType(e))
c.errors.typeMismatch(e.ID(), c.location(e), t, c.getType(e))
}
}
@ -683,26 +633,12 @@ func newResolution(r *ast.ReferenceInfo, t *types.Type) *overloadResolution {
}
}
func (c *checker) location(e *exprpb.Expr) common.Location {
return c.locationByID(e.GetId())
func (c *checker) location(e ast.Expr) common.Location {
return c.locationByID(e.ID())
}
func (c *checker) locationByID(id int64) common.Location {
positions := c.sourceInfo.GetPositions()
var line = 1
if offset, found := positions[id]; found {
col := int(offset)
for _, lineOffset := range c.sourceInfo.GetLineOffsets() {
if lineOffset < offset {
line++
col = int(offset - lineOffset)
} else {
break
}
}
return common.NewLocation(line, col)
}
return common.NoLocation
return c.SourceInfo().GetStartLocation(id)
}
func (c *checker) lookupFieldType(exprID int64, structType, fieldName string) (*types.Type, bool) {

View File

@ -22,8 +22,6 @@ import (
"github.com/google/cel-go/common/overloads"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/parser"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
// WARNING: Any changes to cost calculations in this file require a corresponding change in interpreter/runtimecost.go
@ -58,7 +56,7 @@ type AstNode interface {
// Type returns the deduced type of the AstNode.
Type() *types.Type
// Expr returns the expression of the AstNode.
Expr() *exprpb.Expr
Expr() ast.Expr
// ComputedSize returns a size estimate of the AstNode derived from information available in the CEL expression.
// For constants and inline list and map declarations, the exact size is returned. For concatenated list, strings
// and bytes, the size is derived from the size estimates of the operands. nil is returned if there is no
@ -69,7 +67,7 @@ type AstNode interface {
type astNode struct {
path []string
t *types.Type
expr *exprpb.Expr
expr ast.Expr
derivedSize *SizeEstimate
}
@ -81,7 +79,7 @@ func (e astNode) Type() *types.Type {
return e.t
}
func (e astNode) Expr() *exprpb.Expr {
func (e astNode) Expr() ast.Expr {
return e.expr
}
@ -90,29 +88,27 @@ func (e astNode) ComputedSize() *SizeEstimate {
return e.derivedSize
}
var v uint64
switch ek := e.expr.GetExprKind().(type) {
case *exprpb.Expr_ConstExpr:
switch ck := ek.ConstExpr.GetConstantKind().(type) {
case *exprpb.Constant_StringValue:
switch e.expr.Kind() {
case ast.LiteralKind:
switch ck := e.expr.AsLiteral().(type) {
case types.String:
// converting to runes here is an O(n) operation, but
// this is consistent with how size is computed at runtime,
// and how the language definition defines string size
v = uint64(len([]rune(ck.StringValue)))
case *exprpb.Constant_BytesValue:
v = uint64(len(ck.BytesValue))
case *exprpb.Constant_BoolValue, *exprpb.Constant_DoubleValue, *exprpb.Constant_DurationValue,
*exprpb.Constant_Int64Value, *exprpb.Constant_TimestampValue, *exprpb.Constant_Uint64Value,
*exprpb.Constant_NullValue:
v = uint64(len([]rune(ck)))
case types.Bytes:
v = uint64(len(ck))
case types.Bool, types.Double, types.Duration,
types.Int, types.Timestamp, types.Uint,
types.Null:
v = uint64(1)
default:
return nil
}
case *exprpb.Expr_ListExpr:
v = uint64(len(ek.ListExpr.GetElements()))
case *exprpb.Expr_StructExpr:
if ek.StructExpr.GetMessageName() == "" {
v = uint64(len(ek.StructExpr.GetEntries()))
}
case ast.ListKind:
v = uint64(e.expr.AsList().Size())
case ast.MapKind:
v = uint64(e.expr.AsMap().Size())
default:
return nil
}
@ -265,7 +261,7 @@ type coster struct {
iterRanges iterRangeScopes
// computedSizes tracks the computed sizes of call results.
computedSizes map[int64]SizeEstimate
checkedAST *ast.CheckedAST
checkedAST *ast.AST
estimator CostEstimator
overloadEstimators map[string]FunctionEstimator
// presenceTestCost will either be a zero or one based on whether has() macros count against cost computations.
@ -275,8 +271,8 @@ type coster struct {
// Use a stack of iterVar -> iterRange Expr Ids to handle shadowed variable names.
type iterRangeScopes map[string][]int64
func (vs iterRangeScopes) push(varName string, expr *exprpb.Expr) {
vs[varName] = append(vs[varName], expr.GetId())
func (vs iterRangeScopes) push(varName string, expr ast.Expr) {
vs[varName] = append(vs[varName], expr.ID())
}
func (vs iterRangeScopes) pop(varName string) {
@ -324,9 +320,9 @@ func OverloadCostEstimate(overloadID string, functionCoster FunctionEstimator) C
}
// Cost estimates the cost of the parsed and type checked CEL expression.
func Cost(checker *ast.CheckedAST, estimator CostEstimator, opts ...CostOption) (CostEstimate, error) {
func Cost(checked *ast.AST, estimator CostEstimator, opts ...CostOption) (CostEstimate, error) {
c := &coster{
checkedAST: checker,
checkedAST: checked,
estimator: estimator,
overloadEstimators: map[string]FunctionEstimator{},
exprPath: map[int64][]string{},
@ -340,28 +336,30 @@ func Cost(checker *ast.CheckedAST, estimator CostEstimator, opts ...CostOption)
return CostEstimate{}, err
}
}
return c.cost(checker.Expr), nil
return c.cost(checked.Expr()), nil
}
func (c *coster) cost(e *exprpb.Expr) CostEstimate {
func (c *coster) cost(e ast.Expr) CostEstimate {
if e == nil {
return CostEstimate{}
}
var cost CostEstimate
switch e.GetExprKind().(type) {
case *exprpb.Expr_ConstExpr:
switch e.Kind() {
case ast.LiteralKind:
cost = constCost
case *exprpb.Expr_IdentExpr:
case ast.IdentKind:
cost = c.costIdent(e)
case *exprpb.Expr_SelectExpr:
case ast.SelectKind:
cost = c.costSelect(e)
case *exprpb.Expr_CallExpr:
case ast.CallKind:
cost = c.costCall(e)
case *exprpb.Expr_ListExpr:
case ast.ListKind:
cost = c.costCreateList(e)
case *exprpb.Expr_StructExpr:
case ast.MapKind:
cost = c.costCreateMap(e)
case ast.StructKind:
cost = c.costCreateStruct(e)
case *exprpb.Expr_ComprehensionExpr:
case ast.ComprehensionKind:
cost = c.costComprehension(e)
default:
return CostEstimate{}
@ -369,53 +367,51 @@ func (c *coster) cost(e *exprpb.Expr) CostEstimate {
return cost
}
func (c *coster) costIdent(e *exprpb.Expr) CostEstimate {
identExpr := e.GetIdentExpr()
func (c *coster) costIdent(e ast.Expr) CostEstimate {
identName := e.AsIdent()
// build and track the field path
if iterRange, ok := c.iterRanges.peek(identExpr.GetName()); ok {
switch c.checkedAST.TypeMap[iterRange].Kind() {
if iterRange, ok := c.iterRanges.peek(identName); ok {
switch c.checkedAST.GetType(iterRange).Kind() {
case types.ListKind:
c.addPath(e, append(c.exprPath[iterRange], "@items"))
case types.MapKind:
c.addPath(e, append(c.exprPath[iterRange], "@keys"))
}
} else {
c.addPath(e, []string{identExpr.GetName()})
c.addPath(e, []string{identName})
}
return selectAndIdentCost
}
func (c *coster) costSelect(e *exprpb.Expr) CostEstimate {
sel := e.GetSelectExpr()
func (c *coster) costSelect(e ast.Expr) CostEstimate {
sel := e.AsSelect()
var sum CostEstimate
if sel.GetTestOnly() {
if sel.IsTestOnly() {
// recurse, but do not add any cost
// this is equivalent to how evalTestOnly increments the runtime cost counter
// but does not add any additional cost for the qualifier, except here we do
// the reverse (ident adds cost)
sum = sum.Add(c.presenceTestCost)
sum = sum.Add(c.cost(sel.GetOperand()))
sum = sum.Add(c.cost(sel.Operand()))
return sum
}
sum = sum.Add(c.cost(sel.GetOperand()))
targetType := c.getType(sel.GetOperand())
sum = sum.Add(c.cost(sel.Operand()))
targetType := c.getType(sel.Operand())
switch targetType.Kind() {
case types.MapKind, types.StructKind, types.TypeParamKind:
sum = sum.Add(selectAndIdentCost)
}
// build and track the field path
c.addPath(e, append(c.getPath(sel.GetOperand()), sel.GetField()))
c.addPath(e, append(c.getPath(sel.Operand()), sel.FieldName()))
return sum
}
func (c *coster) costCall(e *exprpb.Expr) CostEstimate {
call := e.GetCallExpr()
target := call.GetTarget()
args := call.GetArgs()
func (c *coster) costCall(e ast.Expr) CostEstimate {
call := e.AsCall()
args := call.Args()
var sum CostEstimate
@ -426,22 +422,20 @@ func (c *coster) costCall(e *exprpb.Expr) CostEstimate {
argTypes[i] = c.newAstNode(arg)
}
ref := c.checkedAST.ReferenceMap[e.GetId()]
if ref == nil || len(ref.OverloadIDs) == 0 {
overloadIDs := c.checkedAST.GetOverloadIDs(e.ID())
if len(overloadIDs) == 0 {
return CostEstimate{}
}
var targetType AstNode
if target != nil {
if call.Target != nil {
sum = sum.Add(c.cost(call.GetTarget()))
targetType = c.newAstNode(call.GetTarget())
}
if call.IsMemberFunction() {
sum = sum.Add(c.cost(call.Target()))
targetType = c.newAstNode(call.Target())
}
// Pick a cost estimate range that covers all the overload cost estimation ranges
fnCost := CostEstimate{Min: uint64(math.MaxUint64), Max: 0}
var resultSize *SizeEstimate
for _, overload := range ref.OverloadIDs {
overloadCost := c.functionCost(call.GetFunction(), overload, &targetType, argTypes, argCosts)
for _, overload := range overloadIDs {
overloadCost := c.functionCost(call.FunctionName(), overload, &targetType, argTypes, argCosts)
fnCost = fnCost.Union(overloadCost.CostEstimate)
if overloadCost.ResultSize != nil {
if resultSize == nil {
@ -464,64 +458,56 @@ func (c *coster) costCall(e *exprpb.Expr) CostEstimate {
}
}
if resultSize != nil {
c.computedSizes[e.GetId()] = *resultSize
c.computedSizes[e.ID()] = *resultSize
}
return sum.Add(fnCost)
}
func (c *coster) costCreateList(e *exprpb.Expr) CostEstimate {
create := e.GetListExpr()
func (c *coster) costCreateList(e ast.Expr) CostEstimate {
create := e.AsList()
var sum CostEstimate
for _, e := range create.GetElements() {
for _, e := range create.Elements() {
sum = sum.Add(c.cost(e))
}
return sum.Add(createListBaseCost)
}
func (c *coster) costCreateStruct(e *exprpb.Expr) CostEstimate {
str := e.GetStructExpr()
if str.MessageName != "" {
return c.costCreateMessage(e)
}
return c.costCreateMap(e)
}
func (c *coster) costCreateMap(e *exprpb.Expr) CostEstimate {
mapVal := e.GetStructExpr()
func (c *coster) costCreateMap(e ast.Expr) CostEstimate {
mapVal := e.AsMap()
var sum CostEstimate
for _, ent := range mapVal.GetEntries() {
key := ent.GetMapKey()
sum = sum.Add(c.cost(key))
sum = sum.Add(c.cost(ent.GetValue()))
for _, ent := range mapVal.Entries() {
entry := ent.AsMapEntry()
sum = sum.Add(c.cost(entry.Key()))
sum = sum.Add(c.cost(entry.Value()))
}
return sum.Add(createMapBaseCost)
}
func (c *coster) costCreateMessage(e *exprpb.Expr) CostEstimate {
msgVal := e.GetStructExpr()
func (c *coster) costCreateStruct(e ast.Expr) CostEstimate {
msgVal := e.AsStruct()
var sum CostEstimate
for _, ent := range msgVal.GetEntries() {
sum = sum.Add(c.cost(ent.GetValue()))
for _, ent := range msgVal.Fields() {
field := ent.AsStructField()
sum = sum.Add(c.cost(field.Value()))
}
return sum.Add(createMessageBaseCost)
}
func (c *coster) costComprehension(e *exprpb.Expr) CostEstimate {
comp := e.GetComprehensionExpr()
func (c *coster) costComprehension(e ast.Expr) CostEstimate {
comp := e.AsComprehension()
var sum CostEstimate
sum = sum.Add(c.cost(comp.GetIterRange()))
sum = sum.Add(c.cost(comp.GetAccuInit()))
sum = sum.Add(c.cost(comp.IterRange()))
sum = sum.Add(c.cost(comp.AccuInit()))
// Track the iterRange of each IterVar for field path construction
c.iterRanges.push(comp.GetIterVar(), comp.GetIterRange())
loopCost := c.cost(comp.GetLoopCondition())
stepCost := c.cost(comp.GetLoopStep())
c.iterRanges.pop(comp.GetIterVar())
sum = sum.Add(c.cost(comp.Result))
rangeCnt := c.sizeEstimate(c.newAstNode(comp.GetIterRange()))
c.iterRanges.push(comp.IterVar(), comp.IterRange())
loopCost := c.cost(comp.LoopCondition())
stepCost := c.cost(comp.LoopStep())
c.iterRanges.pop(comp.IterVar())
sum = sum.Add(c.cost(comp.Result()))
rangeCnt := c.sizeEstimate(c.newAstNode(comp.IterRange()))
c.computedSizes[e.GetId()] = rangeCnt
c.computedSizes[e.ID()] = rangeCnt
rangeCost := rangeCnt.MultiplyByCost(stepCost.Add(loopCost))
sum = sum.Add(rangeCost)
@ -674,26 +660,26 @@ func (c *coster) functionCost(function, overloadID string, target *AstNode, args
return CallEstimate{CostEstimate: CostEstimate{Min: 1, Max: 1}.Add(argCostSum())}
}
func (c *coster) getType(e *exprpb.Expr) *types.Type {
return c.checkedAST.TypeMap[e.GetId()]
func (c *coster) getType(e ast.Expr) *types.Type {
return c.checkedAST.GetType(e.ID())
}
func (c *coster) getPath(e *exprpb.Expr) []string {
return c.exprPath[e.GetId()]
func (c *coster) getPath(e ast.Expr) []string {
return c.exprPath[e.ID()]
}
func (c *coster) addPath(e *exprpb.Expr, path []string) {
c.exprPath[e.GetId()] = path
func (c *coster) addPath(e ast.Expr, path []string) {
c.exprPath[e.ID()] = path
}
func (c *coster) newAstNode(e *exprpb.Expr) *astNode {
func (c *coster) newAstNode(e ast.Expr) *astNode {
path := c.getPath(e)
if len(path) > 0 && path[0] == parser.AccumulatorName {
// only provide paths to root vars; omit accumulator vars
path = nil
}
var derivedSize *SizeEstimate
if size, ok := c.computedSizes[e.GetId()]; ok {
if size, ok := c.computedSizes[e.ID()]; ok {
derivedSize = &size
}
return &astNode{

View File

@ -67,7 +67,7 @@ func NewAbstractType(name string, paramTypes ...*exprpb.Type) *exprpb.Type {
// NewOptionalType constructs an abstract type indicating that the parameterized type
// may be contained within the object.
func NewOptionalType(paramType *exprpb.Type) *exprpb.Type {
return NewAbstractType("optional", paramType)
return NewAbstractType("optional_type", paramType)
}
// NewFunctionType creates a function invocation contract, typically only used

View File

@ -146,6 +146,14 @@ func (e *Env) LookupIdent(name string) *decls.VariableDecl {
return decl
}
if i, found := e.provider.FindIdent(candidate); found {
if t, ok := i.(*types.Type); ok {
decl := decls.NewVariable(candidate, types.NewTypeTypeWithParam(t))
e.declarations.AddIdent(decl)
return decl
}
}
// Next try to import this as an enum value by splitting the name in a type prefix and
// the enum inside.
if enumValue := e.provider.EnumValue(candidate); enumValue.Type() != types.ErrType {

View File

@ -15,13 +15,9 @@
package checker
import (
"reflect"
"github.com/google/cel-go/common"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/types"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
// typeErrors is a specialization of Errors.
@ -34,9 +30,9 @@ func (e *typeErrors) fieldTypeMismatch(id int64, l common.Location, name string,
name, FormatCELType(field), FormatCELType(value))
}
func (e *typeErrors) incompatibleType(id int64, l common.Location, ex *exprpb.Expr, prev, next *types.Type) {
func (e *typeErrors) incompatibleType(id int64, l common.Location, ex ast.Expr, prev, next *types.Type) {
e.errs.ReportErrorAtID(id, l,
"incompatible type already exists for expression: %v(%d) old:%v, new:%v", ex, ex.GetId(), prev, next)
"incompatible type already exists for expression: %v(%d) old:%v, new:%v", ex, ex.ID(), prev, next)
}
func (e *typeErrors) noMatchingOverload(id int64, l common.Location, name string, args []*types.Type, isInstance bool) {
@ -49,7 +45,7 @@ func (e *typeErrors) notAComprehensionRange(id int64, l common.Location, t *type
FormatCELType(t))
}
func (e *typeErrors) notAnOptionalFieldSelection(id int64, l common.Location, field *exprpb.Expr) {
func (e *typeErrors) notAnOptionalFieldSelection(id int64, l common.Location, field ast.Expr) {
e.errs.ReportErrorAtID(id, l, "unsupported optional field selection: %v", field)
}
@ -61,9 +57,9 @@ func (e *typeErrors) notAMessageType(id int64, l common.Location, typeName strin
e.errs.ReportErrorAtID(id, l, "'%s' is not a message type", typeName)
}
func (e *typeErrors) referenceRedefinition(id int64, l common.Location, ex *exprpb.Expr, prev, next *ast.ReferenceInfo) {
func (e *typeErrors) referenceRedefinition(id int64, l common.Location, ex ast.Expr, prev, next *ast.ReferenceInfo) {
e.errs.ReportErrorAtID(id, l,
"reference already exists for expression: %v(%d) old:%v, new:%v", ex, ex.GetId(), prev, next)
"reference already exists for expression: %v(%d) old:%v, new:%v", ex, ex.ID(), prev, next)
}
func (e *typeErrors) typeDoesNotSupportFieldSelection(id int64, l common.Location, t *types.Type) {
@ -87,6 +83,6 @@ func (e *typeErrors) unexpectedFailedResolution(id int64, l common.Location, typ
e.errs.ReportErrorAtID(id, l, "unexpected failed resolution of '%s'", typeName)
}
func (e *typeErrors) unexpectedASTType(id int64, l common.Location, ex *exprpb.Expr) {
e.errs.ReportErrorAtID(id, l, "unrecognized ast type: %v", reflect.TypeOf(ex))
func (e *typeErrors) unexpectedASTType(id int64, l common.Location, kind, typeName string) {
e.errs.ReportErrorAtID(id, l, "unexpected %s type: %v", kind, typeName)
}

View File

@ -17,40 +17,40 @@ package checker
import (
"sort"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/debug"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
type semanticAdorner struct {
checks *exprpb.CheckedExpr
checked *ast.AST
}
var _ debug.Adorner = &semanticAdorner{}
func (a *semanticAdorner) GetMetadata(elem any) string {
result := ""
e, isExpr := elem.(*exprpb.Expr)
e, isExpr := elem.(ast.Expr)
if !isExpr {
return result
}
t := a.checks.TypeMap[e.GetId()]
t := a.checked.TypeMap()[e.ID()]
if t != nil {
result += "~"
result += FormatCheckedType(t)
result += FormatCELType(t)
}
switch e.GetExprKind().(type) {
case *exprpb.Expr_IdentExpr,
*exprpb.Expr_CallExpr,
*exprpb.Expr_StructExpr,
*exprpb.Expr_SelectExpr:
if ref, found := a.checks.ReferenceMap[e.GetId()]; found {
if len(ref.GetOverloadId()) == 0 {
switch e.Kind() {
case ast.IdentKind,
ast.CallKind,
ast.ListKind,
ast.StructKind,
ast.SelectKind:
if ref, found := a.checked.ReferenceMap()[e.ID()]; found {
if len(ref.OverloadIDs) == 0 {
result += "^" + ref.Name
} else {
sort.Strings(ref.GetOverloadId())
for i, overload := range ref.GetOverloadId() {
sort.Strings(ref.OverloadIDs)
for i, overload := range ref.OverloadIDs {
if i == 0 {
result += "^"
} else {
@ -68,7 +68,7 @@ func (a *semanticAdorner) GetMetadata(elem any) string {
// Print returns a string representation of the Expr message,
// annotated with types from the CheckedExpr. The Expr must
// be a sub-expression embedded in the CheckedExpr.
func Print(e *exprpb.Expr, checks *exprpb.CheckedExpr) string {
a := &semanticAdorner{checks: checks}
func Print(e ast.Expr, checked *ast.AST) string {
a := &semanticAdorner{checked: checked}
return debug.ToAdornedDebugString(e, a)
}

View File

@ -41,7 +41,7 @@ func isError(t *types.Type) bool {
func isOptional(t *types.Type) bool {
if t.Kind() == types.OpaqueKind {
return t.TypeName() == "optional"
return t.TypeName() == "optional_type"
}
return false
}
@ -137,7 +137,11 @@ func internalIsAssignable(m *mapping, t1, t2 *types.Type) bool {
case types.BoolKind, types.BytesKind, types.DoubleKind, types.IntKind, types.StringKind, types.UintKind,
types.AnyKind, types.DurationKind, types.TimestampKind,
types.StructKind:
return t1.IsAssignableType(t2)
// Test whether t2 is assignable from t1. The order of this check won't usually matter;
// however, there may be cases where type capabilities are expanded beyond what is supported
// in the current common/types package. For example, an interface designation for a group of
// Struct types.
return t2.IsAssignableType(t1)
case types.TypeKind:
return kind2 == types.TypeKind
case types.OpaqueKind, types.ListKind, types.MapKind:
@ -256,7 +260,7 @@ func notReferencedIn(m *mapping, t, withinType *types.Type) bool {
return true
}
return notReferencedIn(m, t, wtSub)
case types.OpaqueKind, types.ListKind, types.MapKind:
case types.OpaqueKind, types.ListKind, types.MapKind, types.TypeKind:
for _, pt := range withinType.Parameters() {
if !notReferencedIn(m, t, pt) {
return false
@ -288,7 +292,8 @@ func substitute(m *mapping, t *types.Type, typeParamToDyn bool) *types.Type {
substitute(m, t.Parameters()[1], typeParamToDyn))
case types.TypeKind:
if len(t.Parameters()) > 0 {
return types.NewTypeTypeWithParam(substitute(m, t.Parameters()[0], typeParamToDyn))
tParam := t.Parameters()[0]
return types.NewTypeTypeWithParam(substitute(m, tParam, typeParamToDyn))
}
return t
default: