|  | // Copyright 2021 The Dawn & Tint Authors | 
|  | // | 
|  | // Redistribution and use in source and binary forms, with or without | 
|  | // modification, are permitted provided that the following conditions are met: | 
|  | // | 
|  | // 1. Redistributions of source code must retain the above copyright notice, this | 
|  | //    list of conditions and the following disclaimer. | 
|  | // | 
|  | // 2. Redistributions in binary form must reproduce the above copyright notice, | 
|  | //    this list of conditions and the following disclaimer in the documentation | 
|  | //    and/or other materials provided with the distribution. | 
|  | // | 
|  | // 3. Neither the name of the copyright holder nor the names of its | 
|  | //    contributors may be used to endorse or promote products derived from | 
|  | //    this software without specific prior written permission. | 
|  | // | 
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
|  | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
|  | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | 
|  | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 
|  | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 
|  | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
|  | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | package resolver | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "sort" | 
|  |  | 
|  | "dawn.googlesource.com/dawn/tools/src/container" | 
|  | "dawn.googlesource.com/dawn/tools/src/tint/intrinsic/ast" | 
|  | "dawn.googlesource.com/dawn/tools/src/tint/intrinsic/sem" | 
|  | "dawn.googlesource.com/dawn/tools/src/tint/intrinsic/tok" | 
|  | ) | 
|  |  | 
|  | type resolver struct { | 
|  | a *ast.AST | 
|  | s *sem.Sem | 
|  |  | 
|  | globals                   scope | 
|  | builtins                  map[string]*sem.Intrinsic | 
|  | unaryOperators            map[string]*sem.Intrinsic | 
|  | binaryOperators           map[string]*sem.Intrinsic | 
|  | constructorsAndConverters map[string]*sem.Intrinsic | 
|  | enumEntryMatchers         map[*sem.EnumEntry]*sem.EnumMatcher | 
|  | } | 
|  |  | 
|  | // Resolve processes the AST | 
|  | func Resolve(a *ast.AST) (*sem.Sem, error) { | 
|  | r := resolver{ | 
|  | a:                         a, | 
|  | s:                         sem.New(), | 
|  | globals:                   newScope(nil), | 
|  | builtins:                  map[string]*sem.Intrinsic{}, | 
|  | unaryOperators:            map[string]*sem.Intrinsic{}, | 
|  | binaryOperators:           map[string]*sem.Intrinsic{}, | 
|  | constructorsAndConverters: map[string]*sem.Intrinsic{}, | 
|  | enumEntryMatchers:         map[*sem.EnumEntry]*sem.EnumMatcher{}, | 
|  | } | 
|  | // Declare and resolve all the enumerators | 
|  | for _, e := range a.Enums { | 
|  | if err := r.enum(e); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | } | 
|  | // Declare and resolve all the ty types | 
|  | for _, p := range a.Types { | 
|  | if err := r.ty(p); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | } | 
|  | // Declare and resolve the type matchers | 
|  | for _, m := range a.Matchers { | 
|  | if err := r.matcher(m); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | } | 
|  | // Declare and resolve the builtins | 
|  | for _, f := range a.Builtins { | 
|  | if err := r.intrinsic(f, r.builtins, &r.s.Builtins); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | } | 
|  | // Declare and resolve the unary and binary operators | 
|  | for _, o := range a.Operators { | 
|  | switch len(o.Parameters) { | 
|  | case 1: | 
|  | if err := r.intrinsic(o, r.unaryOperators, &r.s.UnaryOperators); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | case 2: | 
|  | if err := r.intrinsic(o, r.binaryOperators, &r.s.BinaryOperators); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | default: | 
|  | return nil, fmt.Errorf("%v operators must have either 1 or 2 parameters", o.Source) | 
|  | } | 
|  | } | 
|  |  | 
|  | // Declare and resolve value constructors and converters | 
|  | for _, c := range a.Constructors { | 
|  | if err := r.intrinsic(c, r.constructorsAndConverters, &r.s.ConstructorsAndConverters); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | } | 
|  | for _, c := range a.Converters { | 
|  | if len(c.Parameters) != 1 { | 
|  | return nil, fmt.Errorf("%v conversions must have a single parameter", c.Source) | 
|  | } | 
|  | if err := r.intrinsic(c, r.constructorsAndConverters, &r.s.ConstructorsAndConverters); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | } | 
|  |  | 
|  | var usages = ast.EnumDecl{} | 
|  | for _, e := range a.Enums { | 
|  | if e.Name == "usages" { | 
|  | usages = e | 
|  | } | 
|  | } | 
|  | // Calculate the unique parameter names | 
|  | r.s.UniqueParameterNames = r.calculateUniqueParameterNames(usages) | 
|  |  | 
|  | return r.s, nil | 
|  | } | 
|  |  | 
|  | // enum() resolves an enum declaration. | 
|  | // The resulting sem.Enum is appended to Sem.Enums, and the enum and all its | 
|  | // entries are registered with the global scope. | 
|  | func (r *resolver) enum(e ast.EnumDecl) error { | 
|  | NS := "core" | 
|  | if ns := e.Attributes.Take("ns"); ns != nil { | 
|  | NS = ns.Values[0].(string) | 
|  | } | 
|  |  | 
|  | s := &sem.Enum{ | 
|  | Decl: e, | 
|  | Name: e.Name, | 
|  | NS:   NS, | 
|  | } | 
|  |  | 
|  | // Register the enum | 
|  | r.s.Enums = append(r.s.Enums, s) | 
|  | if err := r.globals.declare(s, e.Source); err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | // Register each of the enum entries | 
|  | names := container.NewSet[string]() | 
|  | for _, ast := range e.Entries { | 
|  | entry := &sem.EnumEntry{ | 
|  | Name: ast.Name, | 
|  | Enum: s, | 
|  | } | 
|  | if internal := ast.Attributes.Take("internal"); internal != nil { | 
|  | entry.IsInternal = true | 
|  | if len(internal.Values) != 0 { | 
|  | return fmt.Errorf("%v unexpected value for internal attribute", ast.Source) | 
|  | } | 
|  | } | 
|  | if len(ast.Attributes) != 0 { | 
|  | return fmt.Errorf("%v unknown attribute", ast.Attributes[0].Source) | 
|  | } | 
|  | s.Entries = append(s.Entries, entry) | 
|  | if names.Contains(ast.Name) { | 
|  | return fmt.Errorf("%v duplicate enum entry '%v'", ast.Source, ast.Name) | 
|  | } | 
|  | names.Add(ast.Name) | 
|  | } | 
|  |  | 
|  | // Sort the enum entries into lexicographic order | 
|  | sort.Slice(s.Entries, func(i, j int) bool { return s.Entries[i].Name < s.Entries[j].Name }) | 
|  |  | 
|  | if len(e.Attributes) != 0 { | 
|  | return fmt.Errorf("%v unknown attribute: %v", e.Attributes[0].Source, e.Attributes[0].Values[0]) | 
|  | } | 
|  |  | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // ty() resolves a type declaration. | 
|  | // The resulting sem.Type is appended to Sem.Types, and the type is registered | 
|  | // with the global scope. | 
|  | func (r *resolver) ty(a ast.TypeDecl) error { | 
|  | t := &sem.Type{ | 
|  | Decl: a, | 
|  | Name: a.Name, | 
|  | } | 
|  |  | 
|  | // Register the type | 
|  | r.s.Types = append(r.s.Types, t) | 
|  | if err := r.globals.declare(t, a.Source); err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | // Create a new scope for resolving template parameters | 
|  | s := newScope(&r.globals) | 
|  |  | 
|  | // Resolve the type template parameters | 
|  | templateParams, err := r.templateParams(&s, a.TemplateParams) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | t.TemplateParams = templateParams | 
|  |  | 
|  | // Scan for attributes | 
|  | if d := a.Attributes.Take("display"); d != nil { | 
|  | if len(d.Values) != 1 { | 
|  | return fmt.Errorf("%v expected a single value for 'display' attribute", d.Source) | 
|  | } | 
|  | t.DisplayName = fmt.Sprint(d.Values[0]) | 
|  | } | 
|  | if d := a.Attributes.Take("precedence"); d != nil { | 
|  | if len(d.Values) != 1 { | 
|  | return fmt.Errorf("%v expected a single integer value for 'precedence' attribute", d.Source) | 
|  | } | 
|  | n, ok := d.Values[0].(int) | 
|  | if !ok { | 
|  | return fmt.Errorf("%v @precedence value must be an integer", d.Source) | 
|  | } | 
|  | t.Precedence = n | 
|  | } | 
|  |  | 
|  | if len(a.Attributes) != 0 { | 
|  | return fmt.Errorf("%v unknown attribute", a.Attributes[0].Source) | 
|  | } | 
|  |  | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // matcher() resolves a match declaration to either a sem.TypeMatcher or | 
|  | // sem.EnumMatcher. | 
|  | // The resulting matcher is appended to either Sem.TypeMatchers or | 
|  | // Sem.EnumMatchers, and is registered with the global scope. | 
|  | func (r *resolver) matcher(a ast.MatcherDecl) error { | 
|  | isTypeMatcher := len(a.Options.Types) != 0 | 
|  | isEnumMatcher := len(a.Options.Enums) != 0 | 
|  | if isTypeMatcher && isEnumMatcher { | 
|  | return fmt.Errorf("%v matchers cannot mix enums and types", a.Source) | 
|  | } | 
|  |  | 
|  | if isTypeMatcher { | 
|  | options := map[sem.Named]tok.Source{} | 
|  | m := &sem.TypeMatcher{ | 
|  | Decl: a, | 
|  | Name: a.Name, | 
|  | } | 
|  |  | 
|  | // Register the matcher | 
|  | r.s.TypeMatchers = append(r.s.TypeMatchers, m) | 
|  | if err := r.globals.declare(m, a.Source); err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | // Resolve each of the types in the options list | 
|  | for _, ast := range m.Decl.Options.Types { | 
|  | ty, err := r.lookupType(&r.globals, ast) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | m.Types = append(m.Types, ty) | 
|  | if s, dup := options[ty]; dup { | 
|  | return fmt.Errorf("%v duplicate option '%v' in matcher\nFirst declared here: %v", ast.Source, ast.Name, s) | 
|  | } | 
|  | options[ty] = ast.Source | 
|  | } | 
|  |  | 
|  | return nil | 
|  | } | 
|  | if isEnumMatcher { | 
|  | owners := container.NewSet[string]() | 
|  | for _, enum := range a.Options.Enums { | 
|  | owners.Add(enum.Owner) | 
|  | } | 
|  | if len(owners) > 1 { | 
|  | return fmt.Errorf("%v cannot mix enums (%v) in type matcher", a.Source, owners) | 
|  | } | 
|  | enumName := owners.One() | 
|  | lookup := r.globals.lookup(enumName) | 
|  | if lookup == nil { | 
|  | return fmt.Errorf("%v cannot resolve enum '%v'", a.Source, enumName) | 
|  | } | 
|  | enum, _ := lookup.object.(*sem.Enum) | 
|  | if enum == nil { | 
|  | return fmt.Errorf("%v cannot resolve enum '%v'", a.Source, enumName) | 
|  | } | 
|  |  | 
|  | m := &sem.EnumMatcher{ | 
|  | Decl: a, | 
|  | Name: a.Name, | 
|  | Enum: enum, | 
|  | } | 
|  |  | 
|  | // Register the matcher | 
|  | r.s.EnumMatchers = append(r.s.EnumMatchers, m) | 
|  | if err := r.globals.declare(m, a.Source); err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | // Resolve each of the enums in the options list | 
|  | for _, ast := range m.Decl.Options.Enums { | 
|  | entry := enum.FindEntry(ast.Member) | 
|  | if entry == nil { | 
|  | return fmt.Errorf("%v enum '%v' does not contain '%v'", ast.Source, enum.Name, ast.Member) | 
|  | } | 
|  | m.Options = append(m.Options, entry) | 
|  | } | 
|  |  | 
|  | return nil | 
|  | } | 
|  |  | 
|  | return fmt.Errorf("%v matcher cannot be empty", a.Source) | 
|  | } | 
|  |  | 
|  | // intrinsic() resolves a intrinsic overload declaration. | 
|  | // The the first overload for the intrinsic creates and appends the sem.Intrinsic | 
|  | // to Sem.Intrinsics. Subsequent overloads append their resolved overload to the | 
|  | // sem.intrinsic.Overloads list. | 
|  | func (r *resolver) intrinsic( | 
|  | a ast.IntrinsicDecl, | 
|  | intrinsicsByName map[string]*sem.Intrinsic, | 
|  | semIntrinsics *[]*sem.Intrinsic) error { | 
|  | // If this is the first overload of the intrinsic, create and register the | 
|  | // semantic intrinsic. | 
|  | intrinsic := intrinsicsByName[a.Name] | 
|  | if intrinsic == nil { | 
|  | intrinsic = &sem.Intrinsic{Name: a.Name} | 
|  | intrinsicsByName[a.Name] = intrinsic | 
|  | *semIntrinsics = append(*semIntrinsics, intrinsic) | 
|  | } | 
|  |  | 
|  | // Create a new scope for resolving template parameters | 
|  | s := newScope(&r.globals) | 
|  |  | 
|  | // Construct the semantic overload | 
|  | overload := &sem.Overload{ | 
|  | Decl:       a, | 
|  | Intrinsic:  intrinsic, | 
|  | Parameters: make([]sem.Parameter, len(a.Parameters)), | 
|  | } | 
|  |  | 
|  | // Resolve the declared template parameters | 
|  | // Explicit template types can use implicit templates, so resolve implicit first | 
|  | implicitTemplates, err := r.templateParams(&s, a.ImplicitTemplateParams) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | overload.ImplicitTemplates = implicitTemplates | 
|  |  | 
|  | explicitTemplates, err := r.templateParams(&s, a.ExplicitTemplateParams) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | overload.ExplicitTemplates = explicitTemplates | 
|  |  | 
|  | // Process overload attributes | 
|  | if stageDeco := a.Attributes.Take("stage"); stageDeco != nil { | 
|  | for stageDeco != nil { | 
|  | for _, stage := range stageDeco.Values { | 
|  | switch stage { | 
|  | case "vertex": | 
|  | overload.CanBeUsedInStage.Vertex = true | 
|  | case "fragment": | 
|  | overload.CanBeUsedInStage.Fragment = true | 
|  | case "compute": | 
|  | overload.CanBeUsedInStage.Compute = true | 
|  | default: | 
|  | return fmt.Errorf("%v unknown stage '%v'", stageDeco.Source, stage) | 
|  | } | 
|  | } | 
|  | stageDeco = a.Attributes.Take("stage") | 
|  | } | 
|  | } else { | 
|  | overload.CanBeUsedInStage = sem.StageUses{ | 
|  | Vertex:   true, | 
|  | Fragment: true, | 
|  | Compute:  true, | 
|  | } | 
|  | } | 
|  | if mustUse := a.Attributes.Take("must_use"); mustUse != nil { | 
|  | if len(mustUse.Values) > 0 { | 
|  | return fmt.Errorf("%v @must_use does not accept any arguments", mustUse.Source) | 
|  | } | 
|  | if a.ReturnType == nil { | 
|  | return fmt.Errorf("%v @must_use can only be used on a function with a return type", mustUse.Source) | 
|  | } | 
|  | overload.MustUse = true | 
|  | } | 
|  | if constEvalFn := a.Attributes.Take("const"); constEvalFn != nil { | 
|  | switch len(constEvalFn.Values) { | 
|  | case 0: | 
|  | switch overload.Decl.Kind { | 
|  | case ast.Builtin, ast.Operator: | 
|  | overload.ConstEvalFunction = overload.Decl.Name | 
|  | case ast.Constructor: | 
|  | overload.ConstEvalFunction = "Ctor" | 
|  | case ast.Converter: | 
|  | overload.ConstEvalFunction = "Conv" | 
|  | } | 
|  | case 1: | 
|  | fn, ok := constEvalFn.Values[0].(string) | 
|  | if !ok { | 
|  | return fmt.Errorf("%v optional @const value must be a string", constEvalFn.Source) | 
|  | } | 
|  | overload.ConstEvalFunction = fn | 
|  | default: | 
|  | return fmt.Errorf("%v too many values for @const attribute", constEvalFn.Source) | 
|  | } | 
|  | } | 
|  | if memberFunction := a.Attributes.Take("member_function"); memberFunction != nil { | 
|  | overload.MemberFunction = true | 
|  | if len(memberFunction.Values) != 0 { | 
|  | return fmt.Errorf("%v unexpected value for member_function attribute", memberFunction.Source) | 
|  | } | 
|  | if len(a.Parameters) < 1 { | 
|  | return fmt.Errorf("%v @member_function can only be used on a function with at least one parameter", memberFunction.Source) | 
|  | } | 
|  | } | 
|  | if deprecated := a.Attributes.Take("deprecated"); deprecated != nil { | 
|  | overload.IsDeprecated = true | 
|  | if len(deprecated.Values) != 0 { | 
|  | return fmt.Errorf("%v unexpected value for deprecated attribute", deprecated.Source) | 
|  | } | 
|  | } | 
|  | if len(a.Attributes) != 0 { | 
|  | return fmt.Errorf("%v unknown attribute", a.Attributes[0].Source) | 
|  | } | 
|  |  | 
|  | // Append the overload to the intrinsic | 
|  | intrinsic.Overloads = append(intrinsic.Overloads, overload) | 
|  |  | 
|  | // Update high-water mark of templates | 
|  | if n := len(overload.AllTemplates()); r.s.MaxTemplates < n { | 
|  | r.s.MaxTemplates = n | 
|  | } | 
|  |  | 
|  | // Resolve the parameters | 
|  | for i, p := range a.Parameters { | 
|  | usage, err := r.fullyQualifiedName(&s, p.Type) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | isConst := false | 
|  | if attribute := p.Attributes.Take("const"); attribute != nil { | 
|  | isConst = true | 
|  | } | 
|  | testValue := 1.0 | 
|  | if attribute := p.Attributes.Take("test_value"); attribute != nil { | 
|  | switch v := attribute.Values[0].(type) { | 
|  | case int: | 
|  | testValue = float64(v) | 
|  | case float64: | 
|  | testValue = v | 
|  | default: | 
|  | return fmt.Errorf("%v @test_value must be an integer or float", p.Attributes[0].Source) | 
|  | } | 
|  | } | 
|  | if len(p.Attributes) != 0 { | 
|  | return fmt.Errorf("%v unknown attribute", p.Attributes[0].Source) | 
|  | } | 
|  | overload.Parameters[i] = sem.Parameter{ | 
|  | Name:      p.Name, | 
|  | Type:      usage, | 
|  | IsConst:   isConst, | 
|  | TestValue: testValue, | 
|  | } | 
|  | } | 
|  |  | 
|  | // Resolve the return type | 
|  | if a.ReturnType != nil { | 
|  | usage, err := r.fullyQualifiedName(&s, *a.ReturnType) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | switch usage.Target.(type) { | 
|  | case *sem.Type, *sem.TemplateTypeParam: | 
|  | overload.ReturnType = &usage | 
|  | default: | 
|  | return fmt.Errorf("%v cannot use '%v' as return type. Must be a type or template type", a.ReturnType.Source, a.ReturnType.Name) | 
|  | } | 
|  | } | 
|  |  | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // fullyQualifiedName() resolves the ast.TemplatedName to a sem.FullyQualifiedName. | 
|  | // The resolved name cannot be a TypeMatcher | 
|  | func (r *resolver) fullyQualifiedName(s *scope, arg ast.TemplatedName) (sem.FullyQualifiedName, error) { | 
|  | target, err := r.lookupNamed(s, arg) | 
|  | if err != nil { | 
|  | return sem.FullyQualifiedName{}, err | 
|  | } | 
|  | fqn := sem.FullyQualifiedName{ | 
|  | Target:            target, | 
|  | TemplateArguments: make([]interface{}, len(arg.TemplateArgs)), | 
|  | } | 
|  | for i, a := range arg.TemplateArgs { | 
|  | arg, err := r.fullyQualifiedName(s, a) | 
|  | if err != nil { | 
|  | return sem.FullyQualifiedName{}, err | 
|  | } | 
|  | fqn.TemplateArguments[i] = arg | 
|  | } | 
|  | return fqn, nil | 
|  | } | 
|  |  | 
|  | // templateParams() resolves the ast.TemplateParams list into a sem.TemplateParam list. | 
|  | // Each sem.TemplateParam is registered with the scope s. | 
|  | func (r *resolver) templateParams(s *scope, l []ast.TemplateParam) ([]sem.TemplateParam, error) { | 
|  | out := make([]sem.TemplateParam, 0, len(l)) | 
|  | for _, ast := range l { | 
|  | param, err := r.templateParam(s, ast) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | if err := s.declare(param, ast.Source); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | out = append(out, param) | 
|  | } | 
|  | return out, nil | 
|  | } | 
|  |  | 
|  | // templateParams() resolves the ast.TemplateParam into sem.TemplateParam, which | 
|  | // is either a sem.TemplateEnumParam or a sem.TemplateTypeParam. | 
|  | func (r *resolver) templateParam(s *scope, a ast.TemplateParam) (sem.TemplateParam, error) { | 
|  | if a.Type.Name == "num" { | 
|  | return &sem.TemplateNumberParam{Name: a.Name, ASTParam: a}, nil | 
|  | } | 
|  |  | 
|  | if a.Type.Name != "" { | 
|  | resolved, err := r.fullyQualifiedName(s, a.Type) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | switch r := resolved.Target.(type) { | 
|  | case *sem.Enum: | 
|  | return &sem.TemplateEnumParam{Name: a.Name, ASTParam: a, Enum: r}, nil | 
|  | case *sem.EnumMatcher: | 
|  | return &sem.TemplateEnumParam{Name: a.Name, ASTParam: a, Enum: r.Enum, Matcher: r}, nil | 
|  | case *sem.TypeMatcher: | 
|  | return &sem.TemplateTypeParam{Name: a.Name, ASTParam: a, Type: &resolved}, nil | 
|  | case *sem.Type: | 
|  | return &sem.TemplateTypeParam{Name: a.Name, ASTParam: a, Type: &resolved}, nil | 
|  | default: | 
|  | return nil, fmt.Errorf("%v invalid template parameter type '%v'", a.Source, a.Type.Name) | 
|  | } | 
|  | } | 
|  |  | 
|  | return &sem.TemplateTypeParam{Name: a.Name}, nil | 
|  | } | 
|  |  | 
|  | // lookupType() searches the scope `s` and its ancestors for the sem.Type with | 
|  | // the given name. | 
|  | func (r *resolver) lookupType(s *scope, a ast.TemplatedName) (*sem.Type, error) { | 
|  | resolved, err := r.lookupNamed(s, a) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | // Something with the given name was found... | 
|  | if ty, ok := resolved.(*sem.Type); ok { | 
|  | return ty, nil | 
|  | } | 
|  | // ... but that something was not a sem.Type | 
|  | return nil, fmt.Errorf("%v '%v' resolves to %v but type is expected", a.Source, a.Name, describe(resolved)) | 
|  | } | 
|  |  | 
|  | // lookupNamed() searches `s` and its ancestors for the sem.Named object with | 
|  | // the given name. If there are template arguments for the name `a`, then | 
|  | // lookupNamed() performs basic validation that those arguments can be passed | 
|  | // to the named object. | 
|  | func (r *resolver) lookupNamed(s *scope, a ast.TemplatedName) (sem.Named, error) { | 
|  | target := s.lookup(a.Name) | 
|  | if target == nil { | 
|  | return nil, fmt.Errorf("%v cannot resolve '%v'", a.Source, a.Name) | 
|  | } | 
|  |  | 
|  | // Something with the given name was found... | 
|  | var params []sem.TemplateParam | 
|  | var ty sem.ResolvableType | 
|  | switch target := target.object.(type) { | 
|  | case *sem.Type: | 
|  | ty = target | 
|  | params = target.TemplateParams | 
|  | case *sem.TypeMatcher: | 
|  | ty = target | 
|  | case sem.TemplateParam: | 
|  | if len(a.TemplateArgs) != 0 { | 
|  | return nil, fmt.Errorf("%v '%v' template parameters do not accept template arguments", a.Source, a.Name) | 
|  | } | 
|  | return target.(sem.Named), nil | 
|  | case sem.Named: | 
|  | return target, nil | 
|  | default: | 
|  | panic(fmt.Errorf("unknown resolved type %T", target)) | 
|  | } | 
|  | // ... and that something takes template parameters | 
|  | // Check the number of templated name template arguments match the number of | 
|  | // templated parameters for the target. | 
|  | args := a.TemplateArgs | 
|  | if len(params) != len(args) { | 
|  | return nil, fmt.Errorf("%v '%v' requires %d template arguments, but %d were provided", a.Source, a.Name, len(params), len(args)) | 
|  | } | 
|  |  | 
|  | // Check templated name template argument kinds match the parameter kinds | 
|  | for i, ast := range args { | 
|  | param := params[i] | 
|  | arg, err := r.lookupNamed(s, args[i]) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | if err := checkCompatible(arg, param); err != nil { | 
|  | return nil, fmt.Errorf("%v %w", ast.Source, err) | 
|  | } | 
|  | } | 
|  | return ty, nil | 
|  | } | 
|  |  | 
|  | // calculateUniqueParameterNames() iterates over all the parameters of all | 
|  | // builtin overloads, calculating the list of unique parameter names | 
|  | func (r *resolver) calculateUniqueParameterNames(usages ast.EnumDecl) []string { | 
|  | set := map[string]struct{}{"": {}} | 
|  | names := []string{} | 
|  |  | 
|  | for _, e := range usages.Entries { | 
|  | set[e.Name] = struct{}{} | 
|  | names = append(names, e.Name) | 
|  | } | 
|  |  | 
|  | for _, intrinsics := range [][]*sem.Intrinsic{ | 
|  | r.s.Builtins, | 
|  | r.s.UnaryOperators, | 
|  | r.s.BinaryOperators, | 
|  | r.s.ConstructorsAndConverters, | 
|  | } { | 
|  | for _, i := range intrinsics { | 
|  | for _, o := range i.Overloads { | 
|  | for _, p := range o.Parameters { | 
|  | if _, dup := set[p.Name]; !dup { | 
|  | set[p.Name] = struct{}{} | 
|  | names = append(names, p.Name) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | sort.Strings(names) | 
|  | return names | 
|  | } | 
|  |  | 
|  | // describe() returns a string describing a sem.Named | 
|  | func describe(n sem.Named) string { | 
|  | switch n := n.(type) { | 
|  | case *sem.Type: | 
|  | return "type '" + n.Name + "'" | 
|  | case *sem.TypeMatcher: | 
|  | return "type matcher '" + n.Name + "'" | 
|  | case *sem.Enum: | 
|  | return "enum '" + n.Name + "'" | 
|  | case *sem.EnumMatcher: | 
|  | return "enum matcher '" + n.Name + "'" | 
|  | case *sem.TemplateTypeParam: | 
|  | return "template type" | 
|  | case *sem.TemplateEnumParam: | 
|  | return "template enum '" + n.Enum.Name + "'" | 
|  | case *sem.EnumEntry: | 
|  | return "enum entry '" + n.Enum.Name + "." + n.Name + "'" | 
|  | case *sem.TemplateNumberParam: | 
|  | return "template number" | 
|  | default: | 
|  | panic(fmt.Errorf("unhandled type %T", n)) | 
|  | } | 
|  | } | 
|  |  | 
|  | // checkCompatible() returns an error if `arg` cannot be used as an argument for | 
|  | // a parameter of `param`. | 
|  | func checkCompatible(arg, param sem.Named) error { | 
|  | // asEnum() returns the underlying sem.Enum if n is a enum matcher, | 
|  | // templated enum parameter or an enum entry, otherwise nil | 
|  | asEnum := func(n sem.Named) *sem.Enum { | 
|  | switch n := n.(type) { | 
|  | case *sem.EnumMatcher: | 
|  | return n.Enum | 
|  | case *sem.TemplateEnumParam: | 
|  | return n.Enum | 
|  | case *sem.EnumEntry: | 
|  | return n.Enum | 
|  | default: | 
|  | return nil | 
|  | } | 
|  | } | 
|  |  | 
|  | if arg := asEnum(arg); arg != nil { | 
|  | param := asEnum(param) | 
|  | if arg == param { | 
|  | return nil | 
|  | } | 
|  | } | 
|  |  | 
|  | anyNumber := "any number" | 
|  | // asNumber() returns anyNumber if n is a TemplateNumberParam. | 
|  | asNumber := func(n sem.Named) interface{} { | 
|  | switch n.(type) { | 
|  | case *sem.TemplateNumberParam: | 
|  | return anyNumber | 
|  | default: | 
|  | return nil | 
|  | } | 
|  | } | 
|  |  | 
|  | if arg := asNumber(arg); arg != nil { | 
|  | param := asNumber(param) | 
|  | if arg == param { | 
|  | return nil | 
|  | } | 
|  | } | 
|  |  | 
|  | anyType := &sem.Type{} | 
|  | // asNumber() returns the sem.Type, sem.TypeMatcher if the named object | 
|  | // resolves to one of these, or anyType if n is a unconstrained template | 
|  | // type parameter. | 
|  | asResolvableType := func(n sem.Named) sem.ResolvableType { | 
|  | switch n := n.(type) { | 
|  | case *sem.TemplateTypeParam: | 
|  | if n.Type != nil { | 
|  | return n.Type.Target.(sem.ResolvableType) | 
|  | } | 
|  | return anyType | 
|  | case *sem.Type: | 
|  | return n | 
|  | case *sem.TypeMatcher: | 
|  | return n | 
|  | default: | 
|  | return nil | 
|  | } | 
|  | } | 
|  |  | 
|  | if arg := asResolvableType(arg); arg != nil { | 
|  | param := asResolvableType(param) | 
|  | if arg == param || param == anyType { | 
|  | return nil | 
|  | } | 
|  | } | 
|  |  | 
|  | return fmt.Errorf("cannot use %v as %v", describe(arg), describe(param)) | 
|  | } | 
|  |  | 
|  | // scope is a basic hierarchical name to object table | 
|  | type scope struct { | 
|  | objects map[string]objectAndSource | 
|  | parent  *scope | 
|  | } | 
|  |  | 
|  | // objectAndSource is a sem.Named object with a source | 
|  | type objectAndSource struct { | 
|  | object sem.Named | 
|  | source tok.Source | 
|  | } | 
|  |  | 
|  | // newScope returns a newly initalized scope | 
|  | func newScope(parent *scope) scope { | 
|  | return scope{objects: map[string]objectAndSource{}, parent: parent} | 
|  | } | 
|  |  | 
|  | // lookup() searches the scope and then its parents for the symbol with the | 
|  | // given name. | 
|  | func (s *scope) lookup(name string) *objectAndSource { | 
|  | if o, found := s.objects[name]; found { | 
|  | return &o | 
|  | } | 
|  | if s.parent == nil { | 
|  | return nil | 
|  | } | 
|  | return s.parent.lookup(name) | 
|  | } | 
|  |  | 
|  | // declare() declares the symbol with the given name, erroring on symbol | 
|  | // collision. | 
|  | func (s *scope) declare(object sem.Named, source tok.Source) error { | 
|  | name := object.GetName() | 
|  | if existing := s.lookup(name); existing != nil { | 
|  | return fmt.Errorf("%v '%v' already declared\nFirst declared here: %v", source, name, existing.source) | 
|  | } | 
|  | s.objects[name] = objectAndSource{object, source} | 
|  | return nil | 
|  | } |