mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-07 04:35:53 +00:00
These are currently not used, but the first step towards moving type constructors and converters over to using the intrinisc table. This will simplify maintenance of type functions, and will greatly simplify the [AbstractInt -> i32|u32] [AbstractFloat -> f32|f16] logic. Bug: tint:1504 Change-Id: I15526670a6ff801e66551ab5adc37b1570ac49de Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/90242 Reviewed-by: Antonio Maiorano <amaiorano@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
702 lines
20 KiB
Go
702 lines
20 KiB
Go
// Copyright 2021 The Tint Authors.
|
|
//
|
|
// 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 resolver
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
|
|
"dawn.googlesource.com/dawn/tools/src/cmd/intrinsic-gen/ast"
|
|
"dawn.googlesource.com/dawn/tools/src/cmd/intrinsic-gen/sem"
|
|
"dawn.googlesource.com/dawn/tools/src/cmd/intrinsic-gen/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 type 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
|
|
}
|
|
}
|
|
|
|
// Calculate the unique parameter names
|
|
r.s.UniqueParameterNames = r.calculateUniqueParameterNames()
|
|
|
|
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 {
|
|
s := &sem.Enum{
|
|
Decl: e,
|
|
Name: e.Name,
|
|
}
|
|
|
|
// 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
|
|
for _, ast := range e.Entries {
|
|
entry := &sem.EnumEntry{
|
|
Name: ast.Name,
|
|
Enum: s,
|
|
}
|
|
if internal := ast.Decorations.Take("internal"); internal != nil {
|
|
entry.IsInternal = true
|
|
if len(internal.Values) != 0 {
|
|
return fmt.Errorf("%v unexpected value for internal decoration", ast.Source)
|
|
}
|
|
}
|
|
if len(ast.Decorations) != 0 {
|
|
return fmt.Errorf("%v unknown decoration", ast.Decorations[0].Source)
|
|
}
|
|
if err := r.globals.declare(entry, e.Source); err != nil {
|
|
return err
|
|
}
|
|
s.Entries = append(s.Entries, entry)
|
|
}
|
|
|
|
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 decorations
|
|
if d := a.Decorations.Take("display"); d != nil {
|
|
if len(d.Values) != 1 {
|
|
return fmt.Errorf("%v expected a single value for 'display' decoration", d.Source)
|
|
}
|
|
t.DisplayName = d.Values[0]
|
|
}
|
|
if len(a.Decorations) != 0 {
|
|
return fmt.Errorf("%v unknown decoration", a.Decorations[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 {
|
|
// Determine whether this is a type matcher or enum matcher by resolving the
|
|
// first option
|
|
firstOption, err := r.lookupNamed(&r.globals, a.Options[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Resolve to a sem.TypeMatcher or a sem.EnumMatcher
|
|
switch firstOption := firstOption.(type) {
|
|
case *sem.Type:
|
|
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 {
|
|
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
|
|
|
|
case *sem.EnumEntry:
|
|
enum := firstOption.Enum
|
|
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 {
|
|
entry := enum.FindEntry(ast.Name)
|
|
if entry == nil {
|
|
return fmt.Errorf("%v enum '%v' does not contain '%v'", ast.Source, enum.Name, ast.Name)
|
|
}
|
|
m.Options = append(m.Options, entry)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
return fmt.Errorf("'%v' cannot be used for matcher", a.Name)
|
|
}
|
|
|
|
// 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)
|
|
|
|
// Resolve the declared template parameters
|
|
templateParams, err := r.templateParams(&s, a.TemplateParams)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Construct the semantic overload
|
|
overload := &sem.Overload{
|
|
Decl: a,
|
|
Intrinsic: intrinsic,
|
|
Parameters: make([]sem.Parameter, len(a.Parameters)),
|
|
TemplateParams: templateParams,
|
|
}
|
|
|
|
// Process overload decorations
|
|
if stageDeco := a.Decorations.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.Decorations.Take("stage")
|
|
}
|
|
} else {
|
|
overload.CanBeUsedInStage = sem.StageUses{
|
|
Vertex: true,
|
|
Fragment: true,
|
|
Compute: true,
|
|
}
|
|
}
|
|
if deprecated := a.Decorations.Take("deprecated"); deprecated != nil {
|
|
overload.IsDeprecated = true
|
|
if len(deprecated.Values) != 0 {
|
|
return fmt.Errorf("%v unexpected value for deprecated decoration", deprecated.Source)
|
|
}
|
|
}
|
|
if len(a.Decorations) != 0 {
|
|
return fmt.Errorf("%v unknown decoration", a.Decorations[0].Source)
|
|
}
|
|
|
|
// Append the overload to the intrinsic
|
|
intrinsic.Overloads = append(intrinsic.Overloads, overload)
|
|
|
|
// Sort the template parameters by resolved type. Append these to
|
|
// sem.Overload.OpenTypes or sem.Overload.OpenNumbers based on their kind.
|
|
for _, param := range templateParams {
|
|
switch param := param.(type) {
|
|
case *sem.TemplateTypeParam:
|
|
overload.OpenTypes = append(overload.OpenTypes, param)
|
|
case *sem.TemplateEnumParam, *sem.TemplateNumberParam:
|
|
overload.OpenNumbers = append(overload.OpenNumbers, param)
|
|
}
|
|
}
|
|
|
|
// Update high-water marks of open types / numbers
|
|
if r.s.MaxOpenTypes < len(overload.OpenTypes) {
|
|
r.s.MaxOpenTypes = len(overload.OpenTypes)
|
|
}
|
|
if r.s.MaxOpenNumbers < len(overload.OpenNumbers) {
|
|
r.s.MaxOpenNumbers = len(overload.OpenNumbers)
|
|
}
|
|
|
|
// Resolve the parameters
|
|
for i, p := range a.Parameters {
|
|
usage, err := r.fullyQualifiedName(&s, p.Type)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
overload.Parameters[i] = sem.Parameter{
|
|
Name: p.Name,
|
|
Type: usage,
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
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
|
|
}
|
|
|
|
if entry, ok := target.(*sem.EnumEntry); ok {
|
|
// The target resolved to an enum entry.
|
|
// Automagically transform this into a synthetic matcher with a single
|
|
// option. i.e.
|
|
// This:
|
|
// enum E{ a b c }
|
|
// fn F(b)
|
|
// Becomes:
|
|
// enum E{ a b c }
|
|
// matcher b
|
|
// fn F(b)
|
|
// We don't really care right now that we have a symbol collision
|
|
// between E.b and b, as the generators return different names for
|
|
// these.
|
|
matcher, ok := r.enumEntryMatchers[entry]
|
|
if !ok {
|
|
matcher = &sem.EnumMatcher{
|
|
Name: entry.Name,
|
|
Enum: entry.Enum,
|
|
Options: []*sem.EnumEntry{entry},
|
|
}
|
|
r.enumEntryMatchers[entry] = matcher
|
|
r.s.EnumMatchers = append(r.s.EnumMatchers, matcher)
|
|
}
|
|
target = matcher
|
|
}
|
|
|
|
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 into list of sem.TemplateParam.
|
|
// Each sem.TemplateParam is registered with the scope s.
|
|
func (r *resolver) templateParams(s *scope, l ast.TemplateParams) ([]sem.TemplateParam, error) {
|
|
out := []sem.TemplateParam{}
|
|
for _, ast := range l {
|
|
param, err := r.templateParam(ast)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
s.declare(param, ast.Source)
|
|
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(a ast.TemplateParam) (sem.TemplateParam, error) {
|
|
if a.Type.Name == "num" {
|
|
return &sem.TemplateNumberParam{Name: a.Name}, nil
|
|
}
|
|
|
|
if a.Type.Name != "" {
|
|
resolved, err := r.lookupNamed(&r.globals, a.Type)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch r := resolved.(type) {
|
|
case *sem.Enum:
|
|
return &sem.TemplateEnumParam{Name: a.Name, Enum: r}, nil
|
|
case *sem.EnumMatcher:
|
|
return &sem.TemplateEnumParam{Name: a.Name, Enum: r.Enum, Matcher: r}, nil
|
|
case *sem.TypeMatcher:
|
|
return &sem.TemplateTypeParam{Name: a.Name, Type: r}, nil
|
|
case *sem.Type:
|
|
return &sem.TemplateTypeParam{Name: a.Name, Type: r}, 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
|
|
params = target.TemplateParams
|
|
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() []string {
|
|
set := map[string]struct{}{"": {}}
|
|
names := []string{}
|
|
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.
|
|
// TODO(bclayton): Once we support number ranges [e.g.: fn F<N: 1..4>()], we
|
|
// should check number ranges are compatible
|
|
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
|
|
}
|
|
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
|
|
}
|