mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 13:50:01 +00:00 
			
		
		
		
	Pin new dependency: github.com/google/cel-go v0.9.0
This commit is contained in:
		
							
								
								
									
										642
									
								
								vendor/github.com/google/cel-go/checker/checker.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										642
									
								
								vendor/github.com/google/cel-go/checker/checker.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,642 @@ | ||||
| // Copyright 2018 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 | ||||
| // | ||||
| //      http://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 checker defines functions to type-checked a parsed expression | ||||
| // against a set of identifier and function declarations. | ||||
| package checker | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
|  | ||||
| 	"github.com/google/cel-go/checker/decls" | ||||
| 	"github.com/google/cel-go/common" | ||||
| 	"github.com/google/cel-go/common/containers" | ||||
| 	"github.com/google/cel-go/common/types/ref" | ||||
|  | ||||
| 	"google.golang.org/protobuf/proto" | ||||
|  | ||||
| 	exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" | ||||
| ) | ||||
|  | ||||
| type checker struct { | ||||
| 	env                *Env | ||||
| 	errors             *typeErrors | ||||
| 	mappings           *mapping | ||||
| 	freeTypeVarCounter int | ||||
| 	sourceInfo         *exprpb.SourceInfo | ||||
| 	types              map[int64]*exprpb.Type | ||||
| 	references         map[int64]*exprpb.Reference | ||||
| } | ||||
|  | ||||
| // 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) (*exprpb.CheckedExpr, *common.Errors) { | ||||
| 	c := checker{ | ||||
| 		env:                env, | ||||
| 		errors:             &typeErrors{common.NewErrors(source)}, | ||||
| 		mappings:           newMapping(), | ||||
| 		freeTypeVarCounter: 0, | ||||
| 		sourceInfo:         parsedExpr.GetSourceInfo(), | ||||
| 		types:              make(map[int64]*exprpb.Type), | ||||
| 		references:         make(map[int64]*exprpb.Reference), | ||||
| 	} | ||||
| 	c.check(parsedExpr.GetExpr()) | ||||
|  | ||||
| 	// Walk over the final type map substituting any type parameters either by their bound value or | ||||
| 	// by DYN. | ||||
| 	m := make(map[int64]*exprpb.Type) | ||||
| 	for k, v := range c.types { | ||||
| 		m[k] = substitute(c.mappings, v, true) | ||||
| 	} | ||||
|  | ||||
| 	return &exprpb.CheckedExpr{ | ||||
| 		Expr:         parsedExpr.GetExpr(), | ||||
| 		SourceInfo:   parsedExpr.GetSourceInfo(), | ||||
| 		TypeMap:      m, | ||||
| 		ReferenceMap: c.references, | ||||
| 	}, c.errors.Errors | ||||
| } | ||||
|  | ||||
| func (c *checker) check(e *exprpb.Expr) { | ||||
| 	if e == nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	switch e.ExprKind.(type) { | ||||
| 	case *exprpb.Expr_ConstExpr: | ||||
| 		literal := e.GetConstExpr() | ||||
| 		switch literal.ConstantKind.(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) | ||||
| 		} | ||||
| 	case *exprpb.Expr_IdentExpr: | ||||
| 		c.checkIdent(e) | ||||
| 	case *exprpb.Expr_SelectExpr: | ||||
| 		c.checkSelect(e) | ||||
| 	case *exprpb.Expr_CallExpr: | ||||
| 		c.checkCall(e) | ||||
| 	case *exprpb.Expr_ListExpr: | ||||
| 		c.checkCreateList(e) | ||||
| 	case *exprpb.Expr_StructExpr: | ||||
| 		c.checkCreateStruct(e) | ||||
| 	case *exprpb.Expr_ComprehensionExpr: | ||||
| 		c.checkComprehension(e) | ||||
| 	default: | ||||
| 		c.errors.ReportError( | ||||
| 			c.location(e), "Unrecognized ast type: %v", reflect.TypeOf(e)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (c *checker) checkInt64Literal(e *exprpb.Expr) { | ||||
| 	c.setType(e, decls.Int) | ||||
| } | ||||
|  | ||||
| func (c *checker) checkUint64Literal(e *exprpb.Expr) { | ||||
| 	c.setType(e, decls.Uint) | ||||
| } | ||||
|  | ||||
| func (c *checker) checkStringLiteral(e *exprpb.Expr) { | ||||
| 	c.setType(e, decls.String) | ||||
| } | ||||
|  | ||||
| func (c *checker) checkBytesLiteral(e *exprpb.Expr) { | ||||
| 	c.setType(e, decls.Bytes) | ||||
| } | ||||
|  | ||||
| func (c *checker) checkDoubleLiteral(e *exprpb.Expr) { | ||||
| 	c.setType(e, decls.Double) | ||||
| } | ||||
|  | ||||
| func (c *checker) checkBoolLiteral(e *exprpb.Expr) { | ||||
| 	c.setType(e, decls.Bool) | ||||
| } | ||||
|  | ||||
| func (c *checker) checkNullLiteral(e *exprpb.Expr) { | ||||
| 	c.setType(e, decls.Null) | ||||
| } | ||||
|  | ||||
| func (c *checker) checkIdent(e *exprpb.Expr) { | ||||
| 	identExpr := e.GetIdentExpr() | ||||
| 	// Check to see if the identifier is declared. | ||||
| 	if ident := c.env.LookupIdent(identExpr.GetName()); ident != nil { | ||||
| 		c.setType(e, ident.GetIdent().Type) | ||||
| 		c.setReference(e, newIdentReference(ident.GetName(), ident.GetIdent().Value)) | ||||
| 		// Overwrite the identifier with its fully qualified name. | ||||
| 		identExpr.Name = ident.GetName() | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	c.setType(e, decls.Error) | ||||
| 	c.errors.undeclaredReference( | ||||
| 		c.location(e), c.env.container.Name(), identExpr.GetName()) | ||||
| } | ||||
|  | ||||
| func (c *checker) checkSelect(e *exprpb.Expr) { | ||||
| 	sel := e.GetSelectExpr() | ||||
| 	// Before traversing down the tree, try to interpret as qualified name. | ||||
| 	qname, found := containers.ToQualifiedName(e) | ||||
| 	if found { | ||||
| 		ident := c.env.LookupIdent(qname) | ||||
| 		if ident != nil { | ||||
| 			if sel.TestOnly { | ||||
| 				c.errors.expressionDoesNotSelectField(c.location(e)) | ||||
| 				c.setType(e, decls.Bool) | ||||
| 				return | ||||
| 			} | ||||
| 			// Rewrite the node to be a variable reference to the resolved fully-qualified | ||||
| 			// variable name. | ||||
| 			c.setType(e, ident.GetIdent().Type) | ||||
| 			c.setReference(e, newIdentReference(ident.GetName(), ident.GetIdent().Value)) | ||||
| 			identName := ident.GetName() | ||||
| 			e.ExprKind = &exprpb.Expr_IdentExpr{ | ||||
| 				IdentExpr: &exprpb.Expr_Ident{ | ||||
| 					Name: identName, | ||||
| 				}, | ||||
| 			} | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Interpret as field selection, first traversing down the operand. | ||||
| 	c.check(sel.Operand) | ||||
| 	targetType := c.getType(sel.Operand) | ||||
| 	// Assume error type by default as most types do not support field selection. | ||||
| 	resultType := decls.Error | ||||
| 	switch kindOf(targetType) { | ||||
| 	case kindMap: | ||||
| 		// Maps yield their value type as the selection result type. | ||||
| 		mapType := targetType.GetMapType() | ||||
| 		resultType = mapType.ValueType | ||||
| 	case kindObject: | ||||
| 		// 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( | ||||
| 			c.location(e), | ||||
| 			messageType.GetMessageType(), | ||||
| 			sel.Field); found { | ||||
| 			resultType = fieldType.Type | ||||
| 		} | ||||
| 	case kindTypeParam: | ||||
| 		// Set the operand type to DYN to prevent assignment to a potentionally incorrect type | ||||
| 		// at a later point in type-checking. The isAssignable call will update the type | ||||
| 		// substitutions for the type param under the covers. | ||||
| 		c.isAssignable(decls.Dyn, targetType) | ||||
| 		// Also, set the result type to DYN. | ||||
| 		resultType = decls.Dyn | ||||
| 	default: | ||||
| 		// 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) { | ||||
| 			resultType = decls.Dyn | ||||
| 		} else { | ||||
| 			c.errors.typeDoesNotSupportFieldSelection(c.location(e), targetType) | ||||
| 		} | ||||
| 	} | ||||
| 	if sel.TestOnly { | ||||
| 		resultType = decls.Bool | ||||
| 	} | ||||
| 	c.setType(e, resultType) | ||||
| } | ||||
|  | ||||
| func (c *checker) checkCall(e *exprpb.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() | ||||
| 	target := call.GetTarget() | ||||
| 	args := call.GetArgs() | ||||
| 	fnName := call.GetFunction() | ||||
|  | ||||
| 	// Traverse arguments. | ||||
| 	for _, arg := range args { | ||||
| 		c.check(arg) | ||||
| 	} | ||||
|  | ||||
| 	// Regular static call with simple name. | ||||
| 	if target == nil { | ||||
| 		// Check for the existence of the function. | ||||
| 		fn := c.env.LookupFunction(fnName) | ||||
| 		if fn == nil { | ||||
| 			c.errors.undeclaredReference( | ||||
| 				c.location(e), c.env.container.Name(), fnName) | ||||
| 			c.setType(e, decls.Error) | ||||
| 			return | ||||
| 		} | ||||
| 		// Overwrite the function name with its fully qualified resolved name. | ||||
| 		call.Function = fn.GetName() | ||||
| 		// Check to see whether the overload resolves. | ||||
| 		c.resolveOverloadOrError(c.location(e), e, fn, nil, args) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// If a receiver 'target' is present, it may either be a receiver function, or a namespaced | ||||
| 	// function, but not both. Given a.b.c() either a.b.c is a function or c is a function with | ||||
| 	// target a.b. | ||||
| 	// | ||||
| 	// Check whether the target is a namespaced function name. | ||||
| 	qualifiedPrefix, maybeQualified := containers.ToQualifiedName(target) | ||||
| 	if maybeQualified { | ||||
| 		maybeQualifiedName := qualifiedPrefix + "." + fnName | ||||
| 		fn := c.env.LookupFunction(maybeQualifiedName) | ||||
| 		if fn != nil { | ||||
| 			// 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.GetName() | ||||
| 			c.resolveOverloadOrError(c.location(e), e, fn, nil, args) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Regular instance call. | ||||
| 	c.check(call.Target) | ||||
| 	fn := c.env.LookupFunction(fnName) | ||||
| 	// Function found, attempt overload resolution. | ||||
| 	if fn != nil { | ||||
| 		c.resolveOverloadOrError(c.location(e), e, fn, target, args) | ||||
| 		return | ||||
| 	} | ||||
| 	// Function name not declared, record error. | ||||
| 	c.errors.undeclaredReference(c.location(e), c.env.container.Name(), fnName) | ||||
| } | ||||
|  | ||||
| func (c *checker) resolveOverloadOrError( | ||||
| 	loc common.Location, | ||||
| 	e *exprpb.Expr, | ||||
| 	fn *exprpb.Decl, target *exprpb.Expr, args []*exprpb.Expr) { | ||||
| 	// Attempt to resolve the overload. | ||||
| 	resolution := c.resolveOverload(loc, fn, target, args) | ||||
| 	// No such overload, error noted in the resolveOverload call, type recorded here. | ||||
| 	if resolution == nil { | ||||
| 		c.setType(e, decls.Error) | ||||
| 		return | ||||
| 	} | ||||
| 	// Overload found. | ||||
| 	c.setType(e, resolution.Type) | ||||
| 	c.setReference(e, resolution.Reference) | ||||
| } | ||||
|  | ||||
| func (c *checker) resolveOverload( | ||||
| 	loc common.Location, | ||||
| 	fn *exprpb.Decl, target *exprpb.Expr, args []*exprpb.Expr) *overloadResolution { | ||||
|  | ||||
| 	var argTypes []*exprpb.Type | ||||
| 	if target != nil { | ||||
| 		argTypes = append(argTypes, c.getType(target)) | ||||
| 	} | ||||
| 	for _, arg := range args { | ||||
| 		argTypes = append(argTypes, c.getType(arg)) | ||||
| 	} | ||||
|  | ||||
| 	var resultType *exprpb.Type | ||||
| 	var checkedRef *exprpb.Reference | ||||
| 	for _, overload := range fn.GetFunction().Overloads { | ||||
| 		if (target == nil && overload.IsInstanceFunction) || | ||||
| 			(target != nil && !overload.IsInstanceFunction) { | ||||
| 			// not a compatible call style. | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		overloadType := decls.NewFunctionType(overload.ResultType, overload.Params...) | ||||
| 		if len(overload.TypeParams) > 0 { | ||||
| 			// Instantiate overload's type with fresh type variables. | ||||
| 			substitutions := newMapping() | ||||
| 			for _, typePar := range overload.TypeParams { | ||||
| 				substitutions.add(decls.NewTypeParamType(typePar), c.newTypeVar()) | ||||
| 			} | ||||
| 			overloadType = substitute(substitutions, overloadType, false) | ||||
| 		} | ||||
|  | ||||
| 		candidateArgTypes := overloadType.GetFunction().ArgTypes | ||||
| 		if c.isAssignableList(argTypes, candidateArgTypes) { | ||||
| 			if checkedRef == nil { | ||||
| 				checkedRef = newFunctionReference(overload.OverloadId) | ||||
| 			} else { | ||||
| 				checkedRef.OverloadId = append(checkedRef.OverloadId, overload.OverloadId) | ||||
| 			} | ||||
|  | ||||
| 			// First matching overload, determines result type. | ||||
| 			fnResultType := substitute(c.mappings, | ||||
| 				overloadType.GetFunction().ResultType, | ||||
| 				false) | ||||
| 			if resultType == nil { | ||||
| 				resultType = fnResultType | ||||
| 			} else if !isDyn(resultType) && !proto.Equal(fnResultType, resultType) { | ||||
| 				resultType = decls.Dyn | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if resultType == nil { | ||||
| 		c.errors.noMatchingOverload(loc, fn.GetName(), argTypes, target != nil) | ||||
| 		resultType = decls.Error | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return newResolution(checkedRef, resultType) | ||||
| } | ||||
|  | ||||
| func (c *checker) checkCreateList(e *exprpb.Expr) { | ||||
| 	create := e.GetListExpr() | ||||
| 	var elemType *exprpb.Type | ||||
| 	for _, e := range create.Elements { | ||||
| 		c.check(e) | ||||
| 		elemType = c.joinTypes(c.location(e), elemType, c.getType(e)) | ||||
| 	} | ||||
| 	if elemType == nil { | ||||
| 		// If the list is empty, assign free type var to elem type. | ||||
| 		elemType = c.newTypeVar() | ||||
| 	} | ||||
| 	c.setType(e, decls.NewListType(elemType)) | ||||
| } | ||||
|  | ||||
| func (c *checker) checkCreateStruct(e *exprpb.Expr) { | ||||
| 	str := e.GetStructExpr() | ||||
| 	if str.MessageName != "" { | ||||
| 		c.checkCreateMessage(e) | ||||
| 	} else { | ||||
| 		c.checkCreateMap(e) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (c *checker) checkCreateMap(e *exprpb.Expr) { | ||||
| 	mapVal := e.GetStructExpr() | ||||
| 	var keyType *exprpb.Type | ||||
| 	var valueType *exprpb.Type | ||||
| 	for _, ent := range mapVal.GetEntries() { | ||||
| 		key := ent.GetMapKey() | ||||
| 		c.check(key) | ||||
| 		keyType = c.joinTypes(c.location(key), keyType, c.getType(key)) | ||||
|  | ||||
| 		c.check(ent.Value) | ||||
| 		valueType = c.joinTypes(c.location(ent.Value), valueType, c.getType(ent.Value)) | ||||
| 	} | ||||
| 	if keyType == nil { | ||||
| 		// If the map is empty, assign free type variables to typeKey and value type. | ||||
| 		keyType = c.newTypeVar() | ||||
| 		valueType = c.newTypeVar() | ||||
| 	} | ||||
| 	c.setType(e, decls.NewMapType(keyType, valueType)) | ||||
| } | ||||
|  | ||||
| func (c *checker) checkCreateMessage(e *exprpb.Expr) { | ||||
| 	msgVal := e.GetStructExpr() | ||||
| 	// Determine the type of the message. | ||||
| 	messageType := decls.Error | ||||
| 	decl := c.env.LookupIdent(msgVal.MessageName) | ||||
| 	if decl == nil { | ||||
| 		c.errors.undeclaredReference( | ||||
| 			c.location(e), c.env.container.Name(), msgVal.MessageName) | ||||
| 		return | ||||
| 	} | ||||
| 	// Ensure the type name is fully qualified in the AST. | ||||
| 	msgVal.MessageName = decl.GetName() | ||||
| 	c.setReference(e, newIdentReference(decl.GetName(), nil)) | ||||
| 	ident := decl.GetIdent() | ||||
| 	identKind := kindOf(ident.Type) | ||||
| 	if identKind != kindError { | ||||
| 		if identKind != kindType { | ||||
| 			c.errors.notAType(c.location(e), ident.Type) | ||||
| 		} else { | ||||
| 			messageType = ident.Type.GetType() | ||||
| 			if kindOf(messageType) != kindObject { | ||||
| 				c.errors.notAMessageType(c.location(e), messageType) | ||||
| 				messageType = decls.Error | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if isObjectWellKnownType(messageType) { | ||||
| 		c.setType(e, getObjectWellKnownType(messageType)) | ||||
| 	} else { | ||||
| 		c.setType(e, messageType) | ||||
| 	} | ||||
|  | ||||
| 	// Check the field initializers. | ||||
| 	for _, ent := range msgVal.GetEntries() { | ||||
| 		field := ent.GetFieldKey() | ||||
| 		value := ent.Value | ||||
| 		c.check(value) | ||||
|  | ||||
| 		fieldType := decls.Error | ||||
| 		if t, found := c.lookupFieldType( | ||||
| 			c.locationByID(ent.Id), | ||||
| 			messageType.GetMessageType(), | ||||
| 			field); found { | ||||
| 			fieldType = t.Type | ||||
| 		} | ||||
| 		if !c.isAssignable(fieldType, c.getType(value)) { | ||||
| 			c.errors.fieldTypeMismatch( | ||||
| 				c.locationByID(ent.Id), field, fieldType, c.getType(value)) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (c *checker) checkComprehension(e *exprpb.Expr) { | ||||
| 	comp := e.GetComprehensionExpr() | ||||
| 	c.check(comp.IterRange) | ||||
| 	c.check(comp.AccuInit) | ||||
| 	accuType := c.getType(comp.AccuInit) | ||||
| 	rangeType := c.getType(comp.IterRange) | ||||
| 	var varType *exprpb.Type | ||||
|  | ||||
| 	switch kindOf(rangeType) { | ||||
| 	case kindList: | ||||
| 		varType = rangeType.GetListType().ElemType | ||||
| 	case kindMap: | ||||
| 		// Ranges over the keys. | ||||
| 		varType = rangeType.GetMapType().KeyType | ||||
| 	case kindDyn, kindError, kindTypeParam: | ||||
| 		// Set the range type to DYN to prevent assignment to a potentionally incorrect type | ||||
| 		// at a later point in type-checking. The isAssignable call will update the type | ||||
| 		// substitutions for the type param under the covers. | ||||
| 		c.isAssignable(decls.Dyn, rangeType) | ||||
| 		// Set the range iteration variable to type DYN as well. | ||||
| 		varType = decls.Dyn | ||||
| 	default: | ||||
| 		c.errors.notAComprehensionRange(c.location(comp.IterRange), rangeType) | ||||
| 		varType = decls.Error | ||||
| 	} | ||||
|  | ||||
| 	// 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.Add(decls.NewVar(comp.AccuVar, accuType)) | ||||
| 	// Create a block scope for the loop. | ||||
| 	c.env = c.env.enterScope() | ||||
| 	c.env.Add(decls.NewVar(comp.IterVar, varType)) | ||||
| 	// Check the variable references in the condition and step. | ||||
| 	c.check(comp.LoopCondition) | ||||
| 	c.assertType(comp.LoopCondition, decls.Bool) | ||||
| 	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.Result) | ||||
| 	// Exit the comprehension scope. | ||||
| 	c.env = c.env.exitScope() | ||||
| 	c.setType(e, c.getType(comp.Result)) | ||||
| } | ||||
|  | ||||
| // Checks compatibility of joined types, and returns the most general common type. | ||||
| func (c *checker) joinTypes(loc common.Location, | ||||
| 	previous *exprpb.Type, | ||||
| 	current *exprpb.Type) *exprpb.Type { | ||||
| 	if previous == nil { | ||||
| 		return current | ||||
| 	} | ||||
| 	if c.isAssignable(previous, current) { | ||||
| 		return mostGeneral(previous, current) | ||||
| 	} | ||||
| 	if c.dynAggregateLiteralElementTypesEnabled() { | ||||
| 		return decls.Dyn | ||||
| 	} | ||||
| 	c.errors.typeMismatch(loc, previous, current) | ||||
| 	return decls.Error | ||||
| } | ||||
|  | ||||
| func (c *checker) dynAggregateLiteralElementTypesEnabled() bool { | ||||
| 	return c.env.aggLitElemType == dynElementType | ||||
| } | ||||
|  | ||||
| func (c *checker) newTypeVar() *exprpb.Type { | ||||
| 	id := c.freeTypeVarCounter | ||||
| 	c.freeTypeVarCounter++ | ||||
| 	return decls.NewTypeParamType(fmt.Sprintf("_var%d", id)) | ||||
| } | ||||
|  | ||||
| func (c *checker) isAssignable(t1 *exprpb.Type, t2 *exprpb.Type) bool { | ||||
| 	subs := isAssignable(c.mappings, t1, t2) | ||||
| 	if subs != nil { | ||||
| 		c.mappings = subs | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (c *checker) isAssignableList(l1 []*exprpb.Type, l2 []*exprpb.Type) bool { | ||||
| 	subs := isAssignableList(c.mappings, l1, l2) | ||||
| 	if subs != nil { | ||||
| 		c.mappings = subs | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (c *checker) lookupFieldType(l common.Location, messageType string, fieldName string) (*ref.FieldType, bool) { | ||||
| 	if _, found := c.env.provider.FindType(messageType); !found { | ||||
| 		// This should not happen, anyway, report an error. | ||||
| 		c.errors.unexpectedFailedResolution(l, messageType) | ||||
| 		return nil, false | ||||
| 	} | ||||
|  | ||||
| 	if ft, found := c.env.provider.FindFieldType(messageType, fieldName); found { | ||||
| 		return ft, found | ||||
| 	} | ||||
|  | ||||
| 	c.errors.undefinedField(l, fieldName) | ||||
| 	return nil, false | ||||
| } | ||||
|  | ||||
| func (c *checker) setType(e *exprpb.Expr, t *exprpb.Type) { | ||||
| 	if old, found := c.types[e.Id]; found && !proto.Equal(old, t) { | ||||
| 		c.errors.ReportError(c.location(e), | ||||
| 			"(Incompatible) Type already exists for expression: %v(%d) old:%v, new:%v", e, e.GetId(), old, t) | ||||
| 		return | ||||
| 	} | ||||
| 	c.types[e.Id] = t | ||||
| } | ||||
|  | ||||
| func (c *checker) getType(e *exprpb.Expr) *exprpb.Type { | ||||
| 	return c.types[e.Id] | ||||
| } | ||||
|  | ||||
| func (c *checker) setReference(e *exprpb.Expr, r *exprpb.Reference) { | ||||
| 	if old, found := c.references[e.Id]; found && !proto.Equal(old, r) { | ||||
| 		c.errors.ReportError(c.location(e), | ||||
| 			"Reference already exists for expression: %v(%d) old:%v, new:%v", e, e.Id, old, r) | ||||
| 		return | ||||
| 	} | ||||
| 	c.references[e.Id] = r | ||||
| } | ||||
|  | ||||
| func (c *checker) assertType(e *exprpb.Expr, t *exprpb.Type) { | ||||
| 	if !c.isAssignable(t, c.getType(e)) { | ||||
| 		c.errors.typeMismatch(c.location(e), t, c.getType(e)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type overloadResolution struct { | ||||
| 	Reference *exprpb.Reference | ||||
| 	Type      *exprpb.Type | ||||
| } | ||||
|  | ||||
| func newResolution(checkedRef *exprpb.Reference, t *exprpb.Type) *overloadResolution { | ||||
| 	return &overloadResolution{ | ||||
| 		Reference: checkedRef, | ||||
| 		Type:      t, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (c *checker) location(e *exprpb.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.LineOffsets { | ||||
| 			if lineOffset < offset { | ||||
| 				line++ | ||||
| 				col = int(offset - lineOffset) | ||||
| 			} else { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		return common.NewLocation(line, col) | ||||
| 	} | ||||
| 	return common.NoLocation | ||||
| } | ||||
|  | ||||
| func newIdentReference(name string, value *exprpb.Constant) *exprpb.Reference { | ||||
| 	return &exprpb.Reference{Name: name, Value: value} | ||||
| } | ||||
|  | ||||
| func newFunctionReference(overloads ...string) *exprpb.Reference { | ||||
| 	return &exprpb.Reference{OverloadId: overloads} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user