mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
all: REVERSE MERGE dev.typeparams (7cdfa49) into master
This commit is a REVERSE MERGE. It merges dev.typeparams back into its parent branch, master. This marks the end of development on dev.typeparams. Merge List: + 2021-02-197cdfa4969a[dev.typeparams] all: merge master (06b86e9) into dev.typeparams + 2021-02-196521c7b378[dev.typeparams] cmd/compile/internal/types2: resolve decl cycle the same way as in go/types + 2021-02-19dfe0ef961b[dev.typeparams] go/types, types2: revert fancy struct printing (fixes x/tools tests) + 2021-02-192f37939a21go/parser: improve error recovery from invalid selector exprs + 2021-02-198654db4555[dev.typeparams] go/types: adjust printing of embedded struct fields (fixes x/tools/cmd/guru tests) + 2021-02-1947e4b0739eMerge "[dev.typeparams] all: merge master (eb98272) into dev.typeparams" into dev.typeparams + 2021-02-18a789be7814[dev.typeparams] cmd/compile: use new converter functions rather than methods (fix build) + 2021-02-1820050a15fe[dev.typeparams] cmd/compile: support generic types (with stenciling of method calls) + 2021-02-18e7493a9c74[dev.typeparams] all: merge master (eb98272) into dev.typeparams + 2021-02-182ff1e05a4c[dev.typeparams] all: update parent repository + 2021-02-188960ce7735[dev.typeparams] cmd/compile/internal/types2: minor adjustments to match go/types more closely + 2021-02-186f3878b942[dev.typeparams] cmd/compile/internal/types: review of typestring_test.go + 2021-02-18d6bdd1aeef[dev.typeparams] cmd/compile/internal/types: review of typestring.go + 2021-02-18c2314babb8[dev.typeparams] cmd/compile/internal/types: review of type.go + 2021-02-18099374b55e[dev.typeparams] cmd/compile/internal/types2: remove Type.Under method in favor of function + 2021-02-18653386a89a[dev.typeparams] cmd/compile/internal/types2: replace Named, TypeParam methods with functions + 2021-02-185e4da8670b[dev.typeparams] cmd/compile/internal/types2: use converter functions rather than methods + 2021-02-185ecb9a7887[dev.typeparams] go/types: use a new ast.ListExpr for multi-type instances + 2021-02-187b679617f3[dev.typeparams] go/types: conversions to type parameters are not constant + 2021-02-17f5d0c653e6[dev.typeparams] merge master (2f0da6d) into dev.typeparams + 2021-02-17e196cb8258[dev.typeparams] cmd/dist: disable -G=3 on the std go tests for now + 2021-02-12a06bd9fecb[dev.typeparams] cmd/compile/internal/types: review of resolver_test.go + 2021-02-12042f88fe30[dev.typeparams] cmd/compile/internal/types: review of errors_test.go + 2021-02-120abd7b768b[dev.typeparams] cmd/compile/internal/types: review of universe.go + 2021-02-121b6f0bf1b2[dev.typeparams] cmd/compile/internal/types: review of sizes_test.go + 2021-02-121758780181[dev.typeparams] cmd/compile/internal/types: review of sizes.go + 2021-02-123aee461d5c[dev.typeparams] cmd/compile/internal/types: review of return.go + 2021-02-127428318af6[dev.typeparams] cmd/compile/internal/types: review of object_test.go + 2021-02-12b20f9e2da1[dev.typeparams] cmd/compile/internal/types: review of object.go + 2021-02-1220746b2f37[dev.typeparams] cmd/compile/internal/types: review of labels.go + 2021-02-12bab3461123[dev.typeparams] cmd/compile/internal/types: review of infer.go + 2021-02-129168590977[dev.typeparams] cmd/compile/internal/types: review of builtin_test.go + 2021-02-12f1777cf84c[dev.typeparams] cmd/compile/internal/types: review of builtin.go + 2021-02-120f43973b4b[dev.typeparams] go/types: make predeclared "any" alias for interface{} + 2021-02-1258758e0a21[dev.typeparams] go/types: better error message for invalid ... use + 2021-02-11c0aa7bd760[dev.typeparams] cmd/compile: small fixes for stenciling + 2021-02-10df23540dde[dev.typeparams] cmd/gofmt: add the -G flag to allow generic code + 2021-02-10ddec18cf82[dev.typeparams] cmd/compile/internal/types2: overlapping embedded interfaces requires go1.14 + 2021-02-10fdf3496fcc[dev.typeparams] cmd/compile: make type conversions by type parameters work + 2021-02-1012e15d430d[dev.typeparams] cmd/compile: handle calling a method on a type param in stenciling + 2021-02-08ca18c42054[dev.typeparams] merge dev.regabi (618e3c1) into dev.typeparams + 2021-02-08a360eeb528[dev.typeparams] cmd/compile/internal/types2: conversions to type parameters are not constant + 2021-02-080fbde54ea6[dev.typeparams] cmd/compile: allow generic funcs to call other generic funcs for stenciling + 2021-02-05dcb5e0392e[dev.typeparams] cmd/compile: add stenciling of simple generic functions + 2021-02-04f37b0c6c12[dev.typeparams] cmd/compile/internal/types2: type alias decl requires go1.9 + 2021-02-04721488498a[dev.typeparams] cmd/compile: pass -lang flag value to new type checker + 2021-02-041ff2fdaaf1[dev.typeparams] cmd/compile/internal/types2: add support for language version checking + 2021-02-04370e9f5843[dev.typeparams] cmd/compile/internal/types2: use 512 bits as max. integer precision + 2021-02-04ca2f152893[dev.typeparams] go/types: add missing test from dev.go2go + 2021-02-03dc122c7a9c[dev.typeparams] test: exclude a failing test again (fix 32bit builds) + 2021-02-03c910fd7b77[dev.typeparams] cmd/compile: refuse excessively long constants + 2021-02-033db6e18468[dev.typeparams] test: enable more errorcheck tests + 2021-02-03bb53a5ad43[dev.typeparams] cmd/compile/internal/importer: adjust importer to match compiler importer + 2021-02-033f845b3b45[dev.typeparams] cmd/compile: deal with inferred type arguments + 2021-02-03e633f343ba[dev.typeparams] cmd/compile: add OFUNCINST/OTYPEINST nodes for generic func/type instantiation + 2021-02-020d2d6c7464[dev.typeparams] all: merge dev.regabi (23b0c1f) into dev.typeparams + 2021-02-023d5c715bf2[dev.typeparams] Handling multiple type arguments for call via new node OLIST + 2021-02-0113a7412983[dev.typeparams] Parse a generic type arg for generic function call + 2021-01-300aafd69124[dev.typeparams] cmd/compile: start translating type params in noder2 + 2021-01-30a59cb5109d[dev.typeparams] cmd/compile/internal/types2: handle untyped constant arithmetic overflow + 2021-01-29507e641963[dev.typeparams] cmd/compile/internal/typecheck: declutter generated builtin.go (cleanup) + 2021-01-282440dd457a[dev.typeparams] cmd/compile: start adding info needed for typeparams in types & ir + 2021-01-28c0bf904ddf[dev.typeparams] cmd/compile/internal/types2: translate syntax to token constants via tables + 2021-01-28f7d1c5990b[dev.typeparams] cmd/compile/internal/types2: must not import a package called "init" + 2021-01-27217a461f56[dev.typeparams] cmd/compile/internal/types2: report unused packages in source order + 2021-01-2608a598f8c1[dev.typeparams] cmd/compile: fix MethodExpr handling with embedded fields + 2021-01-26cecc1dfcba[dev.typeparams] test: enable excluded test fixedbugs/issue7742.go + 2021-01-26e48d7d3b21[dev.typeparams] go/constant: faster match implementation + 2021-01-26d39685e5e9[dev.typeparams] go/constant: choose internal float representations more consistently + 2021-01-2534704e374f[dev.typeparams] all: merge dev.regabi (5e4a0cd) into dev.typeparams + 2021-01-26c97af0036b[dev.typeparams] cmd/compile: force untyped constants from types2 to expected kind + 2021-01-253663a437a7[dev.typeparams] go/constant: in ToFloat, convert to rational numbers, not floats + 2021-01-253432d24babMerge "[dev.typeparams] all: merge dev.regabi (063c72f) into dev.typeparams" into dev.typeparams + 2021-01-25493eb6e6ec[dev.typeparams] cmd/compile: fix -G=3 handling of blank methods + 2021-01-2513f02018af[dev.typeparams] test: enable more errorcheck tests w/ -G=3 + 2021-01-246d8d118762[dev.typeparams] all: merge dev.regabi (063c72f) into dev.typeparams + 2021-01-249456804e86[dev.typeparams] test: fix excluded files lookup so it works on Windows + 2021-01-237947df436d[dev.typeparams] test: set -G=3 and enable more errorcheck tests in run.go + 2021-01-23a49e941027[dev.typeparams] cmd/compile/internal/types2: remove MethodSet code - not used by types2 + 2021-01-235347241b5e[dev.typeparams] cmd/compile/internal/types2: use same sort criteria for methods as compiler + 2021-01-232b95c28b18[dev.typeparams] cmd/compile: refactor SelectorExpr code into helpers + 2021-01-231946a77e69Merge "[dev.typeparams] all: merge dev.regabi (7e0a81d) into dev.typeparams" into dev.typeparams + 2021-01-236923019a71[dev.typeparams] cmd/compile/internal/types2: factor out sorting of methods + 2021-01-226e46c8fbb5[dev.typeparams] all: merge dev.regabi (7e0a81d) into dev.typeparams + 2021-01-22e4ef30a667[dev.typeparams] cmd/compile: refactor irgen's handling of ":=" + 2021-01-22626406b703[dev.typeparams] go/types: import api_test.go changes from dev.go2go + 2021-01-2212cd9cf7e0[dev.typeparams] cmd/compile: disambiguate OXDOT in noder using types2 Selection info + 2021-01-22f8654579cd[dev.typeparams] cmd/compile/internal/types2: adjust errors in branch checking code, fix a bug + 2021-01-2118bd7aa625[dev.typeparams] cmd/compile: use nil instead of syntax.ImplicitOne + 2021-01-212427f6e6c0[dev.typeparams] cmd/compile: directly set some simple expression types + 2021-01-21455c29af83[dev.typeparams] cmd/compile/internal/types2: convert untyped arguments to delete + 2021-01-21f03f934ede[dev.typeparams] cmd/compile/internal/types2: make predeclared "any" alias for interface{} + 2021-01-200f054c5be0[dev.typeparams] cmd/dist: add -G=3 test coverage + 2021-01-2089ec17be9a[dev.typeparams] cmd/compile: simplify how irgen handles qualified idents + 2021-01-20fa01ade41e[dev.typeparams] go/types: add tests from dev.go2go + 2021-01-20734cb8be0a[dev.typeparams] go/types: refactor untyped conversion for typeparams + 2021-01-20d8796b5670[dev.typeparams] cmd/compile/internal/types2: report type of nil based on context + 2021-01-1948a3cb399d[dev.typeparams] go/types: fix some merge errors in call.go + 2021-01-193e15bf7716[dev.typeparams] go/types: don't modify Named.underlying in validType + 2021-01-19f38f862417[dev.typeparams] go/types: strip annotations from errors + 2021-01-192e64511ac9[dev.typeparams] go/types: unify methods in missingMethod + 2021-01-19958927c824[dev.typeparams] go/parser: error for type instances without ParseTypeParams + 2021-01-1990bfc73071[dev.typeparams] cmd/compile: cache mapped types during irgen + 2021-01-193c0a39c964[dev.typeparams] cmd/compile/internal/types: minor fixes/cleanups around testing + 2021-01-15502198c8dc[dev.typeparams] cmd/compile/internal/types2: consistently report nil type as "untyped nil" + 2021-01-1482c3f0a358[dev.typeparams] cmd/compile/internal/types2: untyped shift counts must fit into uint + 2021-01-1467bf62d939[dev.typeparams] cmd/compile/internal/types2: better error message for invalid ... use + 2021-01-14ef5285fbd0[dev.typeparams] cmd/compile: add types2-based noder + 2021-01-12f065ff221b[dev.typeparams] all: merge dev.regabi (d9acf6f) into dev.typeparams + 2021-01-12099599662d[dev.typeparams] cmd/compile: refactor import logic + 2021-01-12106aa941df[dev.typeparams] cmd/compile: refactor DWARF scope marking + 2021-01-126a56c6c870[dev.typeparams] go/types: import dev.go2go changes to check tests + 2021-01-119e746e4255[dev.typeparams] cmd/compile: refactor varEmbed logic + 2021-01-113e1a87ac2a[dev.typeparams] cmd/compile: extract posMap from noder + 2021-01-112e8f29b79d[dev.typeparams] cmd/compile: add types2.Sizes implementation + 2021-01-1144d1a8523a[dev.typeparams] cmd/compile/internal/types2: fixes for all.bash + 2021-01-118123bc90b8[dev.typeparams] cmd/go: relax test expectation + 2021-01-118c5aa42c79[dev.typeparams] cmd/compile: calculate variable sizes in walk + 2021-01-111ce0854157[dev.typeparams] import stmt changes from dev.go2go + 2021-01-11eb53a6c7cf[dev.typeparams] import operand.go changes from dev.go2go + 2021-01-1181cd99858d[dev.typeparams] go/types: import expr changes from dev.go2go + 2021-01-08822aeacd9e[dev.typeparams] cmd/compile/internal/syntax: remove ShortString, use String instead + 2021-01-08d017a1b649[dev.typeparams] cmd/compile/internal/syntax: add Walk node vistor from types2 + 2021-01-087903214fcc[dev.typeparams] cmd/compile/internal/syntax: add ShortString tests + 2021-01-080aede1205b[dev.typeparams] cmd/compile/internal/types2: use syntax printer to print expressions + 2021-01-08934f9dc0ef[dev.typeparams] cmd/compile/internal/syntax: clean up node printing API + 2021-01-075b9152de57[dev.typeparams] all: merge dev.regabi (cb05a0a) into dev.typeparams + 2021-01-077e689f86e3[dev.typeparams] go/types: move use and useLHS to match dev.go2go + 2021-01-06d76cefed1f[dev.typeparams] go/types: remove disabled code related to type lists + 2021-01-060e286579c5[dev.typeparams] go/types: import typexpr.go from dev.go2go + 2021-01-069546596d77[dev.typeparams] cmd/compile/internal/types2: remove disabled code related to type lists + 2021-01-06196102d046[dev.typeparams] cmd/compile/internal/types2: review of typexpr.go + 2021-01-05a8fe098a12[dev.typeparams] all: merge dev.regabi (77365c5) into dev.typeparams + 2020-12-28a800acaae1[dev.typeparams] all: merge dev.regabi (07569da) into dev.typeparams + 2020-12-27dd40bbc57b[dev.typeparams] cmd/compile: re-enable internal/types2 test + 2020-12-23d99dd17827[dev.typeparams] all: merge dev.regabi (37f138d) into dev.typeparams + 2020-12-228b1fbd8215[dev.typeparams] all: merge dev.regabi (6d03cde) into dev.typeparams + 2020-12-2223922cf83b[dev.typeparams] all: merge dev.regabi (d1d1099) into dev.typeparams + 2020-12-23788dad53c5[dev.typeparams] cmd/compile/internal/types2: disable external test temporarily + 2020-12-2291cc51e005[dev.typeparams] all: merge dev.regabi (ec741b0) into dev.typeparams + 2020-12-22e02a007ffd[dev.typeparams] codereview.cfg: add config for dev.typeparams + 2020-12-2241e7901ca4[dev.typeparams] cmd/compile/internal/types2: report error for invalid main function signature + 2020-12-2253c4c17b09[dev.typeparams] all: merge dev.regabi into dev.typeparams + 2020-12-17060cdbc7b5[dev.typeparams] go/types: import object resolution from dev.go2go + 2020-12-17c4f0da5750[dev.typeparams] cmd/compile/internal/types2: remove code for implicit type arguments + 2020-12-16068dd0470b[dev.typeparams] cmd/compile/internal/syntax: don't panic when providing -verify + 2020-12-167909d6ec28[dev.typeparams] cmd/compile/internal/syntax: type parameters must always be named + 2020-12-163b5918c757[dev.typeparams] cmd/compile/internal/types2: review of predicates.go + 2020-12-1609abd23d9e[dev.typeparams] go/types: import predicates.go from dev.go2go + 2020-12-16f38da2cbb6[dev.typeparams] cmd/compile/internal/types2: review of unify.go + 2020-12-16ceb77db24f[dev.typeparams] go/types: import some support functions from dev.go2go + 2020-12-15a4d4c10340[dev.typeparams] go/types: import lookup logic from dev.go2go + 2020-12-151306435103[dev.typeparams] go/types: import changes to types.Info from dev.go2go + 2020-12-156b18081d01[dev.typeparams] cmd/compile/internal/types2: don't crash if import path is missing + 2020-12-1514e4267c34[dev.typeparams] cmd/compile/internal/types2: report error for invalid (but empty) expr switch + 2020-12-1596999296e6[dev.typeparams] go/types: import unify.go and infer.go from dev.go2go + 2020-12-155aff757efc[dev.typeparams] cmd/compile/internal/gc: provide types2 selection info to noder + 2020-12-14c8e73489c3go/types: import instance.expand and subst.go from dev.go2go + 2020-12-143a912f279f[dev.typeparams] cmd/compile/internal/syntax: export NewName and use it + 2020-12-148ec9e89000[dev.typeparams] cmd/compile/internal/types2: fix stray ')' in error + 2020-12-148fe8e29c9f[dev.typeparams] cmd/compile/internal/types2: report error for invalid type expression + 2020-12-14f8930a2413[dev.typeparams] cmd/compile/internal/types2: report invalid ... in conversions + 2020-12-145aca6e7857[dev.typeparams] test: finish triaging all outstanding failing tests + 2020-12-14df58f3368e[dev.typeparams] cmd/compile/internal/types2: don't report two errors for bad strings + 2020-12-1491803a2df3[dev.typeparams] merge: merge branch 'dev.regabi' into 'dev.typeparams' + 2020-12-11a20021227e[dev.typeparams] cmd/compile/internal/types2: bring over subst.go changes from go/types + 2020-12-10ddf44904f1[dev.typeparams] test: exclude 32bit-specific test that fails on 32bit platforms (fix build) + 2020-12-10dbce27d29c[dev.typeparams] cmd/compile/internal/types2: report correct line number for missing key + 2020-12-09edf80c4209[dev.typeparams] cmd/compile/internal/types2: adjusted more error messages for compiler + 2020-12-0943c7b214db[dev.typeparams] cmd/compile/internal/types2: adjusted qualified identifier error message for compiler + 2020-12-096812eae2e2[dev.typeparams] cmd/compile/internal/types2: adjust init cycle error message for compiler + 2020-12-09810957b155[dev.typeparams] cmd/compile/internal/types2: adjusted array error message for compiler + 2020-12-09c32566c336[dev.typeparams] cmd/compile/internal/types2: avoid endless recursion in Comparable predicate + 2020-12-086015c4e543[dev.typeparams] go/*: add TODOs from CLs importing dev.go2go changes + 2020-12-08b6e678573d[dev.typeparams] go/types: import universe changes from dev.go2go + 2020-12-0814dc2d2345[dev.typeparams] go/types: import the Type API from dev.go2go + 2020-12-07cd15a48036[dev.typeparams] cmd/compile/internal/types2: correct error position for inherited const init expression + 2020-12-0402820d61a9[dev.typeparams] test: enable some more errorcheck tests + 2020-12-039ff27e9fad[dev.typeparams] test: run all errorcheck tests that pass compiler with -G flag + 2020-12-037a1aa7dfaf[dev.typeparams] test: adjust more test cases to match compiler -G output + 2020-12-0307d32c8183[dev.typeparams] cmd/compile/internal/types: adjust some error messages to match the compiler + 2020-12-036b4da14dd3[dev.typeparams] cmd/compile: provide scaffolding to get types2 types during noding + 2020-12-0272cc2353f0[dev.typeparams] go/printer: adapt changes from dev.go2go + 2020-12-02ab18125567[dev.typeparams] cmd/compile/internal/types2: no "declared but not used" errors for invalid var decls + 2020-12-02036245862a[dev.typeparams] cmd/compile/internal/types2: set compiler error message for undeclared variable + 2020-12-01bdc4ffe9a8[dev.typeparams] cmd/compile/internal/types2: add Config.IgnoreBranches flag + 2020-12-0172ad2f44ea[dev.typeparams] test: add scaffolding to run.go to check compiler with -G flag + 2020-12-0187bc85a846[dev.typeparams] cmd/compile/internal/types2: adjustments toward matching compiler error messages + 2020-12-01fbc4c6a3ae[dev.typeparams] cmd/compile/internal/types2: remove support for type parameter pointer designation + 2020-11-25ba9c35aa12[dev.typeparams] merge dev.regabi40f5bc4d55into dev.typeparams + 2020-11-255c2e14872c[dev.typeparams] merge dev.regabi41f3af9d04into dev.typeparams + 2020-11-252c25cd5ba7[dev.typeparams] cmd/compile/internal/types2: a type parameter is a valid type case in a type switch + 2020-11-240fb733b7f7[dev.typeparams] go/parser: support the ParseTypeParams mode + 2020-11-24b56762129e[dev.typeparams] import go2go changes to parse type parameters + 2020-11-24a324aebb7d[dev.typeparams] go/types, cmd/compile/internal/types2: fix incorrect string(int) conversion (regression) + 2020-11-208fbdacf64c[dev.typeparams] cmd/compile/internal/types2: report constant overflow in binary ops + 2020-11-19b1ae0a0646[dev.typeparams] Merge branch 'master' into dev.typeparams + 2020-11-190123c9b321[dev.typeparams] cmd/compile/internal/types2: report an error for invalid constant values + 2020-11-182140049172[dev.typeparams] cmd/compile/internal/types2: port of https://golang.org/cl/270957 + 2020-11-116877ee1e07[dev.typeparams] cmd/compile: use existing findpkg algorithm when importing through types2 + 2020-11-03165ceb09f9[dev.typeparams] merge master into dev.typeparams + 2020-10-2841ff51ae00[dev.typeparams] cmd/compile/internal/types2: review of scopes.go + 2020-10-279392b82919[dev.typeparams] cmd/compile/internal/types: review of objset.go + 2020-10-27c32ac6c15f[dev.typeparams] cmd/compile/internal/types: review of selection.go + 2020-10-27ff6ab114c9[dev.typeparams] cmd/compile/internal/types: review of gccgosizes.go + 2020-10-27abb31c2558[dev.typeparams] cmd/compile/internal/types2: review of hilbert_test.go + 2020-10-276e98406ac3[dev.typeparams] cmd/compile/internal/types2: review of initorder.go + 2020-10-2738af45b4cb[dev.typeparams] cmd/compile/internal/types2: review of package.go + 2020-10-2787eab74628[dev.typeparams] cmd/compile: enable type-checking of generic code + 2020-10-235bfd2964a6[dev.typeparams] merge master into dev.typeparams + 2020-10-22a10fe9f6e7go/ast: import AST changes supporting typeparams from dev.go2go + 2020-10-217a8a720c80[dev.typeparams] cmd/compile/internal/types2: add *.go2 (generic) tests + 2020-10-21755d6de1d8[dev.typeparams] cmd/compile/internal/types2: add some more tests + 2020-10-211d07306a44[dev.typeparams] cmd/compile/internal/types2: adjust tests, enable Testdata tests + 2020-10-21befc62a2c4[dev.typeparams] cmd/compile/internal/types2: add testdata directory + 2020-10-21ca36ba83ab[dev.typeparams] cmd/compile/internal/importer, types2: initial check-in of types2 and importer + 2020-10-206ff16fe3ee[dev.typeparams] cmd/compile/internal/syntax: add utility functions for testing + 2020-10-195e46c6a10f[dev.typeparams] cmd/compile/internal/syntax: add Pos method + 2020-10-15e9e58a4d49[dev.typeparams] cmd/compile/internal/syntax: fix printing of channel types + 2020-10-1473f529845c[dev.typeparams] cmd/compile/internal/syntax: always use IndexExpr node for type instantiation + 2020-10-1348755e06aa[dev.typeparams] cmd/compile: enable parsing of generic code with new -G flag + 2020-10-137668f02dec[dev.typeparams] cmd/compile/internal/syntax: add type parameter tests + 2020-10-13b627988b0c[dev.typeparams] cmd/compile/internal/syntax: implement parsing of type parameters + 2020-10-13617b633917[dev.typeparams] cmd/compile/internal/syntax: prepare syntax nodes for type parameters + 2020-10-12986cad14e2[dev.typeparams] merge master into dev.typeparams Change-Id: Ie0796a53af1fb5db49f4c748144461d3af598575
This commit is contained in:
commit
9a99515c8f
501 changed files with 51236 additions and 2524 deletions
|
|
@ -54,6 +54,7 @@ type CmdFlags struct {
|
||||||
C CountFlag "help:\"disable printing of columns in error messages\""
|
C CountFlag "help:\"disable printing of columns in error messages\""
|
||||||
D string "help:\"set relative `path` for local imports\""
|
D string "help:\"set relative `path` for local imports\""
|
||||||
E CountFlag "help:\"debug symbol export\""
|
E CountFlag "help:\"debug symbol export\""
|
||||||
|
G CountFlag "help:\"accept generic code\""
|
||||||
I func(string) "help:\"add `directory` to import search path\""
|
I func(string) "help:\"add `directory` to import search path\""
|
||||||
K CountFlag "help:\"debug missing line numbers\""
|
K CountFlag "help:\"debug missing line numbers\""
|
||||||
L CountFlag "help:\"show full file names in error messages\""
|
L CountFlag "help:\"show full file names in error messages\""
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@ func exportf(bout *bio.Writer, format string, args ...interface{}) {
|
||||||
func dumpexport(bout *bio.Writer) {
|
func dumpexport(bout *bio.Writer) {
|
||||||
p := &exporter{marked: make(map[*types.Type]bool)}
|
p := &exporter{marked: make(map[*types.Type]bool)}
|
||||||
for _, n := range typecheck.Target.Exports {
|
for _, n := range typecheck.Target.Exports {
|
||||||
|
// Must catch it here rather than Export(), because the type can be
|
||||||
|
// not fully set (still TFORW) when Export() is called.
|
||||||
|
if n.Type() != nil && n.Type().HasTParam() {
|
||||||
|
base.Fatalf("Cannot (yet) export a generic type: %v", n)
|
||||||
|
}
|
||||||
p.markObject(n)
|
p.markObject(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
92
src/cmd/compile/internal/importer/exportdata.go
Normal file
92
src/cmd/compile/internal/importer/exportdata.go
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements FindExportData.
|
||||||
|
|
||||||
|
package importer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
|
||||||
|
// See $GOROOT/include/ar.h.
|
||||||
|
hdr := make([]byte, 16+12+6+6+8+10+2)
|
||||||
|
_, err = io.ReadFull(r, hdr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// leave for debugging
|
||||||
|
if false {
|
||||||
|
fmt.Printf("header: %s", hdr)
|
||||||
|
}
|
||||||
|
s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
|
||||||
|
size, err = strconv.Atoi(s)
|
||||||
|
if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
|
||||||
|
err = fmt.Errorf("invalid archive header")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name = strings.TrimSpace(string(hdr[:16]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindExportData positions the reader r at the beginning of the
|
||||||
|
// export data section of an underlying GC-created object/archive
|
||||||
|
// file by reading from it. The reader must be positioned at the
|
||||||
|
// start of the file before calling this function. The hdr result
|
||||||
|
// is the string before the export data, either "$$" or "$$B".
|
||||||
|
//
|
||||||
|
func FindExportData(r *bufio.Reader) (hdr string, err error) {
|
||||||
|
// Read first line to make sure this is an object file.
|
||||||
|
line, err := r.ReadSlice('\n')
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("can't find export data (%v)", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(line) == "!<arch>\n" {
|
||||||
|
// Archive file. Scan to __.PKGDEF.
|
||||||
|
var name string
|
||||||
|
if name, _, err = readGopackHeader(r); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// First entry should be __.PKGDEF.
|
||||||
|
if name != "__.PKGDEF" {
|
||||||
|
err = fmt.Errorf("go archive is missing __.PKGDEF")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read first line of __.PKGDEF data, so that line
|
||||||
|
// is once again the first line of the input.
|
||||||
|
if line, err = r.ReadSlice('\n'); err != nil {
|
||||||
|
err = fmt.Errorf("can't find export data (%v)", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now at __.PKGDEF in archive or still at beginning of file.
|
||||||
|
// Either way, line should begin with "go object ".
|
||||||
|
if !strings.HasPrefix(string(line), "go object ") {
|
||||||
|
err = fmt.Errorf("not a Go object file")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip over object header to export data.
|
||||||
|
// Begins after first line starting with $$.
|
||||||
|
for line[0] != '$' {
|
||||||
|
if line, err = r.ReadSlice('\n'); err != nil {
|
||||||
|
err = fmt.Errorf("can't find export data (%v)", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hdr = string(line)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
175
src/cmd/compile/internal/importer/gcimporter.go
Normal file
175
src/cmd/compile/internal/importer/gcimporter.go
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// package importer implements Import for gc-generated object files.
|
||||||
|
package importer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
|
"fmt"
|
||||||
|
"go/build"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// debugging/development support
|
||||||
|
const debug = false
|
||||||
|
|
||||||
|
var pkgExts = [...]string{".a", ".o"}
|
||||||
|
|
||||||
|
// FindPkg returns the filename and unique package id for an import
|
||||||
|
// path based on package information provided by build.Import (using
|
||||||
|
// the build.Default build.Context). A relative srcDir is interpreted
|
||||||
|
// relative to the current working directory.
|
||||||
|
// If no file was found, an empty filename is returned.
|
||||||
|
//
|
||||||
|
func FindPkg(path, srcDir string) (filename, id string) {
|
||||||
|
if path == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var noext string
|
||||||
|
switch {
|
||||||
|
default:
|
||||||
|
// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
|
||||||
|
// Don't require the source files to be present.
|
||||||
|
if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282
|
||||||
|
srcDir = abs
|
||||||
|
}
|
||||||
|
bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
|
||||||
|
if bp.PkgObj == "" {
|
||||||
|
id = path // make sure we have an id to print in error message
|
||||||
|
return
|
||||||
|
}
|
||||||
|
noext = strings.TrimSuffix(bp.PkgObj, ".a")
|
||||||
|
id = bp.ImportPath
|
||||||
|
|
||||||
|
case build.IsLocalImport(path):
|
||||||
|
// "./x" -> "/this/directory/x.ext", "/this/directory/x"
|
||||||
|
noext = filepath.Join(srcDir, path)
|
||||||
|
id = noext
|
||||||
|
|
||||||
|
case filepath.IsAbs(path):
|
||||||
|
// for completeness only - go/build.Import
|
||||||
|
// does not support absolute imports
|
||||||
|
// "/x" -> "/x.ext", "/x"
|
||||||
|
noext = path
|
||||||
|
id = path
|
||||||
|
}
|
||||||
|
|
||||||
|
if false { // for debugging
|
||||||
|
if path != id {
|
||||||
|
fmt.Printf("%s -> %s\n", path, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try extensions
|
||||||
|
for _, ext := range pkgExts {
|
||||||
|
filename = noext + ext
|
||||||
|
if f, err := os.Stat(filename); err == nil && !f.IsDir() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = "" // not found
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import imports a gc-generated package given its import path and srcDir, adds
|
||||||
|
// the corresponding package object to the packages map, and returns the object.
|
||||||
|
// The packages map must contain all packages already imported.
|
||||||
|
//
|
||||||
|
func Import(packages map[string]*types2.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types2.Package, err error) {
|
||||||
|
var rc io.ReadCloser
|
||||||
|
var id string
|
||||||
|
if lookup != nil {
|
||||||
|
// With custom lookup specified, assume that caller has
|
||||||
|
// converted path to a canonical import path for use in the map.
|
||||||
|
if path == "unsafe" {
|
||||||
|
return types2.Unsafe, nil
|
||||||
|
}
|
||||||
|
id = path
|
||||||
|
|
||||||
|
// No need to re-import if the package was imported completely before.
|
||||||
|
if pkg = packages[id]; pkg != nil && pkg.Complete() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f, err := lookup(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rc = f
|
||||||
|
} else {
|
||||||
|
var filename string
|
||||||
|
filename, id = FindPkg(path, srcDir)
|
||||||
|
if filename == "" {
|
||||||
|
if path == "unsafe" {
|
||||||
|
return types2.Unsafe, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("can't find import: %q", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// no need to re-import if the package was imported completely before
|
||||||
|
if pkg = packages[id]; pkg != nil && pkg.Complete() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// open file
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
// add file name to error
|
||||||
|
err = fmt.Errorf("%s: %v", filename, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
rc = f
|
||||||
|
}
|
||||||
|
defer rc.Close()
|
||||||
|
|
||||||
|
var hdr string
|
||||||
|
buf := bufio.NewReader(rc)
|
||||||
|
if hdr, err = FindExportData(buf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch hdr {
|
||||||
|
case "$$\n":
|
||||||
|
err = fmt.Errorf("import %q: old textual export format no longer supported (recompile library)", path)
|
||||||
|
|
||||||
|
case "$$B\n":
|
||||||
|
var data []byte
|
||||||
|
data, err = ioutil.ReadAll(buf)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// The indexed export format starts with an 'i'; the older
|
||||||
|
// binary export format starts with a 'c', 'd', or 'v'
|
||||||
|
// (from "version"). Select appropriate importer.
|
||||||
|
if len(data) > 0 && data[0] == 'i' {
|
||||||
|
_, pkg, err = iImportData(packages, data[1:], id)
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("import %q: unknown export data header: %q", path, hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type byPath []*types2.Package
|
||||||
|
|
||||||
|
func (a byPath) Len() int { return len(a) }
|
||||||
|
func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }
|
||||||
611
src/cmd/compile/internal/importer/gcimporter_test.go
Normal file
611
src/cmd/compile/internal/importer/gcimporter_test.go
Normal file
|
|
@ -0,0 +1,611 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package importer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
|
"fmt"
|
||||||
|
"internal/testenv"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// skipSpecialPlatforms causes the test to be skipped for platforms where
|
||||||
|
// builders (build.golang.org) don't have access to compiled packages for
|
||||||
|
// import.
|
||||||
|
func skipSpecialPlatforms(t *testing.T) {
|
||||||
|
switch platform := runtime.GOOS + "-" + runtime.GOARCH; platform {
|
||||||
|
case "darwin-arm64":
|
||||||
|
t.Skipf("no compiled packages available for import on %s", platform)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile runs the compiler on filename, with dirname as the working directory,
|
||||||
|
// and writes the output file to outdirname.
|
||||||
|
func compile(t *testing.T, dirname, filename, outdirname string) string {
|
||||||
|
// filename must end with ".go"
|
||||||
|
if !strings.HasSuffix(filename, ".go") {
|
||||||
|
t.Fatalf("filename doesn't end in .go: %s", filename)
|
||||||
|
}
|
||||||
|
basename := filepath.Base(filename)
|
||||||
|
outname := filepath.Join(outdirname, basename[:len(basename)-2]+"o")
|
||||||
|
cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", outname, filename)
|
||||||
|
cmd.Dir = dirname
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("%s", out)
|
||||||
|
t.Fatalf("go tool compile %s failed: %s", filename, err)
|
||||||
|
}
|
||||||
|
return outname
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPath(t *testing.T, path, srcDir string) *types2.Package {
|
||||||
|
t0 := time.Now()
|
||||||
|
pkg, err := Import(make(map[string]*types2.Package), path, srcDir, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("testPath(%s): %s", path, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t.Logf("testPath(%s): %v", path, time.Since(t0))
|
||||||
|
return pkg
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxTime = 30 * time.Second
|
||||||
|
|
||||||
|
func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
|
||||||
|
dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
|
||||||
|
list, err := ioutil.ReadDir(dirname)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("testDir(%s): %s", dirname, err)
|
||||||
|
}
|
||||||
|
for _, f := range list {
|
||||||
|
if time.Now().After(endTime) {
|
||||||
|
t.Log("testing time used up")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case !f.IsDir():
|
||||||
|
// try extensions
|
||||||
|
for _, ext := range pkgExts {
|
||||||
|
if strings.HasSuffix(f.Name(), ext) {
|
||||||
|
name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension
|
||||||
|
if testPath(t, filepath.Join(dir, name), dir) != nil {
|
||||||
|
nimports++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case f.IsDir():
|
||||||
|
nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func mktmpdir(t *testing.T) string {
|
||||||
|
tmpdir, err := ioutil.TempDir("", "gcimporter_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("mktmpdir:", err)
|
||||||
|
}
|
||||||
|
if err := os.Mkdir(filepath.Join(tmpdir, "testdata"), 0700); err != nil {
|
||||||
|
os.RemoveAll(tmpdir)
|
||||||
|
t.Fatal("mktmpdir:", err)
|
||||||
|
}
|
||||||
|
return tmpdir
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestImportTestdata(t *testing.T) {
|
||||||
|
// This package only handles gc export data.
|
||||||
|
if runtime.Compiler != "gc" {
|
||||||
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpdir := mktmpdir(t)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
|
||||||
|
compile(t, "testdata", "exports.go", filepath.Join(tmpdir, "testdata"))
|
||||||
|
|
||||||
|
if pkg := testPath(t, "./testdata/exports", tmpdir); pkg != nil {
|
||||||
|
// The package's Imports list must include all packages
|
||||||
|
// explicitly imported by exports.go, plus all packages
|
||||||
|
// referenced indirectly via exported objects in exports.go.
|
||||||
|
// With the textual export format, the list may also include
|
||||||
|
// additional packages that are not strictly required for
|
||||||
|
// import processing alone (they are exported to err "on
|
||||||
|
// the safe side").
|
||||||
|
// TODO(gri) update the want list to be precise, now that
|
||||||
|
// the textual export data is gone.
|
||||||
|
got := fmt.Sprint(pkg.Imports())
|
||||||
|
for _, want := range []string{"go/ast", "go/token"} {
|
||||||
|
if !strings.Contains(got, want) {
|
||||||
|
t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionHandling(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// This package only handles gc export data.
|
||||||
|
if runtime.Compiler != "gc" {
|
||||||
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
const dir = "./testdata/versions"
|
||||||
|
list, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpdir := mktmpdir(t)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
corruptdir := filepath.Join(tmpdir, "testdata", "versions")
|
||||||
|
if err := os.Mkdir(corruptdir, 0700); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range list {
|
||||||
|
name := f.Name()
|
||||||
|
if !strings.HasSuffix(name, ".a") {
|
||||||
|
continue // not a package file
|
||||||
|
}
|
||||||
|
if strings.Contains(name, "corrupted") {
|
||||||
|
continue // don't process a leftover corrupted file
|
||||||
|
}
|
||||||
|
pkgpath := "./" + name[:len(name)-2]
|
||||||
|
|
||||||
|
if testing.Verbose() {
|
||||||
|
t.Logf("importing %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test that export data can be imported
|
||||||
|
_, err := Import(make(map[string]*types2.Package), pkgpath, dir, nil)
|
||||||
|
if err != nil {
|
||||||
|
// ok to fail if it fails with a no longer supported error for select files
|
||||||
|
if strings.Contains(err.Error(), "no longer supported") {
|
||||||
|
switch name {
|
||||||
|
case "test_go1.7_0.a", "test_go1.7_1.a",
|
||||||
|
"test_go1.8_4.a", "test_go1.8_5.a",
|
||||||
|
"test_go1.11_6b.a", "test_go1.11_999b.a":
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
// ok to fail if it fails with a newer version error for select files
|
||||||
|
if strings.Contains(err.Error(), "newer version") {
|
||||||
|
switch name {
|
||||||
|
case "test_go1.11_999i.a":
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
t.Errorf("import %q failed: %v", pkgpath, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// create file with corrupted export data
|
||||||
|
// 1) read file
|
||||||
|
data, err := ioutil.ReadFile(filepath.Join(dir, name))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// 2) find export data
|
||||||
|
i := bytes.Index(data, []byte("\n$$B\n")) + 5
|
||||||
|
j := bytes.Index(data[i:], []byte("\n$$\n")) + i
|
||||||
|
if i < 0 || j < 0 || i > j {
|
||||||
|
t.Fatalf("export data section not found (i = %d, j = %d)", i, j)
|
||||||
|
}
|
||||||
|
// 3) corrupt the data (increment every 7th byte)
|
||||||
|
for k := j - 13; k >= i; k -= 7 {
|
||||||
|
data[k]++
|
||||||
|
}
|
||||||
|
// 4) write the file
|
||||||
|
pkgpath += "_corrupted"
|
||||||
|
filename := filepath.Join(corruptdir, pkgpath) + ".a"
|
||||||
|
ioutil.WriteFile(filename, data, 0666)
|
||||||
|
|
||||||
|
// test that importing the corrupted file results in an error
|
||||||
|
_, err = Import(make(map[string]*types2.Package), pkgpath, corruptdir, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("import corrupted %q succeeded", pkgpath)
|
||||||
|
} else if msg := err.Error(); !strings.Contains(msg, "version skew") {
|
||||||
|
t.Errorf("import %q error incorrect (%s)", pkgpath, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestImportStdLib(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// This package only handles gc export data.
|
||||||
|
if runtime.Compiler != "gc" {
|
||||||
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
dt := maxTime
|
||||||
|
if testing.Short() && testenv.Builder() == "" {
|
||||||
|
dt = 10 * time.Millisecond
|
||||||
|
}
|
||||||
|
nimports := testDir(t, "", time.Now().Add(dt)) // installed packages
|
||||||
|
t.Logf("tested %d imports", nimports)
|
||||||
|
}
|
||||||
|
|
||||||
|
var importedObjectTests = []struct {
|
||||||
|
name string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
// non-interfaces
|
||||||
|
{"crypto.Hash", "type Hash uint"},
|
||||||
|
{"go/ast.ObjKind", "type ObjKind int"},
|
||||||
|
{"go/types.Qualifier", "type Qualifier func(*Package) string"},
|
||||||
|
{"go/types.Comparable", "func Comparable(T Type) bool"},
|
||||||
|
{"math.Pi", "const Pi untyped float"},
|
||||||
|
{"math.Sin", "func Sin(x float64) float64"},
|
||||||
|
{"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"},
|
||||||
|
{"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"},
|
||||||
|
|
||||||
|
// interfaces
|
||||||
|
{"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"},
|
||||||
|
{"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"},
|
||||||
|
{"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
|
||||||
|
{"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
|
||||||
|
{"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"},
|
||||||
|
{"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"},
|
||||||
|
// go/types.Type has grown much larger - excluded for now
|
||||||
|
// {"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestImportedTypes(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// This package only handles gc export data.
|
||||||
|
if runtime.Compiler != "gc" {
|
||||||
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range importedObjectTests {
|
||||||
|
s := strings.Split(test.name, ".")
|
||||||
|
if len(s) != 2 {
|
||||||
|
t.Fatal("inconsistent test data")
|
||||||
|
}
|
||||||
|
importPath := s[0]
|
||||||
|
objName := s[1]
|
||||||
|
|
||||||
|
pkg, err := Import(make(map[string]*types2.Package), importPath, ".", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := pkg.Scope().Lookup(objName)
|
||||||
|
if obj == nil {
|
||||||
|
t.Errorf("%s: object not found", test.name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
got := types2.ObjectString(obj, types2.RelativeTo(pkg))
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("%s: got %q; want %q", test.name, got, test.want)
|
||||||
|
}
|
||||||
|
|
||||||
|
if named, _ := obj.Type().(*types2.Named); named != nil {
|
||||||
|
verifyInterfaceMethodRecvs(t, named, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifyInterfaceMethodRecvs verifies that method receiver types
|
||||||
|
// are named if the methods belong to a named interface type.
|
||||||
|
func verifyInterfaceMethodRecvs(t *testing.T, named *types2.Named, level int) {
|
||||||
|
// avoid endless recursion in case of an embedding bug that lead to a cycle
|
||||||
|
if level > 10 {
|
||||||
|
t.Errorf("%s: embeds itself", named)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
iface, _ := named.Underlying().(*types2.Interface)
|
||||||
|
if iface == nil {
|
||||||
|
return // not an interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// check explicitly declared methods
|
||||||
|
for i := 0; i < iface.NumExplicitMethods(); i++ {
|
||||||
|
m := iface.ExplicitMethod(i)
|
||||||
|
recv := m.Type().(*types2.Signature).Recv()
|
||||||
|
if recv == nil {
|
||||||
|
t.Errorf("%s: missing receiver type", m)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if recv.Type() != named {
|
||||||
|
t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check embedded interfaces (if they are named, too)
|
||||||
|
for i := 0; i < iface.NumEmbeddeds(); i++ {
|
||||||
|
// embedding of interfaces cannot have cycles; recursion will terminate
|
||||||
|
if etype, _ := iface.EmbeddedType(i).(*types2.Named); etype != nil {
|
||||||
|
verifyInterfaceMethodRecvs(t, etype, level+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue5815(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// This package only handles gc export data.
|
||||||
|
if runtime.Compiler != "gc" {
|
||||||
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg := importPkg(t, "strings", ".")
|
||||||
|
|
||||||
|
scope := pkg.Scope()
|
||||||
|
for _, name := range scope.Names() {
|
||||||
|
obj := scope.Lookup(name)
|
||||||
|
if obj.Pkg() == nil {
|
||||||
|
t.Errorf("no pkg for %s", obj)
|
||||||
|
}
|
||||||
|
if tname, _ := obj.(*types2.TypeName); tname != nil {
|
||||||
|
named := tname.Type().(*types2.Named)
|
||||||
|
for i := 0; i < named.NumMethods(); i++ {
|
||||||
|
m := named.Method(i)
|
||||||
|
if m.Pkg() == nil {
|
||||||
|
t.Errorf("no pkg for %s", m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smoke test to ensure that imported methods get the correct package.
|
||||||
|
func TestCorrectMethodPackage(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// This package only handles gc export data.
|
||||||
|
if runtime.Compiler != "gc" {
|
||||||
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
imports := make(map[string]*types2.Package)
|
||||||
|
_, err := Import(imports, "net/http", ".", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex := imports["sync"].Scope().Lookup("Mutex").(*types2.TypeName).Type()
|
||||||
|
obj, _, _ := types2.LookupFieldOrMethod(types2.NewPointer(mutex), false, nil, "Lock")
|
||||||
|
lock := obj.(*types2.Func)
|
||||||
|
if got, want := lock.Pkg().Path(), "sync"; got != want {
|
||||||
|
t.Errorf("got package path %q; want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue13566(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// This package only handles gc export data.
|
||||||
|
if runtime.Compiler != "gc" {
|
||||||
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// On windows, we have to set the -D option for the compiler to avoid having a drive
|
||||||
|
// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("avoid dealing with relative paths/drive letters on windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpdir := mktmpdir(t)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
testoutdir := filepath.Join(tmpdir, "testdata")
|
||||||
|
|
||||||
|
// b.go needs to be compiled from the output directory so that the compiler can
|
||||||
|
// find the compiled package a. We pass the full path to compile() so that we
|
||||||
|
// don't have to copy the file to that directory.
|
||||||
|
bpath, err := filepath.Abs(filepath.Join("testdata", "b.go"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
compile(t, "testdata", "a.go", testoutdir)
|
||||||
|
compile(t, testoutdir, bpath, testoutdir)
|
||||||
|
|
||||||
|
// import must succeed (test for issue at hand)
|
||||||
|
pkg := importPkg(t, "./testdata/b", tmpdir)
|
||||||
|
|
||||||
|
// make sure all indirectly imported packages have names
|
||||||
|
for _, imp := range pkg.Imports() {
|
||||||
|
if imp.Name() == "" {
|
||||||
|
t.Errorf("no name for %s package", imp.Path())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue13898(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// This package only handles gc export data.
|
||||||
|
if runtime.Compiler != "gc" {
|
||||||
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// import go/internal/gcimporter which imports go/types partially
|
||||||
|
imports := make(map[string]*types2.Package)
|
||||||
|
_, err := Import(imports, "go/internal/gcimporter", ".", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// look for go/types package
|
||||||
|
var goTypesPkg *types2.Package
|
||||||
|
for path, pkg := range imports {
|
||||||
|
if path == "go/types" {
|
||||||
|
goTypesPkg = pkg
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if goTypesPkg == nil {
|
||||||
|
t.Fatal("go/types not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// look for go/types2.Object type
|
||||||
|
obj := lookupObj(t, goTypesPkg.Scope(), "Object")
|
||||||
|
typ, ok := obj.Type().(*types2.Named)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("go/types2.Object type is %v; wanted named type", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookup go/types2.Object.Pkg method
|
||||||
|
m, index, indirect := types2.LookupFieldOrMethod(typ, false, nil, "Pkg")
|
||||||
|
if m == nil {
|
||||||
|
t.Fatalf("go/types2.Object.Pkg not found (index = %v, indirect = %v)", index, indirect)
|
||||||
|
}
|
||||||
|
|
||||||
|
// the method must belong to go/types
|
||||||
|
if m.Pkg().Path() != "go/types" {
|
||||||
|
t.Fatalf("found %v; want go/types", m.Pkg())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue15517(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// This package only handles gc export data.
|
||||||
|
if runtime.Compiler != "gc" {
|
||||||
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// On windows, we have to set the -D option for the compiler to avoid having a drive
|
||||||
|
// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("avoid dealing with relative paths/drive letters on windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpdir := mktmpdir(t)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
|
||||||
|
compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"))
|
||||||
|
|
||||||
|
// Multiple imports of p must succeed without redeclaration errors.
|
||||||
|
// We use an import path that's not cleaned up so that the eventual
|
||||||
|
// file path for the package is different from the package path; this
|
||||||
|
// will expose the error if it is present.
|
||||||
|
//
|
||||||
|
// (Issue: Both the textual and the binary importer used the file path
|
||||||
|
// of the package to be imported as key into the shared packages map.
|
||||||
|
// However, the binary importer then used the package path to identify
|
||||||
|
// the imported package to mark it as complete; effectively marking the
|
||||||
|
// wrong package as complete. By using an "unclean" package path, the
|
||||||
|
// file and package path are different, exposing the problem if present.
|
||||||
|
// The same issue occurs with vendoring.)
|
||||||
|
imports := make(map[string]*types2.Package)
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
if _, err := Import(imports, "./././testdata/p", tmpdir, nil); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue15920(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// This package only handles gc export data.
|
||||||
|
if runtime.Compiler != "gc" {
|
||||||
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// On windows, we have to set the -D option for the compiler to avoid having a drive
|
||||||
|
// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("avoid dealing with relative paths/drive letters on windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
compileAndImportPkg(t, "issue15920")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue20046(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// This package only handles gc export data.
|
||||||
|
if runtime.Compiler != "gc" {
|
||||||
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// On windows, we have to set the -D option for the compiler to avoid having a drive
|
||||||
|
// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("avoid dealing with relative paths/drive letters on windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
// "./issue20046".V.M must exist
|
||||||
|
pkg := compileAndImportPkg(t, "issue20046")
|
||||||
|
obj := lookupObj(t, pkg.Scope(), "V")
|
||||||
|
if m, index, indirect := types2.LookupFieldOrMethod(obj.Type(), false, nil, "M"); m == nil {
|
||||||
|
t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestIssue25301(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// This package only handles gc export data.
|
||||||
|
if runtime.Compiler != "gc" {
|
||||||
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// On windows, we have to set the -D option for the compiler to avoid having a drive
|
||||||
|
// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("avoid dealing with relative paths/drive letters on windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
compileAndImportPkg(t, "issue25301")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue25596(t *testing.T) {
|
||||||
|
skipSpecialPlatforms(t)
|
||||||
|
|
||||||
|
// This package only handles gc export data.
|
||||||
|
if runtime.Compiler != "gc" {
|
||||||
|
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// On windows, we have to set the -D option for the compiler to avoid having a drive
|
||||||
|
// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("avoid dealing with relative paths/drive letters on windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
compileAndImportPkg(t, "issue25596")
|
||||||
|
}
|
||||||
|
|
||||||
|
func importPkg(t *testing.T, path, srcDir string) *types2.Package {
|
||||||
|
pkg, err := Import(make(map[string]*types2.Package), path, srcDir, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return pkg
|
||||||
|
}
|
||||||
|
|
||||||
|
func compileAndImportPkg(t *testing.T, name string) *types2.Package {
|
||||||
|
tmpdir := mktmpdir(t)
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"))
|
||||||
|
return importPkg(t, "./testdata/"+name, tmpdir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupObj(t *testing.T, scope *types2.Scope, name string) types2.Object {
|
||||||
|
if obj := scope.Lookup(name); obj != nil {
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
t.Fatalf("%s not found", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
612
src/cmd/compile/internal/importer/iimport.go
Normal file
612
src/cmd/compile/internal/importer/iimport.go
Normal file
|
|
@ -0,0 +1,612 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Indexed package import.
|
||||||
|
// See cmd/compile/internal/gc/iexport.go for the export data format.
|
||||||
|
|
||||||
|
package importer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"go/constant"
|
||||||
|
"go/token"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
type intReader struct {
|
||||||
|
*bytes.Reader
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *intReader) int64() int64 {
|
||||||
|
i, err := binary.ReadVarint(r.Reader)
|
||||||
|
if err != nil {
|
||||||
|
errorf("import %q: read varint error: %v", r.path, err)
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *intReader) uint64() uint64 {
|
||||||
|
i, err := binary.ReadUvarint(r.Reader)
|
||||||
|
if err != nil {
|
||||||
|
errorf("import %q: read varint error: %v", r.path, err)
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
const predeclReserved = 32
|
||||||
|
|
||||||
|
type itag uint64
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Types
|
||||||
|
definedType itag = iota
|
||||||
|
pointerType
|
||||||
|
sliceType
|
||||||
|
arrayType
|
||||||
|
chanType
|
||||||
|
mapType
|
||||||
|
signatureType
|
||||||
|
structType
|
||||||
|
interfaceType
|
||||||
|
)
|
||||||
|
|
||||||
|
const io_SeekCurrent = 1 // io.SeekCurrent (not defined in Go 1.4)
|
||||||
|
|
||||||
|
// iImportData imports a package from the serialized package data
|
||||||
|
// and returns the number of bytes consumed and a reference to the package.
|
||||||
|
// If the export data version is not recognized or the format is otherwise
|
||||||
|
// compromised, an error is returned.
|
||||||
|
func iImportData(imports map[string]*types2.Package, data []byte, path string) (_ int, pkg *types2.Package, err error) {
|
||||||
|
const currentVersion = 1
|
||||||
|
version := int64(-1)
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
if version > currentVersion {
|
||||||
|
err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
r := &intReader{bytes.NewReader(data), path}
|
||||||
|
|
||||||
|
version = int64(r.uint64())
|
||||||
|
switch version {
|
||||||
|
case currentVersion, 0:
|
||||||
|
default:
|
||||||
|
errorf("unknown iexport format version %d", version)
|
||||||
|
}
|
||||||
|
|
||||||
|
sLen := int64(r.uint64())
|
||||||
|
dLen := int64(r.uint64())
|
||||||
|
|
||||||
|
whence, _ := r.Seek(0, io_SeekCurrent)
|
||||||
|
stringData := data[whence : whence+sLen]
|
||||||
|
declData := data[whence+sLen : whence+sLen+dLen]
|
||||||
|
r.Seek(sLen+dLen, io_SeekCurrent)
|
||||||
|
|
||||||
|
p := iimporter{
|
||||||
|
ipath: path,
|
||||||
|
version: int(version),
|
||||||
|
|
||||||
|
stringData: stringData,
|
||||||
|
stringCache: make(map[uint64]string),
|
||||||
|
pkgCache: make(map[uint64]*types2.Package),
|
||||||
|
|
||||||
|
declData: declData,
|
||||||
|
pkgIndex: make(map[*types2.Package]map[string]uint64),
|
||||||
|
typCache: make(map[uint64]types2.Type),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, pt := range predeclared {
|
||||||
|
p.typCache[uint64(i)] = pt
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgList := make([]*types2.Package, r.uint64())
|
||||||
|
for i := range pkgList {
|
||||||
|
pkgPathOff := r.uint64()
|
||||||
|
pkgPath := p.stringAt(pkgPathOff)
|
||||||
|
pkgName := p.stringAt(r.uint64())
|
||||||
|
_ = r.uint64() // package height; unused by go/types
|
||||||
|
|
||||||
|
if pkgPath == "" {
|
||||||
|
pkgPath = path
|
||||||
|
}
|
||||||
|
pkg := imports[pkgPath]
|
||||||
|
if pkg == nil {
|
||||||
|
pkg = types2.NewPackage(pkgPath, pkgName)
|
||||||
|
imports[pkgPath] = pkg
|
||||||
|
} else if pkg.Name() != pkgName {
|
||||||
|
errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.pkgCache[pkgPathOff] = pkg
|
||||||
|
|
||||||
|
nameIndex := make(map[string]uint64)
|
||||||
|
for nSyms := r.uint64(); nSyms > 0; nSyms-- {
|
||||||
|
name := p.stringAt(r.uint64())
|
||||||
|
nameIndex[name] = r.uint64()
|
||||||
|
}
|
||||||
|
|
||||||
|
p.pkgIndex[pkg] = nameIndex
|
||||||
|
pkgList[i] = pkg
|
||||||
|
}
|
||||||
|
|
||||||
|
localpkg := pkgList[0]
|
||||||
|
|
||||||
|
names := make([]string, 0, len(p.pkgIndex[localpkg]))
|
||||||
|
for name := range p.pkgIndex[localpkg] {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
for _, name := range names {
|
||||||
|
p.doDecl(localpkg, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, typ := range p.interfaceList {
|
||||||
|
typ.Complete()
|
||||||
|
}
|
||||||
|
|
||||||
|
// record all referenced packages as imports
|
||||||
|
list := append(([]*types2.Package)(nil), pkgList[1:]...)
|
||||||
|
sort.Sort(byPath(list))
|
||||||
|
localpkg.SetImports(list)
|
||||||
|
|
||||||
|
// package was imported completely and without errors
|
||||||
|
localpkg.MarkComplete()
|
||||||
|
|
||||||
|
consumed, _ := r.Seek(0, io_SeekCurrent)
|
||||||
|
return int(consumed), localpkg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type iimporter struct {
|
||||||
|
ipath string
|
||||||
|
version int
|
||||||
|
|
||||||
|
stringData []byte
|
||||||
|
stringCache map[uint64]string
|
||||||
|
pkgCache map[uint64]*types2.Package
|
||||||
|
|
||||||
|
declData []byte
|
||||||
|
pkgIndex map[*types2.Package]map[string]uint64
|
||||||
|
typCache map[uint64]types2.Type
|
||||||
|
|
||||||
|
interfaceList []*types2.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *iimporter) doDecl(pkg *types2.Package, name string) {
|
||||||
|
// See if we've already imported this declaration.
|
||||||
|
if obj := pkg.Scope().Lookup(name); obj != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
off, ok := p.pkgIndex[pkg][name]
|
||||||
|
if !ok {
|
||||||
|
errorf("%v.%v not in index", pkg, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := &importReader{p: p, currPkg: pkg}
|
||||||
|
// Reader.Reset is not available in Go 1.4.
|
||||||
|
// Use bytes.NewReader for now.
|
||||||
|
// r.declReader.Reset(p.declData[off:])
|
||||||
|
r.declReader = *bytes.NewReader(p.declData[off:])
|
||||||
|
|
||||||
|
r.obj(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *iimporter) stringAt(off uint64) string {
|
||||||
|
if s, ok := p.stringCache[off]; ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
slen, n := binary.Uvarint(p.stringData[off:])
|
||||||
|
if n <= 0 {
|
||||||
|
errorf("varint failed")
|
||||||
|
}
|
||||||
|
spos := off + uint64(n)
|
||||||
|
s := string(p.stringData[spos : spos+slen])
|
||||||
|
p.stringCache[off] = s
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *iimporter) pkgAt(off uint64) *types2.Package {
|
||||||
|
if pkg, ok := p.pkgCache[off]; ok {
|
||||||
|
return pkg
|
||||||
|
}
|
||||||
|
path := p.stringAt(off)
|
||||||
|
errorf("missing package %q in %q", path, p.ipath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *iimporter) typAt(off uint64, base *types2.Named) types2.Type {
|
||||||
|
if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
if off < predeclReserved {
|
||||||
|
errorf("predeclared type missing from cache: %v", off)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := &importReader{p: p}
|
||||||
|
// Reader.Reset is not available in Go 1.4.
|
||||||
|
// Use bytes.NewReader for now.
|
||||||
|
// r.declReader.Reset(p.declData[off-predeclReserved:])
|
||||||
|
r.declReader = *bytes.NewReader(p.declData[off-predeclReserved:])
|
||||||
|
t := r.doType(base)
|
||||||
|
|
||||||
|
if base == nil || !isInterface(t) {
|
||||||
|
p.typCache[off] = t
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
type importReader struct {
|
||||||
|
p *iimporter
|
||||||
|
declReader bytes.Reader
|
||||||
|
currPkg *types2.Package
|
||||||
|
prevFile string
|
||||||
|
prevLine int64
|
||||||
|
prevColumn int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) obj(name string) {
|
||||||
|
tag := r.byte()
|
||||||
|
pos := r.pos()
|
||||||
|
|
||||||
|
switch tag {
|
||||||
|
case 'A':
|
||||||
|
typ := r.typ()
|
||||||
|
|
||||||
|
r.declare(types2.NewTypeName(pos, r.currPkg, name, typ))
|
||||||
|
|
||||||
|
case 'C':
|
||||||
|
typ, val := r.value()
|
||||||
|
|
||||||
|
r.declare(types2.NewConst(pos, r.currPkg, name, typ, val))
|
||||||
|
|
||||||
|
case 'F':
|
||||||
|
sig := r.signature(nil)
|
||||||
|
|
||||||
|
r.declare(types2.NewFunc(pos, r.currPkg, name, sig))
|
||||||
|
|
||||||
|
case 'T':
|
||||||
|
// Types can be recursive. We need to setup a stub
|
||||||
|
// declaration before recursing.
|
||||||
|
obj := types2.NewTypeName(pos, r.currPkg, name, nil)
|
||||||
|
named := types2.NewNamed(obj, nil, nil)
|
||||||
|
r.declare(obj)
|
||||||
|
|
||||||
|
underlying := r.p.typAt(r.uint64(), named).Underlying()
|
||||||
|
named.SetUnderlying(underlying)
|
||||||
|
|
||||||
|
if !isInterface(underlying) {
|
||||||
|
for n := r.uint64(); n > 0; n-- {
|
||||||
|
mpos := r.pos()
|
||||||
|
mname := r.ident()
|
||||||
|
recv := r.param()
|
||||||
|
msig := r.signature(recv)
|
||||||
|
|
||||||
|
named.AddMethod(types2.NewFunc(mpos, r.currPkg, mname, msig))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'V':
|
||||||
|
typ := r.typ()
|
||||||
|
|
||||||
|
r.declare(types2.NewVar(pos, r.currPkg, name, typ))
|
||||||
|
|
||||||
|
default:
|
||||||
|
errorf("unexpected tag: %v", tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) declare(obj types2.Object) {
|
||||||
|
obj.Pkg().Scope().Insert(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) value() (typ types2.Type, val constant.Value) {
|
||||||
|
typ = r.typ()
|
||||||
|
|
||||||
|
switch b := typ.Underlying().(*types2.Basic); b.Info() & types2.IsConstType {
|
||||||
|
case types2.IsBoolean:
|
||||||
|
val = constant.MakeBool(r.bool())
|
||||||
|
|
||||||
|
case types2.IsString:
|
||||||
|
val = constant.MakeString(r.string())
|
||||||
|
|
||||||
|
case types2.IsInteger:
|
||||||
|
var x big.Int
|
||||||
|
r.mpint(&x, b)
|
||||||
|
val = constant.Make(&x)
|
||||||
|
|
||||||
|
case types2.IsFloat:
|
||||||
|
val = r.mpfloat(b)
|
||||||
|
|
||||||
|
case types2.IsComplex:
|
||||||
|
re := r.mpfloat(b)
|
||||||
|
im := r.mpfloat(b)
|
||||||
|
val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
|
||||||
|
|
||||||
|
default:
|
||||||
|
errorf("unexpected type %v", typ) // panics
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func intSize(b *types2.Basic) (signed bool, maxBytes uint) {
|
||||||
|
if (b.Info() & types2.IsUntyped) != 0 {
|
||||||
|
return true, 64
|
||||||
|
}
|
||||||
|
|
||||||
|
switch b.Kind() {
|
||||||
|
case types2.Float32, types2.Complex64:
|
||||||
|
return true, 3
|
||||||
|
case types2.Float64, types2.Complex128:
|
||||||
|
return true, 7
|
||||||
|
}
|
||||||
|
|
||||||
|
signed = (b.Info() & types2.IsUnsigned) == 0
|
||||||
|
switch b.Kind() {
|
||||||
|
case types2.Int8, types2.Uint8:
|
||||||
|
maxBytes = 1
|
||||||
|
case types2.Int16, types2.Uint16:
|
||||||
|
maxBytes = 2
|
||||||
|
case types2.Int32, types2.Uint32:
|
||||||
|
maxBytes = 4
|
||||||
|
default:
|
||||||
|
maxBytes = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) mpint(x *big.Int, typ *types2.Basic) {
|
||||||
|
signed, maxBytes := intSize(typ)
|
||||||
|
|
||||||
|
maxSmall := 256 - maxBytes
|
||||||
|
if signed {
|
||||||
|
maxSmall = 256 - 2*maxBytes
|
||||||
|
}
|
||||||
|
if maxBytes == 1 {
|
||||||
|
maxSmall = 256
|
||||||
|
}
|
||||||
|
|
||||||
|
n, _ := r.declReader.ReadByte()
|
||||||
|
if uint(n) < maxSmall {
|
||||||
|
v := int64(n)
|
||||||
|
if signed {
|
||||||
|
v >>= 1
|
||||||
|
if n&1 != 0 {
|
||||||
|
v = ^v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x.SetInt64(v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v := -n
|
||||||
|
if signed {
|
||||||
|
v = -(n &^ 1) >> 1
|
||||||
|
}
|
||||||
|
if v < 1 || uint(v) > maxBytes {
|
||||||
|
errorf("weird decoding: %v, %v => %v", n, signed, v)
|
||||||
|
}
|
||||||
|
b := make([]byte, v)
|
||||||
|
io.ReadFull(&r.declReader, b)
|
||||||
|
x.SetBytes(b)
|
||||||
|
if signed && n&1 != 0 {
|
||||||
|
x.Neg(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) mpfloat(typ *types2.Basic) constant.Value {
|
||||||
|
var mant big.Int
|
||||||
|
r.mpint(&mant, typ)
|
||||||
|
var f big.Float
|
||||||
|
f.SetInt(&mant)
|
||||||
|
if f.Sign() != 0 {
|
||||||
|
f.SetMantExp(&f, int(r.int64()))
|
||||||
|
}
|
||||||
|
return constant.Make(&f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) ident() string {
|
||||||
|
return r.string()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) qualifiedIdent() (*types2.Package, string) {
|
||||||
|
name := r.string()
|
||||||
|
pkg := r.pkg()
|
||||||
|
return pkg, name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) pos() syntax.Pos {
|
||||||
|
if r.p.version >= 1 {
|
||||||
|
r.posv1()
|
||||||
|
} else {
|
||||||
|
r.posv0()
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.prevFile == "" && r.prevLine == 0 && r.prevColumn == 0 {
|
||||||
|
return syntax.Pos{}
|
||||||
|
}
|
||||||
|
// TODO(gri) fix this
|
||||||
|
// return r.p.fake.pos(r.prevFile, int(r.prevLine), int(r.prevColumn))
|
||||||
|
return syntax.Pos{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) posv0() {
|
||||||
|
delta := r.int64()
|
||||||
|
if delta != deltaNewFile {
|
||||||
|
r.prevLine += delta
|
||||||
|
} else if l := r.int64(); l == -1 {
|
||||||
|
r.prevLine += deltaNewFile
|
||||||
|
} else {
|
||||||
|
r.prevFile = r.string()
|
||||||
|
r.prevLine = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) posv1() {
|
||||||
|
delta := r.int64()
|
||||||
|
r.prevColumn += delta >> 1
|
||||||
|
if delta&1 != 0 {
|
||||||
|
delta = r.int64()
|
||||||
|
r.prevLine += delta >> 1
|
||||||
|
if delta&1 != 0 {
|
||||||
|
r.prevFile = r.string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) typ() types2.Type {
|
||||||
|
return r.p.typAt(r.uint64(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isInterface(t types2.Type) bool {
|
||||||
|
_, ok := t.(*types2.Interface)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) pkg() *types2.Package { return r.p.pkgAt(r.uint64()) }
|
||||||
|
func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
|
||||||
|
|
||||||
|
func (r *importReader) doType(base *types2.Named) types2.Type {
|
||||||
|
switch k := r.kind(); k {
|
||||||
|
default:
|
||||||
|
errorf("unexpected kind tag in %q: %v", r.p.ipath, k)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case definedType:
|
||||||
|
pkg, name := r.qualifiedIdent()
|
||||||
|
r.p.doDecl(pkg, name)
|
||||||
|
return pkg.Scope().Lookup(name).(*types2.TypeName).Type()
|
||||||
|
case pointerType:
|
||||||
|
return types2.NewPointer(r.typ())
|
||||||
|
case sliceType:
|
||||||
|
return types2.NewSlice(r.typ())
|
||||||
|
case arrayType:
|
||||||
|
n := r.uint64()
|
||||||
|
return types2.NewArray(r.typ(), int64(n))
|
||||||
|
case chanType:
|
||||||
|
dir := chanDir(int(r.uint64()))
|
||||||
|
return types2.NewChan(dir, r.typ())
|
||||||
|
case mapType:
|
||||||
|
return types2.NewMap(r.typ(), r.typ())
|
||||||
|
case signatureType:
|
||||||
|
r.currPkg = r.pkg()
|
||||||
|
return r.signature(nil)
|
||||||
|
|
||||||
|
case structType:
|
||||||
|
r.currPkg = r.pkg()
|
||||||
|
|
||||||
|
fields := make([]*types2.Var, r.uint64())
|
||||||
|
tags := make([]string, len(fields))
|
||||||
|
for i := range fields {
|
||||||
|
fpos := r.pos()
|
||||||
|
fname := r.ident()
|
||||||
|
ftyp := r.typ()
|
||||||
|
emb := r.bool()
|
||||||
|
tag := r.string()
|
||||||
|
|
||||||
|
fields[i] = types2.NewField(fpos, r.currPkg, fname, ftyp, emb)
|
||||||
|
tags[i] = tag
|
||||||
|
}
|
||||||
|
return types2.NewStruct(fields, tags)
|
||||||
|
|
||||||
|
case interfaceType:
|
||||||
|
r.currPkg = r.pkg()
|
||||||
|
|
||||||
|
embeddeds := make([]types2.Type, r.uint64())
|
||||||
|
for i := range embeddeds {
|
||||||
|
_ = r.pos()
|
||||||
|
embeddeds[i] = r.typ()
|
||||||
|
}
|
||||||
|
|
||||||
|
methods := make([]*types2.Func, r.uint64())
|
||||||
|
for i := range methods {
|
||||||
|
mpos := r.pos()
|
||||||
|
mname := r.ident()
|
||||||
|
|
||||||
|
// TODO(mdempsky): Matches bimport.go, but I
|
||||||
|
// don't agree with this.
|
||||||
|
var recv *types2.Var
|
||||||
|
if base != nil {
|
||||||
|
recv = types2.NewVar(syntax.Pos{}, r.currPkg, "", base)
|
||||||
|
}
|
||||||
|
|
||||||
|
msig := r.signature(recv)
|
||||||
|
methods[i] = types2.NewFunc(mpos, r.currPkg, mname, msig)
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := types2.NewInterfaceType(methods, embeddeds)
|
||||||
|
r.p.interfaceList = append(r.p.interfaceList, typ)
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) kind() itag {
|
||||||
|
return itag(r.uint64())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) signature(recv *types2.Var) *types2.Signature {
|
||||||
|
params := r.paramList()
|
||||||
|
results := r.paramList()
|
||||||
|
variadic := params.Len() > 0 && r.bool()
|
||||||
|
return types2.NewSignature(recv, params, results, variadic)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) paramList() *types2.Tuple {
|
||||||
|
xs := make([]*types2.Var, r.uint64())
|
||||||
|
for i := range xs {
|
||||||
|
xs[i] = r.param()
|
||||||
|
}
|
||||||
|
return types2.NewTuple(xs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) param() *types2.Var {
|
||||||
|
pos := r.pos()
|
||||||
|
name := r.ident()
|
||||||
|
typ := r.typ()
|
||||||
|
return types2.NewParam(pos, r.currPkg, name, typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) bool() bool {
|
||||||
|
return r.uint64() != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) int64() int64 {
|
||||||
|
n, err := binary.ReadVarint(&r.declReader)
|
||||||
|
if err != nil {
|
||||||
|
errorf("readVarint: %v", err)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) uint64() uint64 {
|
||||||
|
n, err := binary.ReadUvarint(&r.declReader)
|
||||||
|
if err != nil {
|
||||||
|
errorf("readUvarint: %v", err)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *importReader) byte() byte {
|
||||||
|
x, err := r.declReader.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
errorf("declReader.ReadByte: %v", err)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
128
src/cmd/compile/internal/importer/support.go
Normal file
128
src/cmd/compile/internal/importer/support.go
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements support functionality for iimport.go.
|
||||||
|
|
||||||
|
package importer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
|
"fmt"
|
||||||
|
"go/token"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func errorf(format string, args ...interface{}) {
|
||||||
|
panic(fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go
|
||||||
|
|
||||||
|
// Synthesize a token.Pos
|
||||||
|
type fakeFileSet struct {
|
||||||
|
fset *token.FileSet
|
||||||
|
files map[string]*token.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *fakeFileSet) pos(file string, line, column int) token.Pos {
|
||||||
|
// TODO(mdempsky): Make use of column.
|
||||||
|
|
||||||
|
// Since we don't know the set of needed file positions, we
|
||||||
|
// reserve maxlines positions per file.
|
||||||
|
const maxlines = 64 * 1024
|
||||||
|
f := s.files[file]
|
||||||
|
if f == nil {
|
||||||
|
f = s.fset.AddFile(file, -1, maxlines)
|
||||||
|
s.files[file] = f
|
||||||
|
// Allocate the fake linebreak indices on first use.
|
||||||
|
// TODO(adonovan): opt: save ~512KB using a more complex scheme?
|
||||||
|
fakeLinesOnce.Do(func() {
|
||||||
|
fakeLines = make([]int, maxlines)
|
||||||
|
for i := range fakeLines {
|
||||||
|
fakeLines[i] = i
|
||||||
|
}
|
||||||
|
})
|
||||||
|
f.SetLines(fakeLines)
|
||||||
|
}
|
||||||
|
|
||||||
|
if line > maxlines {
|
||||||
|
line = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Treat the file as if it contained only newlines
|
||||||
|
// and column=1: use the line number as the offset.
|
||||||
|
return f.Pos(line - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
fakeLines []int
|
||||||
|
fakeLinesOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
func chanDir(d int) types2.ChanDir {
|
||||||
|
// tag values must match the constants in cmd/compile/internal/gc/go.go
|
||||||
|
switch d {
|
||||||
|
case 1 /* Crecv */ :
|
||||||
|
return types2.RecvOnly
|
||||||
|
case 2 /* Csend */ :
|
||||||
|
return types2.SendOnly
|
||||||
|
case 3 /* Cboth */ :
|
||||||
|
return types2.SendRecv
|
||||||
|
default:
|
||||||
|
errorf("unexpected channel dir %d", d)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var predeclared = []types2.Type{
|
||||||
|
// basic types
|
||||||
|
types2.Typ[types2.Bool],
|
||||||
|
types2.Typ[types2.Int],
|
||||||
|
types2.Typ[types2.Int8],
|
||||||
|
types2.Typ[types2.Int16],
|
||||||
|
types2.Typ[types2.Int32],
|
||||||
|
types2.Typ[types2.Int64],
|
||||||
|
types2.Typ[types2.Uint],
|
||||||
|
types2.Typ[types2.Uint8],
|
||||||
|
types2.Typ[types2.Uint16],
|
||||||
|
types2.Typ[types2.Uint32],
|
||||||
|
types2.Typ[types2.Uint64],
|
||||||
|
types2.Typ[types2.Uintptr],
|
||||||
|
types2.Typ[types2.Float32],
|
||||||
|
types2.Typ[types2.Float64],
|
||||||
|
types2.Typ[types2.Complex64],
|
||||||
|
types2.Typ[types2.Complex128],
|
||||||
|
types2.Typ[types2.String],
|
||||||
|
|
||||||
|
// basic type aliases
|
||||||
|
types2.Universe.Lookup("byte").Type(),
|
||||||
|
types2.Universe.Lookup("rune").Type(),
|
||||||
|
|
||||||
|
// error
|
||||||
|
types2.Universe.Lookup("error").Type(),
|
||||||
|
|
||||||
|
// untyped types
|
||||||
|
types2.Typ[types2.UntypedBool],
|
||||||
|
types2.Typ[types2.UntypedInt],
|
||||||
|
types2.Typ[types2.UntypedRune],
|
||||||
|
types2.Typ[types2.UntypedFloat],
|
||||||
|
types2.Typ[types2.UntypedComplex],
|
||||||
|
types2.Typ[types2.UntypedString],
|
||||||
|
types2.Typ[types2.UntypedNil],
|
||||||
|
|
||||||
|
// package unsafe
|
||||||
|
types2.Typ[types2.UnsafePointer],
|
||||||
|
|
||||||
|
// invalid type
|
||||||
|
types2.Typ[types2.Invalid], // only appears in packages with errors
|
||||||
|
|
||||||
|
// used internally by gc; never used by this package or in .a files
|
||||||
|
anyType{},
|
||||||
|
}
|
||||||
|
|
||||||
|
type anyType struct{}
|
||||||
|
|
||||||
|
func (t anyType) Underlying() types2.Type { return t }
|
||||||
|
func (t anyType) String() string { return "any" }
|
||||||
15
src/cmd/compile/internal/importer/testdata/a.go
vendored
Normal file
15
src/cmd/compile/internal/importer/testdata/a.go
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Input for TestIssue13566
|
||||||
|
|
||||||
|
package a
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
type A struct {
|
||||||
|
a *A
|
||||||
|
json json.RawMessage
|
||||||
|
}
|
||||||
12
src/cmd/compile/internal/importer/testdata/b.go
vendored
Normal file
12
src/cmd/compile/internal/importer/testdata/b.go
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Input for TestIssue13566
|
||||||
|
|
||||||
|
package b
|
||||||
|
|
||||||
|
import "./a"
|
||||||
|
|
||||||
|
type A a.A
|
||||||
89
src/cmd/compile/internal/importer/testdata/exports.go
vendored
Normal file
89
src/cmd/compile/internal/importer/testdata/exports.go
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file is used to generate an object file which
|
||||||
|
// serves as test file for gcimporter_test.go.
|
||||||
|
|
||||||
|
package exports
|
||||||
|
|
||||||
|
import "go/ast"
|
||||||
|
|
||||||
|
// Issue 3682: Correctly read dotted identifiers from export data.
|
||||||
|
const init1 = 0
|
||||||
|
|
||||||
|
func init() {}
|
||||||
|
|
||||||
|
const (
|
||||||
|
C0 int = 0
|
||||||
|
C1 = 3.14159265
|
||||||
|
C2 = 2.718281828i
|
||||||
|
C3 = -123.456e-789
|
||||||
|
C4 = +123.456e+789
|
||||||
|
C5 = 1234i
|
||||||
|
C6 = "foo\n"
|
||||||
|
C7 = `bar\n`
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
T1 int
|
||||||
|
T2 [10]int
|
||||||
|
T3 []int
|
||||||
|
T4 *int
|
||||||
|
T5 chan int
|
||||||
|
T6a chan<- int
|
||||||
|
T6b chan (<-chan int)
|
||||||
|
T6c chan<- (chan int)
|
||||||
|
T7 <-chan *ast.File
|
||||||
|
T8 struct{}
|
||||||
|
T9 struct {
|
||||||
|
a int
|
||||||
|
b, c float32
|
||||||
|
d []string `go:"tag"`
|
||||||
|
}
|
||||||
|
T10 struct {
|
||||||
|
T8
|
||||||
|
T9
|
||||||
|
_ *T10
|
||||||
|
}
|
||||||
|
T11 map[int]string
|
||||||
|
T12 interface{}
|
||||||
|
T13 interface {
|
||||||
|
m1()
|
||||||
|
m2(int) float32
|
||||||
|
}
|
||||||
|
T14 interface {
|
||||||
|
T12
|
||||||
|
T13
|
||||||
|
m3(x ...struct{}) []T9
|
||||||
|
}
|
||||||
|
T15 func()
|
||||||
|
T16 func(int)
|
||||||
|
T17 func(x int)
|
||||||
|
T18 func() float32
|
||||||
|
T19 func() (x float32)
|
||||||
|
T20 func(...interface{})
|
||||||
|
T21 struct{ next *T21 }
|
||||||
|
T22 struct{ link *T23 }
|
||||||
|
T23 struct{ link *T22 }
|
||||||
|
T24 *T24
|
||||||
|
T25 *T26
|
||||||
|
T26 *T27
|
||||||
|
T27 *T25
|
||||||
|
T28 func(T28) T28
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
V0 int
|
||||||
|
V1 = -991.0
|
||||||
|
V2 float32 = 1.2
|
||||||
|
)
|
||||||
|
|
||||||
|
func F1() {}
|
||||||
|
func F2(x int) {}
|
||||||
|
func F3() int { return 0 }
|
||||||
|
func F4() float32 { return 0 }
|
||||||
|
func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10)
|
||||||
|
|
||||||
|
func (p *T1) M1()
|
||||||
12
src/cmd/compile/internal/importer/testdata/issue15920.go
vendored
Normal file
12
src/cmd/compile/internal/importer/testdata/issue15920.go
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
// The underlying type of Error is the underlying type of error.
|
||||||
|
// Make sure we can import this again without problems.
|
||||||
|
type Error error
|
||||||
|
|
||||||
|
func F() Error { return nil }
|
||||||
10
src/cmd/compile/internal/importer/testdata/issue20046.go
vendored
Normal file
10
src/cmd/compile/internal/importer/testdata/issue20046.go
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
var V interface {
|
||||||
|
M()
|
||||||
|
}
|
||||||
18
src/cmd/compile/internal/importer/testdata/issue25301.go
vendored
Normal file
18
src/cmd/compile/internal/importer/testdata/issue25301.go
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package issue25301
|
||||||
|
|
||||||
|
type (
|
||||||
|
A = interface {
|
||||||
|
M()
|
||||||
|
}
|
||||||
|
T interface {
|
||||||
|
A
|
||||||
|
}
|
||||||
|
S struct{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (S) M() { println("m") }
|
||||||
14
src/cmd/compile/internal/importer/testdata/issue25596.go
vendored
Normal file
14
src/cmd/compile/internal/importer/testdata/issue25596.go
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package issue25596
|
||||||
|
|
||||||
|
type E interface {
|
||||||
|
M() T
|
||||||
|
}
|
||||||
|
|
||||||
|
type T interface {
|
||||||
|
E
|
||||||
|
}
|
||||||
14
src/cmd/compile/internal/importer/testdata/p.go
vendored
Normal file
14
src/cmd/compile/internal/importer/testdata/p.go
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Input for TestIssue15517
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
const C = 0
|
||||||
|
|
||||||
|
var V int
|
||||||
|
|
||||||
|
func F() {}
|
||||||
29
src/cmd/compile/internal/importer/testdata/versions/test.go
vendored
Normal file
29
src/cmd/compile/internal/importer/testdata/versions/test.go
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// To create a test case for a new export format version,
|
||||||
|
// build this package with the latest compiler and store
|
||||||
|
// the resulting .a file appropriately named in the versions
|
||||||
|
// directory. The VersionHandling test will pick it up.
|
||||||
|
//
|
||||||
|
// In the testdata/versions:
|
||||||
|
//
|
||||||
|
// go build -o test_go1.$X_$Y.a test.go
|
||||||
|
//
|
||||||
|
// with $X = Go version and $Y = export format version
|
||||||
|
// (add 'b' or 'i' to distinguish between binary and
|
||||||
|
// indexed format starting with 1.11 as long as both
|
||||||
|
// formats are supported).
|
||||||
|
//
|
||||||
|
// Make sure this source is extended such that it exercises
|
||||||
|
// whatever export format change has taken place.
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
// Any release before and including Go 1.7 didn't encode
|
||||||
|
// the package for a blank struct field.
|
||||||
|
type BlankField struct {
|
||||||
|
_ int
|
||||||
|
}
|
||||||
|
|
@ -354,6 +354,13 @@ func (v *hairyVisitor) doNode(n ir.Node) bool {
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case ir.OCLOSURE:
|
case ir.OCLOSURE:
|
||||||
|
// TODO(danscales,mdempsky): Get working with -G.
|
||||||
|
// Probably after #43818 is fixed.
|
||||||
|
if base.Flag.G > 0 {
|
||||||
|
v.reason = "inlining closures not yet working with -G"
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(danscales) - fix some bugs when budget is lowered below 15
|
// TODO(danscales) - fix some bugs when budget is lowered below 15
|
||||||
// Maybe make budget proportional to number of closure variables, e.g.:
|
// Maybe make budget proportional to number of closure variables, e.g.:
|
||||||
//v.budget -= int32(len(n.(*ir.ClosureExpr).Func.ClosureVars) * 3)
|
//v.budget -= int32(len(n.(*ir.ClosureExpr).Func.ClosureVars) * 3)
|
||||||
|
|
@ -1221,7 +1228,7 @@ func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
|
||||||
newrecv = newrecvs[0]
|
newrecv = newrecvs[0]
|
||||||
}
|
}
|
||||||
newt := types.NewSignature(oldt.Pkg(), newrecv,
|
newt := types.NewSignature(oldt.Pkg(), newrecv,
|
||||||
subst.fields(oldt.Params()), subst.fields(oldt.Results()))
|
nil, subst.fields(oldt.Params()), subst.fields(oldt.Results()))
|
||||||
|
|
||||||
newfn.Nname.SetType(newt)
|
newfn.Nname.SetType(newt)
|
||||||
newfn.Body = subst.list(oldfn.Body)
|
newfn.Body = subst.list(oldfn.Body)
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,13 @@ func _() {
|
||||||
_ = x[PAUTOHEAP-3]
|
_ = x[PAUTOHEAP-3]
|
||||||
_ = x[PPARAM-4]
|
_ = x[PPARAM-4]
|
||||||
_ = x[PPARAMOUT-5]
|
_ = x[PPARAMOUT-5]
|
||||||
_ = x[PFUNC-6]
|
_ = x[PTYPEPARAM-6]
|
||||||
|
_ = x[PFUNC-7]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _Class_name = "PxxxPEXTERNPAUTOPAUTOHEAPPPARAMPPARAMOUTPFUNC"
|
const _Class_name = "PxxxPEXTERNPAUTOPAUTOHEAPPPARAMPPARAMOUTPTYPEPARAMPFUNC"
|
||||||
|
|
||||||
var _Class_index = [...]uint8{0, 4, 11, 16, 25, 31, 40, 45}
|
var _Class_index = [...]uint8{0, 4, 11, 16, 25, 31, 40, 50, 55}
|
||||||
|
|
||||||
func (i Class) String() string {
|
func (i Class) String() string {
|
||||||
if i >= Class(len(_Class_index)-1) {
|
if i >= Class(len(_Class_index)-1) {
|
||||||
|
|
|
||||||
|
|
@ -670,6 +670,20 @@ func (n *UnaryExpr) SetOp(op Op) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An InstExpr is a generic function or type instantiation.
|
||||||
|
type InstExpr struct {
|
||||||
|
miniExpr
|
||||||
|
X Node
|
||||||
|
Targs []Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInstExpr(pos src.XPos, op Op, x Node, targs []Node) *InstExpr {
|
||||||
|
n := &InstExpr{X: x, Targs: targs}
|
||||||
|
n.pos = pos
|
||||||
|
n.op = op
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
func IsZero(n Node) bool {
|
func IsZero(n Node) bool {
|
||||||
switch n.Op() {
|
switch n.Op() {
|
||||||
case ONIL:
|
case ONIL:
|
||||||
|
|
|
||||||
|
|
@ -479,6 +479,7 @@ const (
|
||||||
PAUTOHEAP // local variables or parameters moved to heap
|
PAUTOHEAP // local variables or parameters moved to heap
|
||||||
PPARAM // input arguments
|
PPARAM // input arguments
|
||||||
PPARAMOUT // output results
|
PPARAMOUT // output results
|
||||||
|
PTYPEPARAM // type params
|
||||||
PFUNC // global functions
|
PFUNC // global functions
|
||||||
|
|
||||||
// Careful: Class is stored in three bits in Node.flags.
|
// Careful: Class is stored in three bits in Node.flags.
|
||||||
|
|
|
||||||
|
|
@ -278,6 +278,8 @@ const (
|
||||||
// OTYPESW: Left := Right.(type) (appears as .Left of OSWITCH)
|
// OTYPESW: Left := Right.(type) (appears as .Left of OSWITCH)
|
||||||
// Left is nil if there is no type-switch variable
|
// Left is nil if there is no type-switch variable
|
||||||
OTYPESW
|
OTYPESW
|
||||||
|
OFUNCINST // instantiation of a generic function
|
||||||
|
OTYPEINST // instantiation of a generic type
|
||||||
|
|
||||||
// types
|
// types
|
||||||
OTCHAN // chan int
|
OTCHAN // chan int
|
||||||
|
|
|
||||||
|
|
@ -669,6 +669,33 @@ func (n *InlinedCallExpr) editChildren(edit func(Node) Node) {
|
||||||
editNodes(n.ReturnVars, edit)
|
editNodes(n.ReturnVars, edit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *InstExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
|
||||||
|
func (n *InstExpr) copy() Node {
|
||||||
|
c := *n
|
||||||
|
c.init = copyNodes(c.init)
|
||||||
|
c.Targs = copyNodes(c.Targs)
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
func (n *InstExpr) doChildren(do func(Node) bool) bool {
|
||||||
|
if doNodes(n.init, do) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if n.X != nil && do(n.X) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if doNodes(n.Targs, do) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func (n *InstExpr) editChildren(edit func(Node) Node) {
|
||||||
|
editNodes(n.init, edit)
|
||||||
|
if n.X != nil {
|
||||||
|
n.X = edit(n.X).(Node)
|
||||||
|
}
|
||||||
|
editNodes(n.Targs, edit)
|
||||||
|
}
|
||||||
|
|
||||||
func (n *InterfaceType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
|
func (n *InterfaceType) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
|
||||||
func (n *InterfaceType) copy() Node {
|
func (n *InterfaceType) copy() Node {
|
||||||
c := *n
|
c := *n
|
||||||
|
|
|
||||||
|
|
@ -137,34 +137,36 @@ func _() {
|
||||||
_ = x[OSELECT-126]
|
_ = x[OSELECT-126]
|
||||||
_ = x[OSWITCH-127]
|
_ = x[OSWITCH-127]
|
||||||
_ = x[OTYPESW-128]
|
_ = x[OTYPESW-128]
|
||||||
_ = x[OTCHAN-129]
|
_ = x[OFUNCINST-129]
|
||||||
_ = x[OTMAP-130]
|
_ = x[OTYPEINST-130]
|
||||||
_ = x[OTSTRUCT-131]
|
_ = x[OTCHAN-131]
|
||||||
_ = x[OTINTER-132]
|
_ = x[OTMAP-132]
|
||||||
_ = x[OTFUNC-133]
|
_ = x[OTSTRUCT-133]
|
||||||
_ = x[OTARRAY-134]
|
_ = x[OTINTER-134]
|
||||||
_ = x[OTSLICE-135]
|
_ = x[OTFUNC-135]
|
||||||
_ = x[OINLCALL-136]
|
_ = x[OTARRAY-136]
|
||||||
_ = x[OEFACE-137]
|
_ = x[OTSLICE-137]
|
||||||
_ = x[OITAB-138]
|
_ = x[OINLCALL-138]
|
||||||
_ = x[OIDATA-139]
|
_ = x[OEFACE-139]
|
||||||
_ = x[OSPTR-140]
|
_ = x[OITAB-140]
|
||||||
_ = x[OCFUNC-141]
|
_ = x[OIDATA-141]
|
||||||
_ = x[OCHECKNIL-142]
|
_ = x[OSPTR-142]
|
||||||
_ = x[OVARDEF-143]
|
_ = x[OCFUNC-143]
|
||||||
_ = x[OVARKILL-144]
|
_ = x[OCHECKNIL-144]
|
||||||
_ = x[OVARLIVE-145]
|
_ = x[OVARDEF-145]
|
||||||
_ = x[ORESULT-146]
|
_ = x[OVARKILL-146]
|
||||||
_ = x[OINLMARK-147]
|
_ = x[OVARLIVE-147]
|
||||||
_ = x[OLINKSYMOFFSET-148]
|
_ = x[ORESULT-148]
|
||||||
_ = x[OTAILCALL-149]
|
_ = x[OINLMARK-149]
|
||||||
_ = x[OGETG-150]
|
_ = x[OLINKSYMOFFSET-150]
|
||||||
_ = x[OEND-151]
|
_ = x[OTAILCALL-151]
|
||||||
|
_ = x[OGETG-152]
|
||||||
|
_ = x[OEND-153]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _Op_name = "XXXNAMENONAMETYPEPACKLITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFMETHEXPRSTMTEXPRBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWTCHANTMAPTSTRUCTTINTERTFUNCTARRAYTSLICEINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKLINKSYMOFFSETTAILCALLGETGEND"
|
const _Op_name = "XXXNAMENONAMETYPEPACKLITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCALLPARTCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECVRUNESTRSELRECV2IOTAREALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFMETHEXPRSTMTEXPRBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTTYPEINSTTCHANTMAPTSTRUCTTINTERTFUNCTARRAYTSLICEINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKLINKSYMOFFSETTAILCALLGETGEND"
|
||||||
|
|
||||||
var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 37, 39, 42, 48, 52, 58, 64, 73, 85, 94, 103, 115, 124, 126, 129, 139, 146, 153, 160, 164, 168, 176, 184, 193, 201, 204, 209, 216, 223, 229, 238, 246, 254, 260, 264, 273, 280, 284, 287, 294, 302, 309, 315, 318, 324, 331, 339, 343, 350, 358, 360, 362, 364, 366, 368, 370, 375, 380, 388, 391, 400, 403, 407, 415, 422, 431, 444, 447, 450, 453, 456, 459, 462, 468, 471, 474, 480, 484, 487, 491, 496, 501, 507, 512, 516, 521, 529, 537, 543, 552, 563, 570, 574, 581, 589, 593, 597, 601, 608, 615, 623, 629, 637, 645, 650, 655, 659, 667, 672, 676, 679, 687, 691, 693, 698, 700, 705, 711, 717, 723, 729, 734, 738, 745, 751, 756, 762, 768, 775, 780, 784, 789, 793, 798, 806, 812, 819, 826, 832, 839, 852, 860, 864, 867}
|
var _Op_index = [...]uint16{0, 3, 7, 13, 17, 21, 28, 31, 34, 37, 39, 42, 48, 52, 58, 64, 73, 85, 94, 103, 115, 124, 126, 129, 139, 146, 153, 160, 164, 168, 176, 184, 193, 201, 204, 209, 216, 223, 229, 238, 246, 254, 260, 264, 273, 280, 284, 287, 294, 302, 309, 315, 318, 324, 331, 339, 343, 350, 358, 360, 362, 364, 366, 368, 370, 375, 380, 388, 391, 400, 403, 407, 415, 422, 431, 444, 447, 450, 453, 456, 459, 462, 468, 471, 474, 480, 484, 487, 491, 496, 501, 507, 512, 516, 521, 529, 537, 543, 552, 563, 570, 574, 581, 589, 593, 597, 601, 608, 615, 623, 629, 637, 645, 650, 655, 659, 667, 672, 676, 679, 687, 691, 693, 698, 700, 705, 711, 717, 723, 729, 737, 745, 750, 754, 761, 767, 772, 778, 784, 791, 796, 800, 805, 809, 814, 822, 828, 835, 842, 848, 855, 868, 876, 880, 883}
|
||||||
|
|
||||||
func (i Op) String() string {
|
func (i Op) String() string {
|
||||||
if i >= Op(len(_Op_index)-1) {
|
if i >= Op(len(_Op_index)-1) {
|
||||||
|
|
|
||||||
|
|
@ -32,4 +32,7 @@ type Package struct {
|
||||||
|
|
||||||
// Exported (or re-exported) symbols.
|
// Exported (or re-exported) symbols.
|
||||||
Exports []*Name
|
Exports []*Name
|
||||||
|
|
||||||
|
// Map from function names of stencils to already-created stencils.
|
||||||
|
Stencils map[*types.Sym]*Func
|
||||||
}
|
}
|
||||||
|
|
|
||||||
241
src/cmd/compile/internal/noder/decl.go
Normal file
241
src/cmd/compile/internal/noder/decl.go
Normal file
|
|
@ -0,0 +1,241 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package noder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/constant"
|
||||||
|
|
||||||
|
"cmd/compile/internal/base"
|
||||||
|
"cmd/compile/internal/ir"
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"cmd/compile/internal/typecheck"
|
||||||
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(mdempsky): Skip blank declarations? Probably only safe
|
||||||
|
// for declarations without pragmas.
|
||||||
|
|
||||||
|
func (g *irgen) decls(decls []syntax.Decl) []ir.Node {
|
||||||
|
var res ir.Nodes
|
||||||
|
for _, decl := range decls {
|
||||||
|
switch decl := decl.(type) {
|
||||||
|
case *syntax.ConstDecl:
|
||||||
|
g.constDecl(&res, decl)
|
||||||
|
case *syntax.FuncDecl:
|
||||||
|
g.funcDecl(&res, decl)
|
||||||
|
case *syntax.TypeDecl:
|
||||||
|
if ir.CurFunc == nil {
|
||||||
|
continue // already handled in irgen.generate
|
||||||
|
}
|
||||||
|
g.typeDecl(&res, decl)
|
||||||
|
case *syntax.VarDecl:
|
||||||
|
g.varDecl(&res, decl)
|
||||||
|
default:
|
||||||
|
g.unhandled("declaration", decl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) importDecl(p *noder, decl *syntax.ImportDecl) {
|
||||||
|
// TODO(mdempsky): Merge with gcimports so we don't have to import
|
||||||
|
// packages twice.
|
||||||
|
|
||||||
|
g.pragmaFlags(decl.Pragma, 0)
|
||||||
|
|
||||||
|
ipkg := importfile(decl)
|
||||||
|
if ipkg == ir.Pkgs.Unsafe {
|
||||||
|
p.importedUnsafe = true
|
||||||
|
}
|
||||||
|
if ipkg.Path == "embed" {
|
||||||
|
p.importedEmbed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) constDecl(out *ir.Nodes, decl *syntax.ConstDecl) {
|
||||||
|
g.pragmaFlags(decl.Pragma, 0)
|
||||||
|
|
||||||
|
for _, name := range decl.NameList {
|
||||||
|
name, obj := g.def(name)
|
||||||
|
|
||||||
|
// For untyped numeric constants, make sure the value
|
||||||
|
// representation matches what the rest of the
|
||||||
|
// compiler (really just iexport) expects.
|
||||||
|
// TODO(mdempsky): Revisit after #43891 is resolved.
|
||||||
|
val := obj.(*types2.Const).Val()
|
||||||
|
switch name.Type() {
|
||||||
|
case types.UntypedInt, types.UntypedRune:
|
||||||
|
val = constant.ToInt(val)
|
||||||
|
case types.UntypedFloat:
|
||||||
|
val = constant.ToFloat(val)
|
||||||
|
case types.UntypedComplex:
|
||||||
|
val = constant.ToComplex(val)
|
||||||
|
}
|
||||||
|
name.SetVal(val)
|
||||||
|
|
||||||
|
out.Append(ir.NewDecl(g.pos(decl), ir.ODCLCONST, name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) funcDecl(out *ir.Nodes, decl *syntax.FuncDecl) {
|
||||||
|
fn := ir.NewFunc(g.pos(decl))
|
||||||
|
fn.Nname, _ = g.def(decl.Name)
|
||||||
|
fn.Nname.Func = fn
|
||||||
|
fn.Nname.Defn = fn
|
||||||
|
|
||||||
|
fn.Pragma = g.pragmaFlags(decl.Pragma, funcPragmas)
|
||||||
|
if fn.Pragma&ir.Systemstack != 0 && fn.Pragma&ir.Nosplit != 0 {
|
||||||
|
base.ErrorfAt(fn.Pos(), "go:nosplit and go:systemstack cannot be combined")
|
||||||
|
}
|
||||||
|
|
||||||
|
if decl.Name.Value == "init" && decl.Recv == nil {
|
||||||
|
g.target.Inits = append(g.target.Inits, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.funcBody(fn, decl.Recv, decl.Type, decl.Body)
|
||||||
|
|
||||||
|
out.Append(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) typeDecl(out *ir.Nodes, decl *syntax.TypeDecl) {
|
||||||
|
if decl.Alias {
|
||||||
|
name, _ := g.def(decl.Name)
|
||||||
|
g.pragmaFlags(decl.Pragma, 0)
|
||||||
|
|
||||||
|
// TODO(mdempsky): This matches how typecheckdef marks aliases for
|
||||||
|
// export, but this won't generalize to exporting function-scoped
|
||||||
|
// type aliases. We should maybe just use n.Alias() instead.
|
||||||
|
if ir.CurFunc == nil {
|
||||||
|
name.Sym().Def = ir.TypeNode(name.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent size calculations until we set the underlying type.
|
||||||
|
types.DeferCheckSize()
|
||||||
|
|
||||||
|
name, obj := g.def(decl.Name)
|
||||||
|
ntyp, otyp := name.Type(), obj.Type()
|
||||||
|
if ir.CurFunc != nil {
|
||||||
|
typecheck.TypeGen++
|
||||||
|
ntyp.Vargen = typecheck.TypeGen
|
||||||
|
}
|
||||||
|
|
||||||
|
pragmas := g.pragmaFlags(decl.Pragma, typePragmas)
|
||||||
|
name.SetPragma(pragmas) // TODO(mdempsky): Is this still needed?
|
||||||
|
|
||||||
|
if pragmas&ir.NotInHeap != 0 {
|
||||||
|
ntyp.SetNotInHeap(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to use g.typeExpr(decl.Type) here to ensure that for
|
||||||
|
// chained, defined-type declarations like
|
||||||
|
//
|
||||||
|
// type T U
|
||||||
|
//
|
||||||
|
// //go:notinheap
|
||||||
|
// type U struct { … }
|
||||||
|
//
|
||||||
|
// that we mark both T and U as NotInHeap. If we instead used just
|
||||||
|
// g.typ(otyp.Underlying()), then we'd instead set T's underlying
|
||||||
|
// type directly to the struct type (which is not marked NotInHeap)
|
||||||
|
// and fail to mark T as NotInHeap.
|
||||||
|
//
|
||||||
|
// Also, we rely here on Type.SetUnderlying allowing passing a
|
||||||
|
// defined type and handling forward references like from T to U
|
||||||
|
// above. Contrast with go/types's Named.SetUnderlying, which
|
||||||
|
// disallows this.
|
||||||
|
//
|
||||||
|
// [mdempsky: Subtleties like these are why I always vehemently
|
||||||
|
// object to new type pragmas.]
|
||||||
|
ntyp.SetUnderlying(g.typeExpr(decl.Type))
|
||||||
|
types.ResumeCheckSize()
|
||||||
|
|
||||||
|
if otyp, ok := otyp.(*types2.Named); ok && otyp.NumMethods() != 0 {
|
||||||
|
methods := make([]*types.Field, otyp.NumMethods())
|
||||||
|
for i := range methods {
|
||||||
|
m := otyp.Method(i)
|
||||||
|
meth := g.obj(m)
|
||||||
|
methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type())
|
||||||
|
methods[i].Nname = meth
|
||||||
|
}
|
||||||
|
ntyp.Methods().Set(methods)
|
||||||
|
}
|
||||||
|
|
||||||
|
out.Append(ir.NewDecl(g.pos(decl), ir.ODCLTYPE, name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) varDecl(out *ir.Nodes, decl *syntax.VarDecl) {
|
||||||
|
pos := g.pos(decl)
|
||||||
|
names := make([]*ir.Name, len(decl.NameList))
|
||||||
|
for i, name := range decl.NameList {
|
||||||
|
names[i], _ = g.def(name)
|
||||||
|
}
|
||||||
|
values := g.exprList(decl.Values)
|
||||||
|
|
||||||
|
if decl.Pragma != nil {
|
||||||
|
pragma := decl.Pragma.(*pragmas)
|
||||||
|
// TODO(mdempsky): Plumb noder.importedEmbed through to here.
|
||||||
|
varEmbed(g.makeXPos, names[0], decl, pragma, true)
|
||||||
|
g.reportUnused(pragma)
|
||||||
|
}
|
||||||
|
|
||||||
|
var as2 *ir.AssignListStmt
|
||||||
|
if len(values) != 0 && len(names) != len(values) {
|
||||||
|
as2 = ir.NewAssignListStmt(pos, ir.OAS2, make([]ir.Node, len(names)), values)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, name := range names {
|
||||||
|
if ir.CurFunc != nil {
|
||||||
|
out.Append(ir.NewDecl(pos, ir.ODCL, name))
|
||||||
|
}
|
||||||
|
if as2 != nil {
|
||||||
|
as2.Lhs[i] = name
|
||||||
|
name.Defn = as2
|
||||||
|
} else {
|
||||||
|
as := ir.NewAssignStmt(pos, name, nil)
|
||||||
|
if len(values) != 0 {
|
||||||
|
as.Y = values[i]
|
||||||
|
name.Defn = as
|
||||||
|
} else if ir.CurFunc == nil {
|
||||||
|
name.Defn = as
|
||||||
|
}
|
||||||
|
out.Append(typecheck.Stmt(as))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if as2 != nil {
|
||||||
|
out.Append(typecheck.Stmt(as2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pragmaFlags returns any specified pragma flags included in allowed,
|
||||||
|
// and reports errors about any other, unexpected pragmas.
|
||||||
|
func (g *irgen) pragmaFlags(pragma syntax.Pragma, allowed ir.PragmaFlag) ir.PragmaFlag {
|
||||||
|
if pragma == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
p := pragma.(*pragmas)
|
||||||
|
present := p.Flag & allowed
|
||||||
|
p.Flag &^= allowed
|
||||||
|
g.reportUnused(p)
|
||||||
|
return present
|
||||||
|
}
|
||||||
|
|
||||||
|
// reportUnused reports errors about any unused pragmas.
|
||||||
|
func (g *irgen) reportUnused(pragma *pragmas) {
|
||||||
|
for _, pos := range pragma.Pos {
|
||||||
|
if pos.Flag&pragma.Flag != 0 {
|
||||||
|
base.ErrorfAt(g.makeXPos(pos.Pos), "misplaced compiler directive")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(pragma.Embeds) > 0 {
|
||||||
|
for _, e := range pragma.Embeds {
|
||||||
|
base.ErrorfAt(g.makeXPos(e.Pos), "misplaced go:embed directive")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
365
src/cmd/compile/internal/noder/expr.go
Normal file
365
src/cmd/compile/internal/noder/expr.go
Normal file
|
|
@ -0,0 +1,365 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package noder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/compile/internal/base"
|
||||||
|
"cmd/compile/internal/ir"
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"cmd/compile/internal/typecheck"
|
||||||
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
|
"cmd/internal/src"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (g *irgen) expr(expr syntax.Expr) ir.Node {
|
||||||
|
// TODO(mdempsky): Change callers to not call on nil?
|
||||||
|
if expr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if expr, ok := expr.(*syntax.Name); ok && expr.Value == "_" {
|
||||||
|
return ir.BlankNode
|
||||||
|
}
|
||||||
|
|
||||||
|
tv, ok := g.info.Types[expr]
|
||||||
|
if !ok {
|
||||||
|
base.FatalfAt(g.pos(expr), "missing type for %v (%T)", expr, expr)
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case tv.IsBuiltin():
|
||||||
|
// TODO(mdempsky): Handle in CallExpr?
|
||||||
|
return g.use(expr.(*syntax.Name))
|
||||||
|
case tv.IsType():
|
||||||
|
return ir.TypeNode(g.typ(tv.Type))
|
||||||
|
case tv.IsValue(), tv.IsVoid():
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
base.FatalfAt(g.pos(expr), "unrecognized type-checker result")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The gc backend expects all expressions to have a concrete type, and
|
||||||
|
// types2 mostly satisfies this expectation already. But there are a few
|
||||||
|
// cases where the Go spec doesn't require converting to concrete type,
|
||||||
|
// and so types2 leaves them untyped. So we need to fix those up here.
|
||||||
|
typ := tv.Type
|
||||||
|
if basic, ok := typ.(*types2.Basic); ok && basic.Info()&types2.IsUntyped != 0 {
|
||||||
|
switch basic.Kind() {
|
||||||
|
case types2.UntypedNil:
|
||||||
|
// ok; can appear in type switch case clauses
|
||||||
|
// TODO(mdempsky): Handle as part of type switches instead?
|
||||||
|
case types2.UntypedBool:
|
||||||
|
typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition
|
||||||
|
case types2.UntypedString:
|
||||||
|
typ = types2.Typ[types2.String] // argument to "append" or "copy" calls
|
||||||
|
default:
|
||||||
|
base.FatalfAt(g.pos(expr), "unexpected untyped type: %v", basic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constant expression.
|
||||||
|
if tv.Value != nil {
|
||||||
|
return Const(g.pos(expr), g.typ(typ), tv.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
n := g.expr0(typ, expr)
|
||||||
|
if n.Typecheck() != 1 {
|
||||||
|
base.FatalfAt(g.pos(expr), "missed typecheck: %+v", n)
|
||||||
|
}
|
||||||
|
if !g.match(n.Type(), typ, tv.HasOk()) {
|
||||||
|
base.FatalfAt(g.pos(expr), "expected %L to have type %v", n, typ)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node {
|
||||||
|
pos := g.pos(expr)
|
||||||
|
|
||||||
|
switch expr := expr.(type) {
|
||||||
|
case *syntax.Name:
|
||||||
|
if _, isNil := g.info.Uses[expr].(*types2.Nil); isNil {
|
||||||
|
return Nil(pos, g.typ(typ))
|
||||||
|
}
|
||||||
|
// TODO(mdempsky): Remove dependency on typecheck.Expr.
|
||||||
|
return typecheck.Expr(g.use(expr))
|
||||||
|
|
||||||
|
case *syntax.CompositeLit:
|
||||||
|
return g.compLit(typ, expr)
|
||||||
|
|
||||||
|
case *syntax.FuncLit:
|
||||||
|
return g.funcLit(typ, expr)
|
||||||
|
|
||||||
|
case *syntax.AssertExpr:
|
||||||
|
return Assert(pos, g.expr(expr.X), g.typeExpr(expr.Type))
|
||||||
|
|
||||||
|
case *syntax.CallExpr:
|
||||||
|
fun := g.expr(expr.Fun)
|
||||||
|
if inferred, ok := g.info.Inferred[expr]; ok && len(inferred.Targs) > 0 {
|
||||||
|
targs := make([]ir.Node, len(inferred.Targs))
|
||||||
|
for i, targ := range inferred.Targs {
|
||||||
|
targs[i] = ir.TypeNode(g.typ(targ))
|
||||||
|
}
|
||||||
|
if fun.Op() == ir.OFUNCINST {
|
||||||
|
// Replace explicit type args with the full list that
|
||||||
|
// includes the additional inferred type args
|
||||||
|
fun.(*ir.InstExpr).Targs = targs
|
||||||
|
} else {
|
||||||
|
// Create a function instantiation here, given
|
||||||
|
// there are only inferred type args (e.g.
|
||||||
|
// min(5,6), where min is a generic function)
|
||||||
|
inst := ir.NewInstExpr(pos, ir.OFUNCINST, fun, targs)
|
||||||
|
typed(fun.Type(), inst)
|
||||||
|
fun = inst
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return Call(pos, g.typ(typ), fun, g.exprs(expr.ArgList), expr.HasDots)
|
||||||
|
|
||||||
|
case *syntax.IndexExpr:
|
||||||
|
var targs []ir.Node
|
||||||
|
if _, ok := expr.Index.(*syntax.ListExpr); ok {
|
||||||
|
targs = g.exprList(expr.Index)
|
||||||
|
} else {
|
||||||
|
index := g.expr(expr.Index)
|
||||||
|
if index.Op() != ir.OTYPE {
|
||||||
|
// This is just a normal index expression
|
||||||
|
return Index(pos, g.expr(expr.X), index)
|
||||||
|
}
|
||||||
|
// This is generic function instantiation with a single type
|
||||||
|
targs = []ir.Node{index}
|
||||||
|
}
|
||||||
|
// This is a generic function instantiation (e.g. min[int])
|
||||||
|
x := g.expr(expr.X)
|
||||||
|
if x.Op() != ir.ONAME || x.Type().Kind() != types.TFUNC {
|
||||||
|
panic("Incorrect argument for generic func instantiation")
|
||||||
|
}
|
||||||
|
// This could also be an OTYPEINST once we can handle those examples.
|
||||||
|
n := ir.NewInstExpr(pos, ir.OFUNCINST, x, targs)
|
||||||
|
typed(g.typ(typ), n)
|
||||||
|
return n
|
||||||
|
|
||||||
|
case *syntax.ParenExpr:
|
||||||
|
return g.expr(expr.X) // skip parens; unneeded after parse+typecheck
|
||||||
|
|
||||||
|
case *syntax.SelectorExpr:
|
||||||
|
// Qualified identifier.
|
||||||
|
if name, ok := expr.X.(*syntax.Name); ok {
|
||||||
|
if _, ok := g.info.Uses[name].(*types2.PkgName); ok {
|
||||||
|
// TODO(mdempsky): Remove dependency on typecheck.Expr.
|
||||||
|
return typecheck.Expr(g.use(expr.Sel))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return g.selectorExpr(pos, typ, expr)
|
||||||
|
|
||||||
|
case *syntax.SliceExpr:
|
||||||
|
return Slice(pos, g.expr(expr.X), g.expr(expr.Index[0]), g.expr(expr.Index[1]), g.expr(expr.Index[2]))
|
||||||
|
|
||||||
|
case *syntax.Operation:
|
||||||
|
if expr.Y == nil {
|
||||||
|
return Unary(pos, g.op(expr.Op, unOps[:]), g.expr(expr.X))
|
||||||
|
}
|
||||||
|
switch op := g.op(expr.Op, binOps[:]); op {
|
||||||
|
case ir.OEQ, ir.ONE, ir.OLT, ir.OLE, ir.OGT, ir.OGE:
|
||||||
|
return Compare(pos, g.typ(typ), op, g.expr(expr.X), g.expr(expr.Y))
|
||||||
|
default:
|
||||||
|
return Binary(pos, op, g.expr(expr.X), g.expr(expr.Y))
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
g.unhandled("expression", expr)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// selectorExpr resolves the choice of ODOT, ODOTPTR, OCALLPART (eventually
|
||||||
|
// ODOTMETH & ODOTINTER), and OMETHEXPR and deals with embedded fields here rather
|
||||||
|
// than in typecheck.go.
|
||||||
|
func (g *irgen) selectorExpr(pos src.XPos, typ types2.Type, expr *syntax.SelectorExpr) ir.Node {
|
||||||
|
x := g.expr(expr.X)
|
||||||
|
if x.Type().Kind() == types.TTYPEPARAM {
|
||||||
|
// Leave a method call on a type param as an OXDOT, since it can
|
||||||
|
// only be fully transformed once it has an instantiated type.
|
||||||
|
n := ir.NewSelectorExpr(pos, ir.OXDOT, x, typecheck.Lookup(expr.Sel.Value))
|
||||||
|
typed(g.typ(typ), n)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
selinfo := g.info.Selections[expr]
|
||||||
|
// Everything up to the last selection is an implicit embedded field access,
|
||||||
|
// and the last selection is determined by selinfo.Kind().
|
||||||
|
index := selinfo.Index()
|
||||||
|
embeds, last := index[:len(index)-1], index[len(index)-1]
|
||||||
|
|
||||||
|
origx := x
|
||||||
|
for _, ix := range embeds {
|
||||||
|
x = Implicit(DotField(pos, x, ix))
|
||||||
|
}
|
||||||
|
|
||||||
|
kind := selinfo.Kind()
|
||||||
|
if kind == types2.FieldVal {
|
||||||
|
return DotField(pos, x, last)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(danscales,mdempsky): Interface method sets are not sorted the
|
||||||
|
// same between types and types2. In particular, using "last" here
|
||||||
|
// without conversion will likely fail if an interface contains
|
||||||
|
// unexported methods from two different packages (due to cross-package
|
||||||
|
// interface embedding).
|
||||||
|
|
||||||
|
var n ir.Node
|
||||||
|
method2 := selinfo.Obj().(*types2.Func)
|
||||||
|
|
||||||
|
if kind == types2.MethodExpr {
|
||||||
|
// OMETHEXPR is unusual in using directly the node and type of the
|
||||||
|
// original OTYPE node (origx) before passing through embedded
|
||||||
|
// fields, even though the method is selected from the type
|
||||||
|
// (x.Type()) reached after following the embedded fields. We will
|
||||||
|
// actually drop any ODOT nodes we created due to the embedded
|
||||||
|
// fields.
|
||||||
|
n = MethodExpr(pos, origx, x.Type(), last)
|
||||||
|
} else {
|
||||||
|
// Add implicit addr/deref for method values, if needed.
|
||||||
|
if x.Type().IsInterface() {
|
||||||
|
n = DotMethod(pos, x, last)
|
||||||
|
} else {
|
||||||
|
recvType2 := method2.Type().(*types2.Signature).Recv().Type()
|
||||||
|
_, wantPtr := recvType2.(*types2.Pointer)
|
||||||
|
havePtr := x.Type().IsPtr()
|
||||||
|
|
||||||
|
if havePtr != wantPtr {
|
||||||
|
if havePtr {
|
||||||
|
x = Implicit(Deref(pos, x))
|
||||||
|
} else {
|
||||||
|
x = Implicit(Addr(pos, x))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recvType2Base := recvType2
|
||||||
|
if wantPtr {
|
||||||
|
recvType2Base = types2.AsPointer(recvType2).Elem()
|
||||||
|
}
|
||||||
|
if len(types2.AsNamed(recvType2Base).TParams()) > 0 {
|
||||||
|
// recvType2 is the original generic type that is
|
||||||
|
// instantiated for this method call.
|
||||||
|
// selinfo.Recv() is the instantiated type
|
||||||
|
recvType2 = recvType2Base
|
||||||
|
// method is the generic method associated with the gen type
|
||||||
|
method := g.obj(types2.AsNamed(recvType2).Method(last))
|
||||||
|
n = ir.NewSelectorExpr(pos, ir.OCALLPART, x, method.Sym())
|
||||||
|
n.(*ir.SelectorExpr).Selection = types.NewField(pos, method.Sym(), method.Type())
|
||||||
|
n.(*ir.SelectorExpr).Selection.Nname = method
|
||||||
|
typed(method.Type(), n)
|
||||||
|
|
||||||
|
// selinfo.Targs() are the types used to
|
||||||
|
// instantiate the type of receiver
|
||||||
|
targs2 := selinfo.TArgs()
|
||||||
|
targs := make([]ir.Node, len(targs2))
|
||||||
|
for i, targ2 := range targs2 {
|
||||||
|
targs[i] = ir.TypeNode(g.typ(targ2))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create function instantiation with the type
|
||||||
|
// args for the receiver type for the method call.
|
||||||
|
n = ir.NewInstExpr(pos, ir.OFUNCINST, n, targs)
|
||||||
|
typed(g.typ(typ), n)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
if !g.match(x.Type(), recvType2, false) {
|
||||||
|
base.FatalfAt(pos, "expected %L to have type %v", x, recvType2)
|
||||||
|
} else {
|
||||||
|
n = DotMethod(pos, x, last)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if have, want := n.Sym(), g.selector(method2); have != want {
|
||||||
|
base.FatalfAt(pos, "bad Sym: have %v, want %v", have, want)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) exprList(expr syntax.Expr) []ir.Node {
|
||||||
|
switch expr := expr.(type) {
|
||||||
|
case nil:
|
||||||
|
return nil
|
||||||
|
case *syntax.ListExpr:
|
||||||
|
return g.exprs(expr.ElemList)
|
||||||
|
default:
|
||||||
|
return []ir.Node{g.expr(expr)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) exprs(exprs []syntax.Expr) []ir.Node {
|
||||||
|
nodes := make([]ir.Node, len(exprs))
|
||||||
|
for i, expr := range exprs {
|
||||||
|
nodes[i] = g.expr(expr)
|
||||||
|
}
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) compLit(typ types2.Type, lit *syntax.CompositeLit) ir.Node {
|
||||||
|
if ptr, ok := typ.Underlying().(*types2.Pointer); ok {
|
||||||
|
n := ir.NewAddrExpr(g.pos(lit), g.compLit(ptr.Elem(), lit))
|
||||||
|
n.SetOp(ir.OPTRLIT)
|
||||||
|
return typed(g.typ(typ), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, isStruct := typ.Underlying().(*types2.Struct)
|
||||||
|
|
||||||
|
exprs := make([]ir.Node, len(lit.ElemList))
|
||||||
|
for i, elem := range lit.ElemList {
|
||||||
|
switch elem := elem.(type) {
|
||||||
|
case *syntax.KeyValueExpr:
|
||||||
|
if isStruct {
|
||||||
|
exprs[i] = ir.NewStructKeyExpr(g.pos(elem), g.name(elem.Key.(*syntax.Name)), g.expr(elem.Value))
|
||||||
|
} else {
|
||||||
|
exprs[i] = ir.NewKeyExpr(g.pos(elem), g.expr(elem.Key), g.expr(elem.Value))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
exprs[i] = g.expr(elem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(mdempsky): Remove dependency on typecheck.Expr.
|
||||||
|
return typecheck.Expr(ir.NewCompLitExpr(g.pos(lit), ir.OCOMPLIT, ir.TypeNode(g.typ(typ)), exprs))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) funcLit(typ types2.Type, expr *syntax.FuncLit) ir.Node {
|
||||||
|
fn := ir.NewFunc(g.pos(expr))
|
||||||
|
fn.SetIsHiddenClosure(ir.CurFunc != nil)
|
||||||
|
|
||||||
|
fn.Nname = ir.NewNameAt(g.pos(expr), typecheck.ClosureName(ir.CurFunc))
|
||||||
|
ir.MarkFunc(fn.Nname)
|
||||||
|
fn.Nname.SetType(g.typ(typ))
|
||||||
|
fn.Nname.Func = fn
|
||||||
|
fn.Nname.Defn = fn
|
||||||
|
|
||||||
|
fn.OClosure = ir.NewClosureExpr(g.pos(expr), fn)
|
||||||
|
fn.OClosure.SetType(fn.Nname.Type())
|
||||||
|
fn.OClosure.SetTypecheck(1)
|
||||||
|
|
||||||
|
g.funcBody(fn, nil, expr.Type, expr.Body)
|
||||||
|
|
||||||
|
ir.FinishCaptureNames(fn.Pos(), ir.CurFunc, fn)
|
||||||
|
|
||||||
|
// TODO(mdempsky): ir.CaptureName should probably handle
|
||||||
|
// copying these fields from the canonical variable.
|
||||||
|
for _, cv := range fn.ClosureVars {
|
||||||
|
cv.SetType(cv.Canonical().Type())
|
||||||
|
cv.SetTypecheck(1)
|
||||||
|
cv.SetWalkdef(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.target.Decls = append(g.target.Decls, fn)
|
||||||
|
|
||||||
|
return fn.OClosure
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) typeExpr(typ syntax.Expr) *types.Type {
|
||||||
|
n := g.expr(typ)
|
||||||
|
if n.Op() != ir.OTYPE {
|
||||||
|
base.FatalfAt(g.pos(typ), "expected type: %L", n)
|
||||||
|
}
|
||||||
|
return n.Type()
|
||||||
|
}
|
||||||
74
src/cmd/compile/internal/noder/func.go
Normal file
74
src/cmd/compile/internal/noder/func.go
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package noder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/compile/internal/base"
|
||||||
|
"cmd/compile/internal/ir"
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"cmd/compile/internal/typecheck"
|
||||||
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/internal/src"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (g *irgen) funcBody(fn *ir.Func, recv *syntax.Field, sig *syntax.FuncType, block *syntax.BlockStmt) {
|
||||||
|
typecheck.Func(fn)
|
||||||
|
|
||||||
|
// TODO(mdempsky): Remove uses of ir.CurFunc and
|
||||||
|
// typecheck.DeclContext after we stop relying on typecheck
|
||||||
|
// for desugaring.
|
||||||
|
outerfn, outerctxt := ir.CurFunc, typecheck.DeclContext
|
||||||
|
ir.CurFunc = fn
|
||||||
|
|
||||||
|
typ := fn.Type()
|
||||||
|
if param := typ.Recv(); param != nil {
|
||||||
|
g.defParam(param, recv, ir.PPARAM)
|
||||||
|
}
|
||||||
|
for i, param := range typ.Params().FieldSlice() {
|
||||||
|
g.defParam(param, sig.ParamList[i], ir.PPARAM)
|
||||||
|
}
|
||||||
|
for i, result := range typ.Results().FieldSlice() {
|
||||||
|
g.defParam(result, sig.ResultList[i], ir.PPARAMOUT)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We may have type-checked a call to this function already and
|
||||||
|
// calculated its size, including parameter offsets. Now that we've
|
||||||
|
// created the parameter Names, force a recalculation to ensure
|
||||||
|
// their offsets are correct.
|
||||||
|
typ.Align = 0
|
||||||
|
types.CalcSize(typ)
|
||||||
|
|
||||||
|
if block != nil {
|
||||||
|
typecheck.DeclContext = ir.PAUTO
|
||||||
|
|
||||||
|
fn.Body = g.stmts(block.List)
|
||||||
|
if fn.Body == nil {
|
||||||
|
fn.Body = []ir.Node{ir.NewBlockStmt(src.NoXPos, nil)}
|
||||||
|
}
|
||||||
|
fn.Endlineno = g.makeXPos(block.Rbrace)
|
||||||
|
|
||||||
|
if base.Flag.Dwarf {
|
||||||
|
g.recordScopes(fn, sig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ir.CurFunc, typecheck.DeclContext = outerfn, outerctxt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) defParam(param *types.Field, decl *syntax.Field, class ir.Class) {
|
||||||
|
typecheck.DeclContext = class
|
||||||
|
|
||||||
|
var name *ir.Name
|
||||||
|
if decl.Name != nil {
|
||||||
|
name, _ = g.def(decl.Name)
|
||||||
|
} else if class == ir.PPARAMOUT {
|
||||||
|
name = g.obj(g.info.Implicits[decl])
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != nil {
|
||||||
|
param.Nname = name
|
||||||
|
param.Sym = name.Sym() // in case it was renamed
|
||||||
|
}
|
||||||
|
}
|
||||||
277
src/cmd/compile/internal/noder/helpers.go
Normal file
277
src/cmd/compile/internal/noder/helpers.go
Normal file
|
|
@ -0,0 +1,277 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package noder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/constant"
|
||||||
|
|
||||||
|
"cmd/compile/internal/base"
|
||||||
|
"cmd/compile/internal/ir"
|
||||||
|
"cmd/compile/internal/typecheck"
|
||||||
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/internal/src"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Helpers for constructing typed IR nodes.
|
||||||
|
//
|
||||||
|
// TODO(mdempsky): Move into their own package so they can be easily
|
||||||
|
// reused by iimport and frontend optimizations.
|
||||||
|
//
|
||||||
|
// TODO(mdempsky): Update to consistently return already typechecked
|
||||||
|
// results, rather than leaving the caller responsible for using
|
||||||
|
// typecheck.Expr or typecheck.Stmt.
|
||||||
|
|
||||||
|
type ImplicitNode interface {
|
||||||
|
ir.Node
|
||||||
|
SetImplicit(x bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implicit returns n after marking it as Implicit.
|
||||||
|
func Implicit(n ImplicitNode) ImplicitNode {
|
||||||
|
n.SetImplicit(true)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// typed returns n after setting its type to typ.
|
||||||
|
func typed(typ *types.Type, n ir.Node) ir.Node {
|
||||||
|
n.SetType(typ)
|
||||||
|
n.SetTypecheck(1)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values
|
||||||
|
|
||||||
|
func Const(pos src.XPos, typ *types.Type, val constant.Value) ir.Node {
|
||||||
|
return typed(typ, ir.NewBasicLit(pos, val))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Nil(pos src.XPos, typ *types.Type) ir.Node {
|
||||||
|
return typed(typ, ir.NewNilExpr(pos))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expressions
|
||||||
|
|
||||||
|
func Addr(pos src.XPos, x ir.Node) *ir.AddrExpr {
|
||||||
|
// TODO(mdempsky): Avoid typecheck.Expr. Probably just need to set OPTRLIT when appropriate.
|
||||||
|
n := typecheck.Expr(typecheck.NodAddrAt(pos, x)).(*ir.AddrExpr)
|
||||||
|
typed(types.NewPtr(x.Type()), n)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func Assert(pos src.XPos, x ir.Node, typ *types.Type) ir.Node {
|
||||||
|
return typed(typ, ir.NewTypeAssertExpr(pos, x, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Binary(pos src.XPos, op ir.Op, x, y ir.Node) ir.Node {
|
||||||
|
switch op {
|
||||||
|
case ir.OANDAND, ir.OOROR:
|
||||||
|
return typed(x.Type(), ir.NewLogicalExpr(pos, op, x, y))
|
||||||
|
case ir.OADD:
|
||||||
|
if x.Type().IsString() {
|
||||||
|
// TODO(mdempsky): Construct OADDSTR directly.
|
||||||
|
return typecheck.Expr(ir.NewBinaryExpr(pos, op, x, y))
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
return typed(x.Type(), ir.NewBinaryExpr(pos, op, x, y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Call(pos src.XPos, typ *types.Type, fun ir.Node, args []ir.Node, dots bool) ir.Node {
|
||||||
|
// TODO(mdempsky): This should not be so difficult.
|
||||||
|
if fun.Op() == ir.OTYPE {
|
||||||
|
// Actually a type conversion, not a function call.
|
||||||
|
n := ir.NewCallExpr(pos, ir.OCALL, fun, args)
|
||||||
|
if fun.Type().Kind() == types.TTYPEPARAM {
|
||||||
|
// For type params, don't typecheck until we actually know
|
||||||
|
// the type.
|
||||||
|
return typed(typ, n)
|
||||||
|
}
|
||||||
|
return typecheck.Expr(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fun, ok := fun.(*ir.Name); ok && fun.BuiltinOp != 0 {
|
||||||
|
// Call to a builtin function.
|
||||||
|
n := ir.NewCallExpr(pos, ir.OCALL, fun, args)
|
||||||
|
n.IsDDD = dots
|
||||||
|
switch fun.BuiltinOp {
|
||||||
|
case ir.OCLOSE, ir.ODELETE, ir.OPANIC, ir.OPRINT, ir.OPRINTN:
|
||||||
|
return typecheck.Stmt(n)
|
||||||
|
default:
|
||||||
|
return typecheck.Expr(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add information, now that we know that fun is actually being called.
|
||||||
|
switch fun := fun.(type) {
|
||||||
|
case *ir.ClosureExpr:
|
||||||
|
fun.Func.SetClosureCalled(true)
|
||||||
|
case *ir.SelectorExpr:
|
||||||
|
if fun.Op() == ir.OCALLPART {
|
||||||
|
op := ir.ODOTMETH
|
||||||
|
if fun.X.Type().IsInterface() {
|
||||||
|
op = ir.ODOTINTER
|
||||||
|
}
|
||||||
|
fun.SetOp(op)
|
||||||
|
// Set the type to include the receiver, since that's what
|
||||||
|
// later parts of the compiler expect
|
||||||
|
fun.SetType(fun.Selection.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n := ir.NewCallExpr(pos, ir.OCALL, fun, args)
|
||||||
|
n.IsDDD = dots
|
||||||
|
|
||||||
|
if fun.Op() == ir.OXDOT {
|
||||||
|
if fun.(*ir.SelectorExpr).X.Type().Kind() != types.TTYPEPARAM {
|
||||||
|
base.FatalfAt(pos, "Expecting type param receiver in %v", fun)
|
||||||
|
}
|
||||||
|
// For methods called in a generic function, don't do any extra
|
||||||
|
// transformations. We will do those later when we create the
|
||||||
|
// instantiated function and have the correct receiver type.
|
||||||
|
typed(typ, n)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
if fun.Op() != ir.OFUNCINST {
|
||||||
|
// If no type params, still do normal typechecking, since we're
|
||||||
|
// still missing some things done by tcCall below (mainly
|
||||||
|
// typecheckargs and typecheckaste).
|
||||||
|
typecheck.Call(n)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
n.Use = ir.CallUseExpr
|
||||||
|
if fun.Type().NumResults() == 0 {
|
||||||
|
n.Use = ir.CallUseStmt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewrite call node depending on use.
|
||||||
|
switch fun.Op() {
|
||||||
|
case ir.ODOTINTER:
|
||||||
|
n.SetOp(ir.OCALLINTER)
|
||||||
|
|
||||||
|
case ir.ODOTMETH:
|
||||||
|
n.SetOp(ir.OCALLMETH)
|
||||||
|
|
||||||
|
default:
|
||||||
|
n.SetOp(ir.OCALLFUNC)
|
||||||
|
}
|
||||||
|
|
||||||
|
typed(typ, n)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func Compare(pos src.XPos, typ *types.Type, op ir.Op, x, y ir.Node) ir.Node {
|
||||||
|
n := ir.NewBinaryExpr(pos, op, x, y)
|
||||||
|
if !types.Identical(x.Type(), y.Type()) {
|
||||||
|
// TODO(mdempsky): Handle subtleties of constructing mixed-typed comparisons.
|
||||||
|
n = typecheck.Expr(n).(*ir.BinaryExpr)
|
||||||
|
}
|
||||||
|
return typed(typ, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Deref(pos src.XPos, x ir.Node) *ir.StarExpr {
|
||||||
|
n := ir.NewStarExpr(pos, x)
|
||||||
|
typed(x.Type().Elem(), n)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func DotField(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr {
|
||||||
|
op, typ := ir.ODOT, x.Type()
|
||||||
|
if typ.IsPtr() {
|
||||||
|
op, typ = ir.ODOTPTR, typ.Elem()
|
||||||
|
}
|
||||||
|
if !typ.IsStruct() {
|
||||||
|
base.FatalfAt(pos, "DotField of non-struct: %L", x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(mdempsky): This is the backend's responsibility.
|
||||||
|
types.CalcSize(typ)
|
||||||
|
|
||||||
|
field := typ.Field(index)
|
||||||
|
return dot(pos, field.Type, op, x, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DotMethod(pos src.XPos, x ir.Node, index int) *ir.SelectorExpr {
|
||||||
|
method := method(x.Type(), index)
|
||||||
|
|
||||||
|
// Method value.
|
||||||
|
typ := typecheck.NewMethodType(method.Type, nil)
|
||||||
|
return dot(pos, typ, ir.OCALLPART, x, method)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MethodExpr returns a OMETHEXPR node with the indicated index into the methods
|
||||||
|
// of typ. The receiver type is set from recv, which is different from typ if the
|
||||||
|
// method was accessed via embedded fields. Similarly, the X value of the
|
||||||
|
// ir.SelectorExpr is recv, the original OTYPE node before passing through the
|
||||||
|
// embedded fields.
|
||||||
|
func MethodExpr(pos src.XPos, recv ir.Node, embed *types.Type, index int) *ir.SelectorExpr {
|
||||||
|
method := method(embed, index)
|
||||||
|
typ := typecheck.NewMethodType(method.Type, recv.Type())
|
||||||
|
// The method expression T.m requires a wrapper when T
|
||||||
|
// is different from m's declared receiver type. We
|
||||||
|
// normally generate these wrappers while writing out
|
||||||
|
// runtime type descriptors, which is always done for
|
||||||
|
// types declared at package scope. However, we need
|
||||||
|
// to make sure to generate wrappers for anonymous
|
||||||
|
// receiver types too.
|
||||||
|
if recv.Sym() == nil {
|
||||||
|
typecheck.NeedRuntimeType(recv.Type())
|
||||||
|
}
|
||||||
|
return dot(pos, typ, ir.OMETHEXPR, recv, method)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dot(pos src.XPos, typ *types.Type, op ir.Op, x ir.Node, selection *types.Field) *ir.SelectorExpr {
|
||||||
|
n := ir.NewSelectorExpr(pos, op, x, selection.Sym)
|
||||||
|
n.Selection = selection
|
||||||
|
typed(typ, n)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(mdempsky): Move to package types.
|
||||||
|
func method(typ *types.Type, index int) *types.Field {
|
||||||
|
if typ.IsInterface() {
|
||||||
|
return typ.Field(index)
|
||||||
|
}
|
||||||
|
return types.ReceiverBaseType(typ).Methods().Index(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Index(pos src.XPos, x, index ir.Node) ir.Node {
|
||||||
|
// TODO(mdempsky): Avoid typecheck.Expr (which will call tcIndex)
|
||||||
|
return typecheck.Expr(ir.NewIndexExpr(pos, x, index))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Slice(pos src.XPos, x, low, high, max ir.Node) ir.Node {
|
||||||
|
op := ir.OSLICE
|
||||||
|
if max != nil {
|
||||||
|
op = ir.OSLICE3
|
||||||
|
}
|
||||||
|
// TODO(mdempsky): Avoid typecheck.Expr.
|
||||||
|
return typecheck.Expr(ir.NewSliceExpr(pos, op, x, low, high, max))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unary(pos src.XPos, op ir.Op, x ir.Node) ir.Node {
|
||||||
|
switch op {
|
||||||
|
case ir.OADDR:
|
||||||
|
return Addr(pos, x)
|
||||||
|
case ir.ODEREF:
|
||||||
|
return Deref(pos, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := x.Type()
|
||||||
|
if op == ir.ORECV {
|
||||||
|
typ = typ.Elem()
|
||||||
|
}
|
||||||
|
return typed(typ, ir.NewUnaryExpr(pos, op, x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statements
|
||||||
|
|
||||||
|
var one = constant.MakeInt64(1)
|
||||||
|
|
||||||
|
func IncDec(pos src.XPos, op ir.Op, x ir.Node) ir.Node {
|
||||||
|
x = typecheck.AssignExpr(x)
|
||||||
|
return ir.NewAssignOpStmt(pos, op, x, typecheck.DefaultLit(ir.NewBasicLit(pos, one), x.Type()))
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ package noder
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
pathpkg "path"
|
pathpkg "path"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
@ -17,10 +18,12 @@ import (
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"cmd/compile/internal/base"
|
"cmd/compile/internal/base"
|
||||||
|
"cmd/compile/internal/importer"
|
||||||
"cmd/compile/internal/ir"
|
"cmd/compile/internal/ir"
|
||||||
"cmd/compile/internal/syntax"
|
"cmd/compile/internal/syntax"
|
||||||
"cmd/compile/internal/typecheck"
|
"cmd/compile/internal/typecheck"
|
||||||
"cmd/compile/internal/types"
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
"cmd/internal/archive"
|
"cmd/internal/archive"
|
||||||
"cmd/internal/bio"
|
"cmd/internal/bio"
|
||||||
"cmd/internal/goobj"
|
"cmd/internal/goobj"
|
||||||
|
|
@ -28,6 +31,29 @@ import (
|
||||||
"cmd/internal/src"
|
"cmd/internal/src"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Temporary import helper to get type2-based type-checking going.
|
||||||
|
type gcimports struct {
|
||||||
|
packages map[string]*types2.Package
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *gcimports) Import(path string) (*types2.Package, error) {
|
||||||
|
return m.ImportFrom(path, "" /* no vendoring */, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) {
|
||||||
|
if mode != 0 {
|
||||||
|
panic("mode must be 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
path, err := resolveImportPath(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lookup := func(path string) (io.ReadCloser, error) { return openPackage(path) }
|
||||||
|
return importer.Import(m.packages, path, srcDir, lookup)
|
||||||
|
}
|
||||||
|
|
||||||
func isDriveLetter(b byte) bool {
|
func isDriveLetter(b byte) bool {
|
||||||
return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
|
return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
209
src/cmd/compile/internal/noder/irgen.go
Normal file
209
src/cmd/compile/internal/noder/irgen.go
Normal file
|
|
@ -0,0 +1,209 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package noder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"cmd/compile/internal/base"
|
||||||
|
"cmd/compile/internal/dwarfgen"
|
||||||
|
"cmd/compile/internal/ir"
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"cmd/compile/internal/typecheck"
|
||||||
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
|
"cmd/internal/src"
|
||||||
|
)
|
||||||
|
|
||||||
|
// check2 type checks a Go package using types2, and then generates IR
|
||||||
|
// using the results.
|
||||||
|
func check2(noders []*noder) {
|
||||||
|
if base.SyntaxErrors() != 0 {
|
||||||
|
base.ErrorExit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup and syntax error reporting
|
||||||
|
var m posMap
|
||||||
|
files := make([]*syntax.File, len(noders))
|
||||||
|
for i, p := range noders {
|
||||||
|
m.join(&p.posMap)
|
||||||
|
files[i] = p.file
|
||||||
|
}
|
||||||
|
|
||||||
|
// typechecking
|
||||||
|
conf := types2.Config{
|
||||||
|
GoVersion: base.Flag.Lang,
|
||||||
|
InferFromConstraints: true,
|
||||||
|
IgnoreLabels: true, // parser already checked via syntax.CheckBranches mode
|
||||||
|
CompilerErrorMessages: true, // use error strings matching existing compiler errors
|
||||||
|
Error: func(err error) {
|
||||||
|
terr := err.(types2.Error)
|
||||||
|
if len(terr.Msg) > 0 && terr.Msg[0] == '\t' {
|
||||||
|
// types2 reports error clarifications via separate
|
||||||
|
// error messages which are indented with a tab.
|
||||||
|
// Ignore them to satisfy tools and tests that expect
|
||||||
|
// only one error in such cases.
|
||||||
|
// TODO(gri) Need to adjust error reporting in types2.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
base.ErrorfAt(m.makeXPos(terr.Pos), "%s", terr.Msg)
|
||||||
|
},
|
||||||
|
Importer: &gcimports{
|
||||||
|
packages: make(map[string]*types2.Package),
|
||||||
|
},
|
||||||
|
Sizes: &gcSizes{},
|
||||||
|
}
|
||||||
|
info := types2.Info{
|
||||||
|
Types: make(map[syntax.Expr]types2.TypeAndValue),
|
||||||
|
Defs: make(map[*syntax.Name]types2.Object),
|
||||||
|
Uses: make(map[*syntax.Name]types2.Object),
|
||||||
|
Selections: make(map[*syntax.SelectorExpr]*types2.Selection),
|
||||||
|
Implicits: make(map[syntax.Node]types2.Object),
|
||||||
|
Scopes: make(map[syntax.Node]*types2.Scope),
|
||||||
|
Inferred: make(map[syntax.Expr]types2.Inferred),
|
||||||
|
// expand as needed
|
||||||
|
}
|
||||||
|
pkg, err := conf.Check(base.Ctxt.Pkgpath, files, &info)
|
||||||
|
files = nil
|
||||||
|
if err != nil {
|
||||||
|
base.FatalfAt(src.NoXPos, "conf.Check error: %v", err)
|
||||||
|
}
|
||||||
|
base.ExitIfErrors()
|
||||||
|
if base.Flag.G < 2 {
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
g := irgen{
|
||||||
|
target: typecheck.Target,
|
||||||
|
self: pkg,
|
||||||
|
info: &info,
|
||||||
|
posMap: m,
|
||||||
|
objs: make(map[types2.Object]*ir.Name),
|
||||||
|
typs: make(map[types2.Type]*types.Type),
|
||||||
|
}
|
||||||
|
g.generate(noders)
|
||||||
|
|
||||||
|
if base.Flag.G < 3 {
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type irgen struct {
|
||||||
|
target *ir.Package
|
||||||
|
self *types2.Package
|
||||||
|
info *types2.Info
|
||||||
|
|
||||||
|
posMap
|
||||||
|
objs map[types2.Object]*ir.Name
|
||||||
|
typs map[types2.Type]*types.Type
|
||||||
|
marker dwarfgen.ScopeMarker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) generate(noders []*noder) {
|
||||||
|
types.LocalPkg.Name = g.self.Name()
|
||||||
|
typecheck.TypecheckAllowed = true
|
||||||
|
|
||||||
|
// Prevent size calculations until we set the underlying type
|
||||||
|
// for all package-block defined types.
|
||||||
|
types.DeferCheckSize()
|
||||||
|
|
||||||
|
// At this point, types2 has already handled name resolution and
|
||||||
|
// type checking. We just need to map from its object and type
|
||||||
|
// representations to those currently used by the rest of the
|
||||||
|
// compiler. This happens mostly in 3 passes.
|
||||||
|
|
||||||
|
// 1. Process all import declarations. We use the compiler's own
|
||||||
|
// importer for this, rather than types2's gcimporter-derived one,
|
||||||
|
// to handle extensions and inline function bodies correctly.
|
||||||
|
//
|
||||||
|
// Also, we need to do this in a separate pass, because mappings are
|
||||||
|
// instantiated on demand. If we interleaved processing import
|
||||||
|
// declarations with other declarations, it's likely we'd end up
|
||||||
|
// wanting to map an object/type from another source file, but not
|
||||||
|
// yet have the import data it relies on.
|
||||||
|
declLists := make([][]syntax.Decl, len(noders))
|
||||||
|
Outer:
|
||||||
|
for i, p := range noders {
|
||||||
|
g.pragmaFlags(p.file.Pragma, ir.GoBuildPragma)
|
||||||
|
for j, decl := range p.file.DeclList {
|
||||||
|
switch decl := decl.(type) {
|
||||||
|
case *syntax.ImportDecl:
|
||||||
|
g.importDecl(p, decl)
|
||||||
|
default:
|
||||||
|
declLists[i] = p.file.DeclList[j:]
|
||||||
|
continue Outer // no more ImportDecls
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
types.LocalPkg.Height = myheight
|
||||||
|
|
||||||
|
// 2. Process all package-block type declarations. As with imports,
|
||||||
|
// we need to make sure all types are properly instantiated before
|
||||||
|
// trying to map any expressions that utilize them. In particular,
|
||||||
|
// we need to make sure type pragmas are already known (see comment
|
||||||
|
// in irgen.typeDecl).
|
||||||
|
//
|
||||||
|
// We could perhaps instead defer processing of package-block
|
||||||
|
// variable initializers and function bodies, like noder does, but
|
||||||
|
// special-casing just package-block type declarations minimizes the
|
||||||
|
// differences between processing package-block and function-scoped
|
||||||
|
// declarations.
|
||||||
|
for _, declList := range declLists {
|
||||||
|
for _, decl := range declList {
|
||||||
|
switch decl := decl.(type) {
|
||||||
|
case *syntax.TypeDecl:
|
||||||
|
g.typeDecl((*ir.Nodes)(&g.target.Decls), decl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
types.ResumeCheckSize()
|
||||||
|
|
||||||
|
// 3. Process all remaining declarations.
|
||||||
|
for _, declList := range declLists {
|
||||||
|
g.target.Decls = append(g.target.Decls, g.decls(declList)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if base.Flag.W > 1 {
|
||||||
|
for _, n := range g.target.Decls {
|
||||||
|
s := fmt.Sprintf("\nafter noder2 %v", n)
|
||||||
|
ir.Dump(s, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typecheck.DeclareUniverse()
|
||||||
|
|
||||||
|
for _, p := range noders {
|
||||||
|
// Process linkname and cgo pragmas.
|
||||||
|
p.processPragmas()
|
||||||
|
|
||||||
|
// Double check for any type-checking inconsistencies. This can be
|
||||||
|
// removed once we're confident in IR generation results.
|
||||||
|
syntax.Walk(p.file, func(n syntax.Node) bool {
|
||||||
|
g.validate(n)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create any needed stencils of generic functions
|
||||||
|
g.stencil()
|
||||||
|
|
||||||
|
// For now, remove all generic functions from g.target.Decl, since they
|
||||||
|
// have been used for stenciling, but don't compile. TODO: We will
|
||||||
|
// eventually export any exportable generic functions.
|
||||||
|
j := 0
|
||||||
|
for i, decl := range g.target.Decls {
|
||||||
|
if decl.Op() != ir.ODCLFUNC || !decl.Type().HasTParam() {
|
||||||
|
g.target.Decls[j] = g.target.Decls[i]
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.target.Decls = g.target.Decls[:j]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) unhandled(what string, p poser) {
|
||||||
|
base.FatalfAt(g.pos(p), "unhandled %s: %T", what, p)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,9 @@ func LoadPackage(filenames []string) {
|
||||||
base.Timer.Start("fe", "parse")
|
base.Timer.Start("fe", "parse")
|
||||||
|
|
||||||
mode := syntax.CheckBranches
|
mode := syntax.CheckBranches
|
||||||
|
if base.Flag.G != 0 {
|
||||||
|
mode |= syntax.AllowGenerics
|
||||||
|
}
|
||||||
|
|
||||||
// Limit the number of simultaneously open files.
|
// Limit the number of simultaneously open files.
|
||||||
sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10)
|
sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10)
|
||||||
|
|
@ -65,10 +68,16 @@ func LoadPackage(filenames []string) {
|
||||||
for e := range p.err {
|
for e := range p.err {
|
||||||
p.errorAt(e.Pos, "%s", e.Msg)
|
p.errorAt(e.Pos, "%s", e.Msg)
|
||||||
}
|
}
|
||||||
lines += p.file.Lines
|
lines += p.file.EOF.Line()
|
||||||
}
|
}
|
||||||
base.Timer.AddEvent(int64(lines), "lines")
|
base.Timer.AddEvent(int64(lines), "lines")
|
||||||
|
|
||||||
|
if base.Flag.G != 0 {
|
||||||
|
// Use types2 to type-check and possibly generate IR.
|
||||||
|
check2(noders)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for _, p := range noders {
|
for _, p := range noders {
|
||||||
p.node()
|
p.node()
|
||||||
p.file = nil // release memory
|
p.file = nil // release memory
|
||||||
|
|
@ -1031,8 +1040,7 @@ func (p *noder) stmtFall(stmt syntax.Stmt, fallOK bool) ir.Node {
|
||||||
case *syntax.DeclStmt:
|
case *syntax.DeclStmt:
|
||||||
return ir.NewBlockStmt(src.NoXPos, p.decls(stmt.DeclList))
|
return ir.NewBlockStmt(src.NoXPos, p.decls(stmt.DeclList))
|
||||||
case *syntax.AssignStmt:
|
case *syntax.AssignStmt:
|
||||||
if stmt.Rhs == syntax.ImplicitOne {
|
if stmt.Rhs == nil {
|
||||||
one := constant.MakeInt64(1)
|
|
||||||
pos := p.pos(stmt)
|
pos := p.pos(stmt)
|
||||||
n := ir.NewAssignOpStmt(pos, p.binOp(stmt.Op), p.expr(stmt.Lhs), ir.NewBasicLit(pos, one))
|
n := ir.NewAssignOpStmt(pos, p.binOp(stmt.Op), p.expr(stmt.Lhs), ir.NewBasicLit(pos, one))
|
||||||
n.IncDec = true
|
n.IncDec = true
|
||||||
|
|
@ -1447,6 +1455,20 @@ func (p *noder) basicLit(lit *syntax.BasicLit) constant.Value {
|
||||||
switch lit.Kind {
|
switch lit.Kind {
|
||||||
case syntax.IntLit, syntax.FloatLit, syntax.ImagLit:
|
case syntax.IntLit, syntax.FloatLit, syntax.ImagLit:
|
||||||
checkLangCompat(lit)
|
checkLangCompat(lit)
|
||||||
|
// The max. mantissa precision for untyped numeric values
|
||||||
|
// is 512 bits, or 4048 bits for each of the two integer
|
||||||
|
// parts of a fraction for floating-point numbers that are
|
||||||
|
// represented accurately in the go/constant package.
|
||||||
|
// Constant literals that are longer than this many bits
|
||||||
|
// are not meaningful; and excessively long constants may
|
||||||
|
// consume a lot of space and time for a useless conversion.
|
||||||
|
// Cap constant length with a generous upper limit that also
|
||||||
|
// allows for separators between all digits.
|
||||||
|
const limit = 10000
|
||||||
|
if len(lit.Value) > limit {
|
||||||
|
p.errorAt(lit.Pos(), "excessively long constant: %s... (%d chars)", lit.Value[:10], len(lit.Value))
|
||||||
|
return constant.MakeUnknown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
v := constant.MakeFromLiteral(lit.Value, tokenForLitKind[lit.Kind], 0)
|
v := constant.MakeFromLiteral(lit.Value, tokenForLitKind[lit.Kind], 0)
|
||||||
|
|
|
||||||
175
src/cmd/compile/internal/noder/object.go
Normal file
175
src/cmd/compile/internal/noder/object.go
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package noder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/compile/internal/base"
|
||||||
|
"cmd/compile/internal/ir"
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"cmd/compile/internal/typecheck"
|
||||||
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
|
"cmd/internal/src"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (g *irgen) def(name *syntax.Name) (*ir.Name, types2.Object) {
|
||||||
|
obj, ok := g.info.Defs[name]
|
||||||
|
if !ok {
|
||||||
|
base.FatalfAt(g.pos(name), "unknown name %v", name)
|
||||||
|
}
|
||||||
|
return g.obj(obj), obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) use(name *syntax.Name) *ir.Name {
|
||||||
|
obj, ok := g.info.Uses[name]
|
||||||
|
if !ok {
|
||||||
|
base.FatalfAt(g.pos(name), "unknown name %v", name)
|
||||||
|
}
|
||||||
|
return ir.CaptureName(g.pos(obj), ir.CurFunc, g.obj(obj))
|
||||||
|
}
|
||||||
|
|
||||||
|
// obj returns the Name that represents the given object. If no such
|
||||||
|
// Name exists yet, it will be implicitly created.
|
||||||
|
//
|
||||||
|
// For objects declared at function scope, ir.CurFunc must already be
|
||||||
|
// set to the respective function when the Name is created.
|
||||||
|
func (g *irgen) obj(obj types2.Object) *ir.Name {
|
||||||
|
// For imported objects, we use iimport directly instead of mapping
|
||||||
|
// the types2 representation.
|
||||||
|
if obj.Pkg() != g.self {
|
||||||
|
sym := g.sym(obj)
|
||||||
|
if sym.Def != nil {
|
||||||
|
return sym.Def.(*ir.Name)
|
||||||
|
}
|
||||||
|
n := typecheck.Resolve(ir.NewIdent(src.NoXPos, sym))
|
||||||
|
if n, ok := n.(*ir.Name); ok {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
base.FatalfAt(g.pos(obj), "failed to resolve %v", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
if name, ok := g.objs[obj]; ok {
|
||||||
|
return name // previously mapped
|
||||||
|
}
|
||||||
|
|
||||||
|
var name *ir.Name
|
||||||
|
pos := g.pos(obj)
|
||||||
|
|
||||||
|
class := typecheck.DeclContext
|
||||||
|
if obj.Parent() == g.self.Scope() {
|
||||||
|
class = ir.PEXTERN // forward reference to package-block declaration
|
||||||
|
}
|
||||||
|
|
||||||
|
// "You are in a maze of twisting little passages, all different."
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *types2.Const:
|
||||||
|
name = g.objCommon(pos, ir.OLITERAL, g.sym(obj), class, g.typ(obj.Type()))
|
||||||
|
|
||||||
|
case *types2.Func:
|
||||||
|
sig := obj.Type().(*types2.Signature)
|
||||||
|
var sym *types.Sym
|
||||||
|
var typ *types.Type
|
||||||
|
if recv := sig.Recv(); recv == nil {
|
||||||
|
if obj.Name() == "init" {
|
||||||
|
sym = renameinit()
|
||||||
|
} else {
|
||||||
|
sym = g.sym(obj)
|
||||||
|
}
|
||||||
|
typ = g.typ(sig)
|
||||||
|
} else {
|
||||||
|
sym = g.selector(obj)
|
||||||
|
if !sym.IsBlank() {
|
||||||
|
sym = ir.MethodSym(g.typ(recv.Type()), sym)
|
||||||
|
}
|
||||||
|
typ = g.signature(g.param(recv), sig)
|
||||||
|
}
|
||||||
|
name = g.objCommon(pos, ir.ONAME, sym, ir.PFUNC, typ)
|
||||||
|
|
||||||
|
case *types2.TypeName:
|
||||||
|
if obj.IsAlias() {
|
||||||
|
name = g.objCommon(pos, ir.OTYPE, g.sym(obj), class, g.typ(obj.Type()))
|
||||||
|
} else {
|
||||||
|
name = ir.NewDeclNameAt(pos, ir.OTYPE, g.sym(obj))
|
||||||
|
g.objFinish(name, class, types.NewNamed(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
case *types2.Var:
|
||||||
|
var sym *types.Sym
|
||||||
|
if class == ir.PPARAMOUT {
|
||||||
|
// Backend needs names for result parameters,
|
||||||
|
// even if they're anonymous or blank.
|
||||||
|
switch obj.Name() {
|
||||||
|
case "":
|
||||||
|
sym = typecheck.LookupNum("~r", len(ir.CurFunc.Dcl)) // 'r' for "result"
|
||||||
|
case "_":
|
||||||
|
sym = typecheck.LookupNum("~b", len(ir.CurFunc.Dcl)) // 'b' for "blank"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sym == nil {
|
||||||
|
sym = g.sym(obj)
|
||||||
|
}
|
||||||
|
name = g.objCommon(pos, ir.ONAME, sym, class, g.typ(obj.Type()))
|
||||||
|
|
||||||
|
default:
|
||||||
|
g.unhandled("object", obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.objs[obj] = name
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) objCommon(pos src.XPos, op ir.Op, sym *types.Sym, class ir.Class, typ *types.Type) *ir.Name {
|
||||||
|
name := ir.NewDeclNameAt(pos, op, sym)
|
||||||
|
g.objFinish(name, class, typ)
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) objFinish(name *ir.Name, class ir.Class, typ *types.Type) {
|
||||||
|
sym := name.Sym()
|
||||||
|
|
||||||
|
name.SetType(typ)
|
||||||
|
name.Class = class
|
||||||
|
if name.Class == ir.PFUNC {
|
||||||
|
sym.SetFunc(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We already know name's type, but typecheck is really eager to try
|
||||||
|
// recomputing it later. This appears to prevent that at least.
|
||||||
|
name.Ntype = ir.TypeNode(typ)
|
||||||
|
name.SetTypecheck(1)
|
||||||
|
name.SetWalkdef(1)
|
||||||
|
|
||||||
|
if ir.IsBlank(name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch class {
|
||||||
|
case ir.PEXTERN:
|
||||||
|
g.target.Externs = append(g.target.Externs, name)
|
||||||
|
fallthrough
|
||||||
|
case ir.PFUNC:
|
||||||
|
sym.Def = name
|
||||||
|
if name.Class == ir.PFUNC && name.Type().Recv() != nil {
|
||||||
|
break // methods are exported with their receiver type
|
||||||
|
}
|
||||||
|
if types.IsExported(sym.Name) {
|
||||||
|
if name.Class == ir.PFUNC && name.Type().NumTParams() > 0 {
|
||||||
|
base.FatalfAt(name.Pos(), "Cannot export a generic function (yet): %v", name)
|
||||||
|
}
|
||||||
|
typecheck.Export(name)
|
||||||
|
}
|
||||||
|
if base.Flag.AsmHdr != "" && !name.Sym().Asm() {
|
||||||
|
name.Sym().SetAsm(true)
|
||||||
|
g.target.Asms = append(g.target.Asms, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Function-scoped declaration.
|
||||||
|
name.Curfn = ir.CurFunc
|
||||||
|
if name.Op() == ir.ONAME {
|
||||||
|
ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/cmd/compile/internal/noder/scopes.go
Normal file
64
src/cmd/compile/internal/noder/scopes.go
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package noder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"cmd/compile/internal/base"
|
||||||
|
"cmd/compile/internal/ir"
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// recordScopes populates fn.Parents and fn.Marks based on the scoping
|
||||||
|
// information provided by types2.
|
||||||
|
func (g *irgen) recordScopes(fn *ir.Func, sig *syntax.FuncType) {
|
||||||
|
scope, ok := g.info.Scopes[sig]
|
||||||
|
if !ok {
|
||||||
|
base.FatalfAt(fn.Pos(), "missing scope for %v", fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, n := 0, scope.NumChildren(); i < n; i++ {
|
||||||
|
g.walkScope(scope.Child(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
g.marker.WriteTo(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) walkScope(scope *types2.Scope) bool {
|
||||||
|
// types2 doesn't provide a proper API for determining the
|
||||||
|
// lexical element a scope represents, so we have to resort to
|
||||||
|
// string matching. Conveniently though, this allows us to
|
||||||
|
// skip both function types and function literals, neither of
|
||||||
|
// which are interesting to us here.
|
||||||
|
if strings.HasPrefix(scope.String(), "function scope ") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
g.marker.Push(g.pos(scope))
|
||||||
|
|
||||||
|
haveVars := false
|
||||||
|
for _, name := range scope.Names() {
|
||||||
|
if obj, ok := scope.Lookup(name).(*types2.Var); ok && obj.Name() != "_" {
|
||||||
|
haveVars = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, n := 0, scope.NumChildren(); i < n; i++ {
|
||||||
|
if g.walkScope(scope.Child(i)) {
|
||||||
|
haveVars = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if haveVars {
|
||||||
|
g.marker.Pop(g.end(scope))
|
||||||
|
} else {
|
||||||
|
g.marker.Unpush()
|
||||||
|
}
|
||||||
|
|
||||||
|
return haveVars
|
||||||
|
}
|
||||||
150
src/cmd/compile/internal/noder/sizes.go
Normal file
150
src/cmd/compile/internal/noder/sizes.go
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package noder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Code below based on go/types.StdSizes.
|
||||||
|
// Intentional differences are marked with "gc:".
|
||||||
|
|
||||||
|
type gcSizes struct{}
|
||||||
|
|
||||||
|
func (s *gcSizes) Alignof(T types2.Type) int64 {
|
||||||
|
// For arrays and structs, alignment is defined in terms
|
||||||
|
// of alignment of the elements and fields, respectively.
|
||||||
|
switch t := T.Underlying().(type) {
|
||||||
|
case *types2.Array:
|
||||||
|
// spec: "For a variable x of array type: unsafe.Alignof(x)
|
||||||
|
// is the same as unsafe.Alignof(x[0]), but at least 1."
|
||||||
|
return s.Alignof(t.Elem())
|
||||||
|
case *types2.Struct:
|
||||||
|
// spec: "For a variable x of struct type: unsafe.Alignof(x)
|
||||||
|
// is the largest of the values unsafe.Alignof(x.f) for each
|
||||||
|
// field f of x, but at least 1."
|
||||||
|
max := int64(1)
|
||||||
|
for i, nf := 0, t.NumFields(); i < nf; i++ {
|
||||||
|
if a := s.Alignof(t.Field(i).Type()); a > max {
|
||||||
|
max = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
case *types2.Slice, *types2.Interface:
|
||||||
|
// Multiword data structures are effectively structs
|
||||||
|
// in which each element has size PtrSize.
|
||||||
|
return int64(types.PtrSize)
|
||||||
|
case *types2.Basic:
|
||||||
|
// Strings are like slices and interfaces.
|
||||||
|
if t.Info()&types2.IsString != 0 {
|
||||||
|
return int64(types.PtrSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a := s.Sizeof(T) // may be 0
|
||||||
|
// spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
|
||||||
|
if a < 1 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
// complex{64,128} are aligned like [2]float{32,64}.
|
||||||
|
if isComplex(T) {
|
||||||
|
a /= 2
|
||||||
|
}
|
||||||
|
if a > int64(types.RegSize) {
|
||||||
|
return int64(types.RegSize)
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
func isComplex(T types2.Type) bool {
|
||||||
|
basic, ok := T.Underlying().(*types2.Basic)
|
||||||
|
return ok && basic.Info()&types2.IsComplex != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *gcSizes) Offsetsof(fields []*types2.Var) []int64 {
|
||||||
|
offsets := make([]int64, len(fields))
|
||||||
|
var o int64
|
||||||
|
for i, f := range fields {
|
||||||
|
typ := f.Type()
|
||||||
|
a := s.Alignof(typ)
|
||||||
|
o = types.Rnd(o, a)
|
||||||
|
offsets[i] = o
|
||||||
|
o += s.Sizeof(typ)
|
||||||
|
}
|
||||||
|
return offsets
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *gcSizes) Sizeof(T types2.Type) int64 {
|
||||||
|
switch t := T.Underlying().(type) {
|
||||||
|
case *types2.Basic:
|
||||||
|
k := t.Kind()
|
||||||
|
if int(k) < len(basicSizes) {
|
||||||
|
if s := basicSizes[k]; s > 0 {
|
||||||
|
return int64(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch k {
|
||||||
|
case types2.String:
|
||||||
|
return int64(types.PtrSize) * 2
|
||||||
|
case types2.Int, types2.Uint, types2.Uintptr, types2.UnsafePointer:
|
||||||
|
return int64(types.PtrSize)
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("unimplemented basic: %v (kind %v)", T, k))
|
||||||
|
case *types2.Array:
|
||||||
|
n := t.Len()
|
||||||
|
if n <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// n > 0
|
||||||
|
// gc: Size includes alignment padding.
|
||||||
|
return s.Sizeof(t.Elem()) * n
|
||||||
|
case *types2.Slice:
|
||||||
|
return int64(types.PtrSize) * 3
|
||||||
|
case *types2.Struct:
|
||||||
|
n := t.NumFields()
|
||||||
|
if n == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
fields := make([]*types2.Var, n)
|
||||||
|
for i := range fields {
|
||||||
|
fields[i] = t.Field(i)
|
||||||
|
}
|
||||||
|
offsets := s.Offsetsof(fields)
|
||||||
|
|
||||||
|
// gc: The last field of a struct is not allowed to
|
||||||
|
// have size 0.
|
||||||
|
last := s.Sizeof(fields[n-1].Type())
|
||||||
|
if last == 0 {
|
||||||
|
last = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// gc: Size includes alignment padding.
|
||||||
|
return types.Rnd(offsets[n-1]+last, s.Alignof(t))
|
||||||
|
case *types2.Interface:
|
||||||
|
return int64(types.PtrSize) * 2
|
||||||
|
case *types2.Chan, *types2.Map, *types2.Pointer, *types2.Signature:
|
||||||
|
return int64(types.PtrSize)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unimplemented type: %T", t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var basicSizes = [...]byte{
|
||||||
|
types2.Bool: 1,
|
||||||
|
types2.Int8: 1,
|
||||||
|
types2.Int16: 2,
|
||||||
|
types2.Int32: 4,
|
||||||
|
types2.Int64: 8,
|
||||||
|
types2.Uint8: 1,
|
||||||
|
types2.Uint16: 2,
|
||||||
|
types2.Uint32: 4,
|
||||||
|
types2.Uint64: 8,
|
||||||
|
types2.Float32: 4,
|
||||||
|
types2.Float64: 8,
|
||||||
|
types2.Complex64: 8,
|
||||||
|
types2.Complex128: 16,
|
||||||
|
}
|
||||||
460
src/cmd/compile/internal/noder/stencil.go
Normal file
460
src/cmd/compile/internal/noder/stencil.go
Normal file
|
|
@ -0,0 +1,460 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file will evolve, since we plan to do a mix of stenciling and passing
|
||||||
|
// around dictionaries.
|
||||||
|
|
||||||
|
package noder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cmd/compile/internal/base"
|
||||||
|
"cmd/compile/internal/ir"
|
||||||
|
"cmd/compile/internal/typecheck"
|
||||||
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/internal/src"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// stencil scans functions for instantiated generic function calls and
|
||||||
|
// creates the required stencils for simple generic functions.
|
||||||
|
func (g *irgen) stencil() {
|
||||||
|
g.target.Stencils = make(map[*types.Sym]*ir.Func)
|
||||||
|
// Don't use range(g.target.Decls) - we also want to process any new instantiated
|
||||||
|
// functions that are created during this loop, in order to handle generic
|
||||||
|
// functions calling other generic functions.
|
||||||
|
for i := 0; i < len(g.target.Decls); i++ {
|
||||||
|
decl := g.target.Decls[i]
|
||||||
|
if decl.Op() != ir.ODCLFUNC || decl.Type().NumTParams() > 0 {
|
||||||
|
// Skip any non-function declarations and skip generic functions
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// For each non-generic function, search for any function calls using
|
||||||
|
// generic function instantiations. (We don't yet handle generic
|
||||||
|
// function instantiations that are not immediately called.)
|
||||||
|
// Then create the needed instantiated function if it hasn't been
|
||||||
|
// created yet, and change to calling that function directly.
|
||||||
|
f := decl.(*ir.Func)
|
||||||
|
modified := false
|
||||||
|
ir.VisitList(f.Body, func(n ir.Node) {
|
||||||
|
if n.Op() != ir.OCALLFUNC || n.(*ir.CallExpr).X.Op() != ir.OFUNCINST {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// We have found a function call using a generic function
|
||||||
|
// instantiation.
|
||||||
|
call := n.(*ir.CallExpr)
|
||||||
|
inst := call.X.(*ir.InstExpr)
|
||||||
|
sym := makeInstName(inst)
|
||||||
|
//fmt.Printf("Found generic func call in %v to %v\n", f, s)
|
||||||
|
st := g.target.Stencils[sym]
|
||||||
|
if st == nil {
|
||||||
|
// If instantiation doesn't exist yet, create it and add
|
||||||
|
// to the list of decls.
|
||||||
|
st = genericSubst(sym, inst)
|
||||||
|
g.target.Stencils[sym] = st
|
||||||
|
g.target.Decls = append(g.target.Decls, st)
|
||||||
|
if base.Flag.W > 1 {
|
||||||
|
ir.Dump(fmt.Sprintf("\nstenciled %v", st), st)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Replace the OFUNCINST with a direct reference to the
|
||||||
|
// new stenciled function
|
||||||
|
call.X = st.Nname
|
||||||
|
if inst.X.Op() == ir.OCALLPART {
|
||||||
|
// When we create an instantiation of a method
|
||||||
|
// call, we make it a function. So, move the
|
||||||
|
// receiver to be the first arg of the function
|
||||||
|
// call.
|
||||||
|
withRecv := make([]ir.Node, len(call.Args)+1)
|
||||||
|
dot := inst.X.(*ir.SelectorExpr)
|
||||||
|
withRecv[0] = dot.X
|
||||||
|
copy(withRecv[1:], call.Args)
|
||||||
|
call.Args = withRecv
|
||||||
|
}
|
||||||
|
modified = true
|
||||||
|
})
|
||||||
|
if base.Flag.W > 1 && modified {
|
||||||
|
ir.Dump(fmt.Sprintf("\nmodified %v", decl), decl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeInstName makes the unique name for a stenciled generic function, based on
|
||||||
|
// the name of the function and the types of the type params.
|
||||||
|
func makeInstName(inst *ir.InstExpr) *types.Sym {
|
||||||
|
b := bytes.NewBufferString("#")
|
||||||
|
if meth, ok := inst.X.(*ir.SelectorExpr); ok {
|
||||||
|
// Write the name of the generic method, including receiver type
|
||||||
|
b.WriteString(meth.Selection.Nname.Sym().Name)
|
||||||
|
} else {
|
||||||
|
b.WriteString(inst.X.(*ir.Name).Name().Sym().Name)
|
||||||
|
}
|
||||||
|
b.WriteString("[")
|
||||||
|
for i, targ := range inst.Targs {
|
||||||
|
if i > 0 {
|
||||||
|
b.WriteString(",")
|
||||||
|
}
|
||||||
|
b.WriteString(targ.Type().String())
|
||||||
|
}
|
||||||
|
b.WriteString("]")
|
||||||
|
return typecheck.Lookup(b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct containing info needed for doing the substitution as we create the
|
||||||
|
// instantiation of a generic function with specified type arguments.
|
||||||
|
type subster struct {
|
||||||
|
newf *ir.Func // Func node for the new stenciled function
|
||||||
|
tparams []*types.Field
|
||||||
|
targs []ir.Node
|
||||||
|
// The substitution map from name nodes in the generic function to the
|
||||||
|
// name nodes in the new stenciled function.
|
||||||
|
vars map[*ir.Name]*ir.Name
|
||||||
|
seen map[*types.Type]*types.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// genericSubst returns a new function with the specified name. The function is an
|
||||||
|
// instantiation of a generic function or method with type params, as specified by
|
||||||
|
// inst. For a method with a generic receiver, it returns an instantiated function
|
||||||
|
// type where the receiver becomes the first parameter. Otherwise the instantiated
|
||||||
|
// method would still need to be transformed by later compiler phases.
|
||||||
|
func genericSubst(name *types.Sym, inst *ir.InstExpr) *ir.Func {
|
||||||
|
var nameNode *ir.Name
|
||||||
|
var tparams []*types.Field
|
||||||
|
if selExpr, ok := inst.X.(*ir.SelectorExpr); ok {
|
||||||
|
// Get the type params from the method receiver (after skipping
|
||||||
|
// over any pointer)
|
||||||
|
nameNode = ir.AsNode(selExpr.Selection.Nname).(*ir.Name)
|
||||||
|
recvType := selExpr.Type().Recv().Type
|
||||||
|
if recvType.IsPtr() {
|
||||||
|
recvType = recvType.Elem()
|
||||||
|
}
|
||||||
|
tparams = make([]*types.Field, len(recvType.RParams))
|
||||||
|
for i, rparam := range recvType.RParams {
|
||||||
|
tparams[i] = types.NewField(src.NoXPos, nil, rparam)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nameNode = inst.X.(*ir.Name)
|
||||||
|
tparams = nameNode.Type().TParams().Fields().Slice()
|
||||||
|
}
|
||||||
|
gf := nameNode.Func
|
||||||
|
newf := ir.NewFunc(inst.Pos())
|
||||||
|
newf.Nname = ir.NewNameAt(inst.Pos(), name)
|
||||||
|
newf.Nname.Func = newf
|
||||||
|
newf.Nname.Defn = newf
|
||||||
|
name.Def = newf.Nname
|
||||||
|
|
||||||
|
subst := &subster{
|
||||||
|
newf: newf,
|
||||||
|
tparams: tparams,
|
||||||
|
targs: inst.Targs,
|
||||||
|
vars: make(map[*ir.Name]*ir.Name),
|
||||||
|
seen: make(map[*types.Type]*types.Type),
|
||||||
|
}
|
||||||
|
|
||||||
|
newf.Dcl = make([]*ir.Name, len(gf.Dcl))
|
||||||
|
for i, n := range gf.Dcl {
|
||||||
|
newf.Dcl[i] = subst.node(n).(*ir.Name)
|
||||||
|
}
|
||||||
|
newf.Body = subst.list(gf.Body)
|
||||||
|
|
||||||
|
// Ugly: we have to insert the Name nodes of the parameters/results into
|
||||||
|
// the function type. The current function type has no Nname fields set,
|
||||||
|
// because it came via conversion from the types2 type.
|
||||||
|
oldt := inst.X.Type()
|
||||||
|
// We also transform a generic method type to the corresponding
|
||||||
|
// instantiated function type where the receiver is the first parameter.
|
||||||
|
newt := types.NewSignature(oldt.Pkg(), nil, nil,
|
||||||
|
subst.fields(ir.PPARAM, append(oldt.Recvs().FieldSlice(), oldt.Params().FieldSlice()...), newf.Dcl),
|
||||||
|
subst.fields(ir.PPARAMOUT, oldt.Results().FieldSlice(), newf.Dcl))
|
||||||
|
|
||||||
|
newf.Nname.Ntype = ir.TypeNode(newt)
|
||||||
|
newf.Nname.SetType(newt)
|
||||||
|
ir.MarkFunc(newf.Nname)
|
||||||
|
newf.SetTypecheck(1)
|
||||||
|
newf.Nname.SetTypecheck(1)
|
||||||
|
// TODO(danscales) - remove later, but avoid confusion for now.
|
||||||
|
newf.Pragma = ir.Noinline
|
||||||
|
return newf
|
||||||
|
}
|
||||||
|
|
||||||
|
// node is like DeepCopy(), but creates distinct ONAME nodes, and also descends
|
||||||
|
// into closures. It substitutes type arguments for type parameters in all the new
|
||||||
|
// nodes.
|
||||||
|
func (subst *subster) node(n ir.Node) ir.Node {
|
||||||
|
// Use closure to capture all state needed by the ir.EditChildren argument.
|
||||||
|
var edit func(ir.Node) ir.Node
|
||||||
|
edit = func(x ir.Node) ir.Node {
|
||||||
|
switch x.Op() {
|
||||||
|
case ir.OTYPE:
|
||||||
|
return ir.TypeNode(subst.typ(x.Type()))
|
||||||
|
|
||||||
|
case ir.ONAME:
|
||||||
|
name := x.(*ir.Name)
|
||||||
|
if v := subst.vars[name]; v != nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
m := ir.NewNameAt(name.Pos(), name.Sym())
|
||||||
|
t := x.Type()
|
||||||
|
newt := subst.typ(t)
|
||||||
|
m.SetType(newt)
|
||||||
|
m.Curfn = subst.newf
|
||||||
|
m.Class = name.Class
|
||||||
|
m.Func = name.Func
|
||||||
|
subst.vars[name] = m
|
||||||
|
m.SetTypecheck(1)
|
||||||
|
return m
|
||||||
|
case ir.OLITERAL, ir.ONIL:
|
||||||
|
if x.Sym() != nil {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m := ir.Copy(x)
|
||||||
|
if _, isExpr := m.(ir.Expr); isExpr {
|
||||||
|
t := x.Type()
|
||||||
|
if t == nil {
|
||||||
|
// t can be nil only if this is a call that has no
|
||||||
|
// return values, so allow that and otherwise give
|
||||||
|
// an error.
|
||||||
|
if _, isCallExpr := m.(*ir.CallExpr); !isCallExpr {
|
||||||
|
base.Fatalf(fmt.Sprintf("Nil type for %v", x))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m.SetType(subst.typ(x.Type()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ir.EditChildren(m, edit)
|
||||||
|
|
||||||
|
if x.Op() == ir.OXDOT {
|
||||||
|
// A method value/call via a type param will have been left as an
|
||||||
|
// OXDOT. When we see this during stenciling, finish the
|
||||||
|
// typechecking, now that we have the instantiated receiver type.
|
||||||
|
// We need to do this now, since the access/selection to the
|
||||||
|
// method for the real type is very different from the selection
|
||||||
|
// for the type param.
|
||||||
|
m.SetTypecheck(0)
|
||||||
|
// m will transform to an OCALLPART
|
||||||
|
typecheck.Expr(m)
|
||||||
|
}
|
||||||
|
if x.Op() == ir.OCALL {
|
||||||
|
call := m.(*ir.CallExpr)
|
||||||
|
if call.X.Op() == ir.OTYPE {
|
||||||
|
// Do typechecking on a conversion, now that we
|
||||||
|
// know the type argument.
|
||||||
|
m.SetTypecheck(0)
|
||||||
|
m = typecheck.Expr(m)
|
||||||
|
} else if call.X.Op() == ir.OCALLPART {
|
||||||
|
// Redo the typechecking, now that we know the method
|
||||||
|
// value is being called.
|
||||||
|
call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
|
||||||
|
call.X.SetTypecheck(0)
|
||||||
|
call.X.SetType(nil)
|
||||||
|
typecheck.Callee(call.X)
|
||||||
|
m.SetTypecheck(0)
|
||||||
|
typecheck.Call(m.(*ir.CallExpr))
|
||||||
|
} else {
|
||||||
|
base.FatalfAt(call.Pos(), "Expecting OCALLPART or OTYPE with CALL")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.Op() == ir.OCLOSURE {
|
||||||
|
x := x.(*ir.ClosureExpr)
|
||||||
|
// Need to save/duplicate x.Func.Nname,
|
||||||
|
// x.Func.Nname.Ntype, x.Func.Dcl, x.Func.ClosureVars, and
|
||||||
|
// x.Func.Body.
|
||||||
|
oldfn := x.Func
|
||||||
|
newfn := ir.NewFunc(oldfn.Pos())
|
||||||
|
if oldfn.ClosureCalled() {
|
||||||
|
newfn.SetClosureCalled(true)
|
||||||
|
}
|
||||||
|
m.(*ir.ClosureExpr).Func = newfn
|
||||||
|
newfn.Nname = ir.NewNameAt(oldfn.Nname.Pos(), oldfn.Nname.Sym())
|
||||||
|
newfn.Nname.SetType(oldfn.Nname.Type())
|
||||||
|
newfn.Nname.Ntype = subst.node(oldfn.Nname.Ntype).(ir.Ntype)
|
||||||
|
newfn.Body = subst.list(oldfn.Body)
|
||||||
|
// Make shallow copy of the Dcl and ClosureVar slices
|
||||||
|
newfn.Dcl = append([]*ir.Name(nil), oldfn.Dcl...)
|
||||||
|
newfn.ClosureVars = append([]*ir.Name(nil), oldfn.ClosureVars...)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
return edit(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (subst *subster) list(l []ir.Node) []ir.Node {
|
||||||
|
s := make([]ir.Node, len(l))
|
||||||
|
for i, n := range l {
|
||||||
|
s[i] = subst.node(n)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// tstruct substitutes type params in a structure type
|
||||||
|
func (subst *subster) tstruct(t *types.Type) *types.Type {
|
||||||
|
if t.NumFields() == 0 {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
var newfields []*types.Field
|
||||||
|
for i, f := range t.Fields().Slice() {
|
||||||
|
t2 := subst.typ(f.Type)
|
||||||
|
if t2 != f.Type && newfields == nil {
|
||||||
|
newfields = make([]*types.Field, t.NumFields())
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
newfields[j] = t.Field(j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newfields != nil {
|
||||||
|
newfields[i] = types.NewField(f.Pos, f.Sym, t2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newfields != nil {
|
||||||
|
return types.NewStruct(t.Pkg(), newfields)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// instTypeName creates a name for an instantiated type, based on the type args
|
||||||
|
func instTypeName(name string, targs []ir.Node) string {
|
||||||
|
b := bytes.NewBufferString(name)
|
||||||
|
b.WriteByte('[')
|
||||||
|
for i, targ := range targs {
|
||||||
|
if i > 0 {
|
||||||
|
b.WriteByte(',')
|
||||||
|
}
|
||||||
|
b.WriteString(targ.Type().String())
|
||||||
|
}
|
||||||
|
b.WriteByte(']')
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// typ computes the type obtained by substituting any type parameter in t with the
|
||||||
|
// corresponding type argument in subst. If t contains no type parameters, the
|
||||||
|
// result is t; otherwise the result is a new type.
|
||||||
|
// It deals with recursive types by using a map and TFORW types.
|
||||||
|
// TODO(danscales) deal with recursion besides ptr/struct cases.
|
||||||
|
func (subst *subster) typ(t *types.Type) *types.Type {
|
||||||
|
if !t.HasTParam() {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
if subst.seen[t] != nil {
|
||||||
|
// We've hit a recursive type
|
||||||
|
return subst.seen[t]
|
||||||
|
}
|
||||||
|
|
||||||
|
var newt *types.Type
|
||||||
|
switch t.Kind() {
|
||||||
|
case types.TTYPEPARAM:
|
||||||
|
for i, tp := range subst.tparams {
|
||||||
|
if tp.Type == t {
|
||||||
|
return subst.targs[i].Type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
|
||||||
|
case types.TARRAY:
|
||||||
|
elem := t.Elem()
|
||||||
|
newelem := subst.typ(elem)
|
||||||
|
if newelem != elem {
|
||||||
|
newt = types.NewArray(newelem, t.NumElem())
|
||||||
|
}
|
||||||
|
|
||||||
|
case types.TPTR:
|
||||||
|
elem := t.Elem()
|
||||||
|
// In order to deal with recursive generic types, create a TFORW
|
||||||
|
// type initially and store it in the seen map, so it can be
|
||||||
|
// accessed if this type appears recursively within the type.
|
||||||
|
forw := types.New(types.TFORW)
|
||||||
|
subst.seen[t] = forw
|
||||||
|
newelem := subst.typ(elem)
|
||||||
|
if newelem != elem {
|
||||||
|
forw.SetUnderlying(types.NewPtr(newelem))
|
||||||
|
newt = forw
|
||||||
|
}
|
||||||
|
delete(subst.seen, t)
|
||||||
|
|
||||||
|
case types.TSLICE:
|
||||||
|
elem := t.Elem()
|
||||||
|
newelem := subst.typ(elem)
|
||||||
|
if newelem != elem {
|
||||||
|
newt = types.NewSlice(newelem)
|
||||||
|
}
|
||||||
|
|
||||||
|
case types.TSTRUCT:
|
||||||
|
forw := types.New(types.TFORW)
|
||||||
|
subst.seen[t] = forw
|
||||||
|
newt = subst.tstruct(t)
|
||||||
|
if newt != t {
|
||||||
|
forw.SetUnderlying(newt)
|
||||||
|
newt = forw
|
||||||
|
}
|
||||||
|
delete(subst.seen, t)
|
||||||
|
|
||||||
|
case types.TFUNC:
|
||||||
|
newrecvs := subst.tstruct(t.Recvs())
|
||||||
|
newparams := subst.tstruct(t.Params())
|
||||||
|
newresults := subst.tstruct(t.Results())
|
||||||
|
if newrecvs != t.Recvs() || newparams != t.Params() || newresults != t.Results() {
|
||||||
|
var newrecv *types.Field
|
||||||
|
if newrecvs.NumFields() > 0 {
|
||||||
|
newrecv = newrecvs.Field(0)
|
||||||
|
}
|
||||||
|
newt = types.NewSignature(t.Pkg(), newrecv, nil, newparams.FieldSlice(), newresults.FieldSlice())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: case TCHAN
|
||||||
|
// TODO: case TMAP
|
||||||
|
// TODO: case TINTER
|
||||||
|
}
|
||||||
|
if newt != nil {
|
||||||
|
if t.Sym() != nil {
|
||||||
|
// Since we've substituted types, we also need to change
|
||||||
|
// the defined name of the type, by removing the old types
|
||||||
|
// (in brackets) from the name, and adding the new types.
|
||||||
|
oldname := t.Sym().Name
|
||||||
|
i := strings.Index(oldname, "[")
|
||||||
|
oldname = oldname[:i]
|
||||||
|
sym := t.Sym().Pkg.Lookup(instTypeName(oldname, subst.targs))
|
||||||
|
if sym.Def != nil {
|
||||||
|
// We've already created this instantiated defined type.
|
||||||
|
return sym.Def.Type()
|
||||||
|
}
|
||||||
|
newt.SetSym(sym)
|
||||||
|
sym.Def = ir.TypeNode(newt)
|
||||||
|
}
|
||||||
|
return newt
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// fields sets the Nname field for the Field nodes inside a type signature, based
|
||||||
|
// on the corresponding in/out parameters in dcl. It depends on the in and out
|
||||||
|
// parameters being in order in dcl.
|
||||||
|
func (subst *subster) fields(class ir.Class, oldfields []*types.Field, dcl []*ir.Name) []*types.Field {
|
||||||
|
newfields := make([]*types.Field, len(oldfields))
|
||||||
|
var i int
|
||||||
|
|
||||||
|
// Find the starting index in dcl of declarations of the class (either
|
||||||
|
// PPARAM or PPARAMOUT).
|
||||||
|
for i = range dcl {
|
||||||
|
if dcl[i].Class == class {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create newfields nodes that are copies of the oldfields nodes, but
|
||||||
|
// with substitution for any type params, and with Nname set to be the node in
|
||||||
|
// Dcl for the corresponding PPARAM or PPARAMOUT.
|
||||||
|
for j := range oldfields {
|
||||||
|
newfields[j] = oldfields[j].Copy()
|
||||||
|
newfields[j].Type = subst.typ(oldfields[j].Type)
|
||||||
|
newfields[j].Nname = dcl[i]
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return newfields
|
||||||
|
}
|
||||||
303
src/cmd/compile/internal/noder/stmt.go
Normal file
303
src/cmd/compile/internal/noder/stmt.go
Normal file
|
|
@ -0,0 +1,303 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package noder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/compile/internal/ir"
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"cmd/compile/internal/typecheck"
|
||||||
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/internal/src"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (g *irgen) stmts(stmts []syntax.Stmt) []ir.Node {
|
||||||
|
var nodes []ir.Node
|
||||||
|
for _, stmt := range stmts {
|
||||||
|
switch s := g.stmt(stmt).(type) {
|
||||||
|
case nil: // EmptyStmt
|
||||||
|
case *ir.BlockStmt:
|
||||||
|
nodes = append(nodes, s.List...)
|
||||||
|
default:
|
||||||
|
nodes = append(nodes, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) stmt(stmt syntax.Stmt) ir.Node {
|
||||||
|
// TODO(mdempsky): Remove dependency on typecheck.
|
||||||
|
return typecheck.Stmt(g.stmt0(stmt))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) stmt0(stmt syntax.Stmt) ir.Node {
|
||||||
|
switch stmt := stmt.(type) {
|
||||||
|
case nil, *syntax.EmptyStmt:
|
||||||
|
return nil
|
||||||
|
case *syntax.LabeledStmt:
|
||||||
|
return g.labeledStmt(stmt)
|
||||||
|
case *syntax.BlockStmt:
|
||||||
|
return ir.NewBlockStmt(g.pos(stmt), g.blockStmt(stmt))
|
||||||
|
case *syntax.ExprStmt:
|
||||||
|
x := g.expr(stmt.X)
|
||||||
|
if call, ok := x.(*ir.CallExpr); ok {
|
||||||
|
call.Use = ir.CallUseStmt
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
case *syntax.SendStmt:
|
||||||
|
return ir.NewSendStmt(g.pos(stmt), g.expr(stmt.Chan), g.expr(stmt.Value))
|
||||||
|
case *syntax.DeclStmt:
|
||||||
|
return ir.NewBlockStmt(g.pos(stmt), g.decls(stmt.DeclList))
|
||||||
|
|
||||||
|
case *syntax.AssignStmt:
|
||||||
|
if stmt.Op != 0 && stmt.Op != syntax.Def {
|
||||||
|
op := g.op(stmt.Op, binOps[:])
|
||||||
|
if stmt.Rhs == nil {
|
||||||
|
return IncDec(g.pos(stmt), op, g.expr(stmt.Lhs))
|
||||||
|
}
|
||||||
|
return ir.NewAssignOpStmt(g.pos(stmt), op, g.expr(stmt.Lhs), g.expr(stmt.Rhs))
|
||||||
|
}
|
||||||
|
|
||||||
|
names, lhs := g.assignList(stmt.Lhs, stmt.Op == syntax.Def)
|
||||||
|
rhs := g.exprList(stmt.Rhs)
|
||||||
|
|
||||||
|
if len(lhs) == 1 && len(rhs) == 1 {
|
||||||
|
n := ir.NewAssignStmt(g.pos(stmt), lhs[0], rhs[0])
|
||||||
|
n.Def = initDefn(n, names)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
n := ir.NewAssignListStmt(g.pos(stmt), ir.OAS2, lhs, rhs)
|
||||||
|
n.Def = initDefn(n, names)
|
||||||
|
return n
|
||||||
|
|
||||||
|
case *syntax.BranchStmt:
|
||||||
|
return ir.NewBranchStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), branchOps[:]), g.name(stmt.Label))
|
||||||
|
case *syntax.CallStmt:
|
||||||
|
return ir.NewGoDeferStmt(g.pos(stmt), g.tokOp(int(stmt.Tok), callOps[:]), g.expr(stmt.Call))
|
||||||
|
case *syntax.ReturnStmt:
|
||||||
|
return ir.NewReturnStmt(g.pos(stmt), g.exprList(stmt.Results))
|
||||||
|
case *syntax.IfStmt:
|
||||||
|
return g.ifStmt(stmt)
|
||||||
|
case *syntax.ForStmt:
|
||||||
|
return g.forStmt(stmt)
|
||||||
|
case *syntax.SelectStmt:
|
||||||
|
return g.selectStmt(stmt)
|
||||||
|
case *syntax.SwitchStmt:
|
||||||
|
return g.switchStmt(stmt)
|
||||||
|
|
||||||
|
default:
|
||||||
|
g.unhandled("statement", stmt)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(mdempsky): Investigate replacing with switch statements or dense arrays.
|
||||||
|
|
||||||
|
var branchOps = [...]ir.Op{
|
||||||
|
syntax.Break: ir.OBREAK,
|
||||||
|
syntax.Continue: ir.OCONTINUE,
|
||||||
|
syntax.Fallthrough: ir.OFALL,
|
||||||
|
syntax.Goto: ir.OGOTO,
|
||||||
|
}
|
||||||
|
|
||||||
|
var callOps = [...]ir.Op{
|
||||||
|
syntax.Defer: ir.ODEFER,
|
||||||
|
syntax.Go: ir.OGO,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) tokOp(tok int, ops []ir.Op) ir.Op {
|
||||||
|
// TODO(mdempsky): Validate.
|
||||||
|
return ops[tok]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) op(op syntax.Operator, ops []ir.Op) ir.Op {
|
||||||
|
// TODO(mdempsky): Validate.
|
||||||
|
return ops[op]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) assignList(expr syntax.Expr, def bool) ([]*ir.Name, []ir.Node) {
|
||||||
|
if !def {
|
||||||
|
return nil, g.exprList(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var exprs []syntax.Expr
|
||||||
|
if list, ok := expr.(*syntax.ListExpr); ok {
|
||||||
|
exprs = list.ElemList
|
||||||
|
} else {
|
||||||
|
exprs = []syntax.Expr{expr}
|
||||||
|
}
|
||||||
|
|
||||||
|
var names []*ir.Name
|
||||||
|
res := make([]ir.Node, len(exprs))
|
||||||
|
for i, expr := range exprs {
|
||||||
|
expr := expr.(*syntax.Name)
|
||||||
|
if expr.Value == "_" {
|
||||||
|
res[i] = ir.BlankNode
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj, ok := g.info.Uses[expr]; ok {
|
||||||
|
res[i] = g.obj(obj)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name, _ := g.def(expr)
|
||||||
|
names = append(names, name)
|
||||||
|
res[i] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
return names, res
|
||||||
|
}
|
||||||
|
|
||||||
|
// initDefn marks the given names as declared by defn and populates
|
||||||
|
// its Init field with ODCL nodes. It then reports whether any names
|
||||||
|
// were so declared, which can be used to initialize defn.Def.
|
||||||
|
func initDefn(defn ir.InitNode, names []*ir.Name) bool {
|
||||||
|
if len(names) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
init := make([]ir.Node, len(names))
|
||||||
|
for i, name := range names {
|
||||||
|
name.Defn = defn
|
||||||
|
init[i] = ir.NewDecl(name.Pos(), ir.ODCL, name)
|
||||||
|
}
|
||||||
|
defn.SetInit(init)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) blockStmt(stmt *syntax.BlockStmt) []ir.Node {
|
||||||
|
return g.stmts(stmt.List)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) ifStmt(stmt *syntax.IfStmt) ir.Node {
|
||||||
|
init := g.stmt(stmt.Init)
|
||||||
|
n := ir.NewIfStmt(g.pos(stmt), g.expr(stmt.Cond), g.blockStmt(stmt.Then), nil)
|
||||||
|
if stmt.Else != nil {
|
||||||
|
e := g.stmt(stmt.Else)
|
||||||
|
if e.Op() == ir.OBLOCK {
|
||||||
|
e := e.(*ir.BlockStmt)
|
||||||
|
n.Else = e.List
|
||||||
|
} else {
|
||||||
|
n.Else = []ir.Node{e}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return g.init(init, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpackTwo returns the first two nodes in list. If list has fewer
|
||||||
|
// than 2 nodes, then the missing nodes are replaced with nils.
|
||||||
|
func unpackTwo(list []ir.Node) (fst, snd ir.Node) {
|
||||||
|
switch len(list) {
|
||||||
|
case 0:
|
||||||
|
return nil, nil
|
||||||
|
case 1:
|
||||||
|
return list[0], nil
|
||||||
|
default:
|
||||||
|
return list[0], list[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) forStmt(stmt *syntax.ForStmt) ir.Node {
|
||||||
|
if r, ok := stmt.Init.(*syntax.RangeClause); ok {
|
||||||
|
names, lhs := g.assignList(r.Lhs, r.Def)
|
||||||
|
key, value := unpackTwo(lhs)
|
||||||
|
n := ir.NewRangeStmt(g.pos(r), key, value, g.expr(r.X), g.blockStmt(stmt.Body))
|
||||||
|
n.Def = initDefn(n, names)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
return ir.NewForStmt(g.pos(stmt), g.stmt(stmt.Init), g.expr(stmt.Cond), g.stmt(stmt.Post), g.blockStmt(stmt.Body))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) selectStmt(stmt *syntax.SelectStmt) ir.Node {
|
||||||
|
body := make([]*ir.CommClause, len(stmt.Body))
|
||||||
|
for i, clause := range stmt.Body {
|
||||||
|
body[i] = ir.NewCommStmt(g.pos(clause), g.stmt(clause.Comm), g.stmts(clause.Body))
|
||||||
|
}
|
||||||
|
return ir.NewSelectStmt(g.pos(stmt), body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) switchStmt(stmt *syntax.SwitchStmt) ir.Node {
|
||||||
|
pos := g.pos(stmt)
|
||||||
|
init := g.stmt(stmt.Init)
|
||||||
|
|
||||||
|
var expr ir.Node
|
||||||
|
switch tag := stmt.Tag.(type) {
|
||||||
|
case *syntax.TypeSwitchGuard:
|
||||||
|
var ident *ir.Ident
|
||||||
|
if tag.Lhs != nil {
|
||||||
|
ident = ir.NewIdent(g.pos(tag.Lhs), g.name(tag.Lhs))
|
||||||
|
}
|
||||||
|
expr = ir.NewTypeSwitchGuard(pos, ident, g.expr(tag.X))
|
||||||
|
default:
|
||||||
|
expr = g.expr(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := make([]*ir.CaseClause, len(stmt.Body))
|
||||||
|
for i, clause := range stmt.Body {
|
||||||
|
// Check for an implicit clause variable before
|
||||||
|
// visiting body, because it may contain function
|
||||||
|
// literals that reference it, and then it'll be
|
||||||
|
// associated to the wrong function.
|
||||||
|
//
|
||||||
|
// Also, override its position to the clause's colon, so that
|
||||||
|
// dwarfgen can find the right scope for it later.
|
||||||
|
// TODO(mdempsky): We should probably just store the scope
|
||||||
|
// directly in the ir.Name.
|
||||||
|
var cv *ir.Name
|
||||||
|
if obj, ok := g.info.Implicits[clause]; ok {
|
||||||
|
cv = g.obj(obj)
|
||||||
|
cv.SetPos(g.makeXPos(clause.Colon))
|
||||||
|
}
|
||||||
|
body[i] = ir.NewCaseStmt(g.pos(clause), g.exprList(clause.Cases), g.stmts(clause.Body))
|
||||||
|
body[i].Var = cv
|
||||||
|
}
|
||||||
|
|
||||||
|
return g.init(init, ir.NewSwitchStmt(pos, expr, body))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) labeledStmt(label *syntax.LabeledStmt) ir.Node {
|
||||||
|
sym := g.name(label.Label)
|
||||||
|
lhs := ir.NewLabelStmt(g.pos(label), sym)
|
||||||
|
ls := g.stmt(label.Stmt)
|
||||||
|
|
||||||
|
// Attach label directly to control statement too.
|
||||||
|
switch ls := ls.(type) {
|
||||||
|
case *ir.ForStmt:
|
||||||
|
ls.Label = sym
|
||||||
|
case *ir.RangeStmt:
|
||||||
|
ls.Label = sym
|
||||||
|
case *ir.SelectStmt:
|
||||||
|
ls.Label = sym
|
||||||
|
case *ir.SwitchStmt:
|
||||||
|
ls.Label = sym
|
||||||
|
}
|
||||||
|
|
||||||
|
l := []ir.Node{lhs}
|
||||||
|
if ls != nil {
|
||||||
|
if ls.Op() == ir.OBLOCK {
|
||||||
|
ls := ls.(*ir.BlockStmt)
|
||||||
|
l = append(l, ls.List...)
|
||||||
|
} else {
|
||||||
|
l = append(l, ls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ir.NewBlockStmt(src.NoXPos, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) init(init ir.Node, stmt ir.InitNode) ir.InitNode {
|
||||||
|
if init != nil {
|
||||||
|
stmt.SetInit([]ir.Node{init})
|
||||||
|
}
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) name(name *syntax.Name) *types.Sym {
|
||||||
|
if name == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return typecheck.Lookup(name.Value)
|
||||||
|
}
|
||||||
328
src/cmd/compile/internal/noder/types.go
Normal file
328
src/cmd/compile/internal/noder/types.go
Normal file
|
|
@ -0,0 +1,328 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package noder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cmd/compile/internal/base"
|
||||||
|
"cmd/compile/internal/ir"
|
||||||
|
"cmd/compile/internal/typecheck"
|
||||||
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
|
"cmd/internal/src"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (g *irgen) pkg(pkg *types2.Package) *types.Pkg {
|
||||||
|
switch pkg {
|
||||||
|
case nil:
|
||||||
|
return types.BuiltinPkg
|
||||||
|
case g.self:
|
||||||
|
return types.LocalPkg
|
||||||
|
case types2.Unsafe:
|
||||||
|
return ir.Pkgs.Unsafe
|
||||||
|
}
|
||||||
|
return types.NewPkg(pkg.Path(), pkg.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
// typ converts a types2.Type to a types.Type, including caching of previously
|
||||||
|
// translated types.
|
||||||
|
func (g *irgen) typ(typ types2.Type) *types.Type {
|
||||||
|
// Caching type mappings isn't strictly needed, because typ0 preserves
|
||||||
|
// type identity; but caching minimizes memory blow-up from mapping the
|
||||||
|
// same composite type multiple times, and also plays better with the
|
||||||
|
// current state of cmd/compile (e.g., haphazard calculation of type
|
||||||
|
// sizes).
|
||||||
|
res, ok := g.typs[typ]
|
||||||
|
if !ok {
|
||||||
|
res = g.typ0(typ)
|
||||||
|
g.typs[typ] = res
|
||||||
|
|
||||||
|
// Ensure we calculate the size for all concrete types seen by
|
||||||
|
// the frontend. This is another heavy hammer for something that
|
||||||
|
// should really be the backend's responsibility instead.
|
||||||
|
if res != nil && !res.IsUntyped() && !res.IsFuncArgStruct() {
|
||||||
|
types.CheckSize(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// instTypeName2 creates a name for an instantiated type, base on the type args
|
||||||
|
// (given as types2 types).
|
||||||
|
func instTypeName2(name string, targs []types2.Type) string {
|
||||||
|
b := bytes.NewBufferString(name)
|
||||||
|
b.WriteByte('[')
|
||||||
|
for i, targ := range targs {
|
||||||
|
if i > 0 {
|
||||||
|
b.WriteByte(',')
|
||||||
|
}
|
||||||
|
b.WriteString(types2.TypeString(targ,
|
||||||
|
func(*types2.Package) string { return "" }))
|
||||||
|
}
|
||||||
|
b.WriteByte(']')
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// typ0 converts a types2.Type to a types.Type, but doesn't do the caching check
|
||||||
|
// at the top level.
|
||||||
|
func (g *irgen) typ0(typ types2.Type) *types.Type {
|
||||||
|
switch typ := typ.(type) {
|
||||||
|
case *types2.Basic:
|
||||||
|
return g.basic(typ)
|
||||||
|
case *types2.Named:
|
||||||
|
if typ.TParams() != nil {
|
||||||
|
// typ is an instantiation of a defined (named) generic type.
|
||||||
|
// This instantiation should also be a defined (named) type.
|
||||||
|
// types2 gives us the substituted type in t.Underlying()
|
||||||
|
// The substituted type may or may not still have type
|
||||||
|
// params. We might, for example, be substituting one type
|
||||||
|
// param for another type param.
|
||||||
|
|
||||||
|
if typ.TArgs() == nil {
|
||||||
|
base.Fatalf("In typ0, Targs should be set if TParams is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// When converted to types.Type, typ must have a name,
|
||||||
|
// based on the names of the type arguments. We need a
|
||||||
|
// name to deal with recursive generic types (and it also
|
||||||
|
// looks better when printing types).
|
||||||
|
instName := instTypeName2(typ.Obj().Name(), typ.TArgs())
|
||||||
|
s := g.pkg(typ.Obj().Pkg()).Lookup(instName)
|
||||||
|
if s.Def != nil {
|
||||||
|
// We have already encountered this instantiation,
|
||||||
|
// so use the type we previously created, since there
|
||||||
|
// must be exactly one instance of a defined type.
|
||||||
|
return s.Def.Type()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a forwarding type first and put it in the g.typs
|
||||||
|
// map, in order to deal with recursive generic types.
|
||||||
|
ntyp := types.New(types.TFORW)
|
||||||
|
g.typs[typ] = ntyp
|
||||||
|
ntyp.SetUnderlying(g.typ(typ.Underlying()))
|
||||||
|
ntyp.SetSym(s)
|
||||||
|
|
||||||
|
if ntyp.HasTParam() {
|
||||||
|
// If ntyp still has type params, then we must be
|
||||||
|
// referencing something like 'value[T2]', as when
|
||||||
|
// specifying the generic receiver of a method,
|
||||||
|
// where value was defined as "type value[T any]
|
||||||
|
// ...". Save the type args, which will now be the
|
||||||
|
// new type params of the current type.
|
||||||
|
ntyp.RParams = make([]*types.Type, len(typ.TArgs()))
|
||||||
|
for i, targ := range typ.TArgs() {
|
||||||
|
ntyp.RParams[i] = g.typ(targ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure instantiated type can be uniquely found from
|
||||||
|
// the sym
|
||||||
|
s.Def = ir.TypeNode(ntyp)
|
||||||
|
return ntyp
|
||||||
|
}
|
||||||
|
obj := g.obj(typ.Obj())
|
||||||
|
if obj.Op() != ir.OTYPE {
|
||||||
|
base.FatalfAt(obj.Pos(), "expected type: %L", obj)
|
||||||
|
}
|
||||||
|
return obj.Type()
|
||||||
|
|
||||||
|
case *types2.Array:
|
||||||
|
return types.NewArray(g.typ(typ.Elem()), typ.Len())
|
||||||
|
case *types2.Chan:
|
||||||
|
return types.NewChan(g.typ(typ.Elem()), dirs[typ.Dir()])
|
||||||
|
case *types2.Map:
|
||||||
|
return types.NewMap(g.typ(typ.Key()), g.typ(typ.Elem()))
|
||||||
|
case *types2.Pointer:
|
||||||
|
return types.NewPtr(g.typ(typ.Elem()))
|
||||||
|
case *types2.Signature:
|
||||||
|
return g.signature(nil, typ)
|
||||||
|
case *types2.Slice:
|
||||||
|
return types.NewSlice(g.typ(typ.Elem()))
|
||||||
|
|
||||||
|
case *types2.Struct:
|
||||||
|
fields := make([]*types.Field, typ.NumFields())
|
||||||
|
for i := range fields {
|
||||||
|
v := typ.Field(i)
|
||||||
|
f := types.NewField(g.pos(v), g.selector(v), g.typ(v.Type()))
|
||||||
|
f.Note = typ.Tag(i)
|
||||||
|
if v.Embedded() {
|
||||||
|
f.Embedded = 1
|
||||||
|
}
|
||||||
|
fields[i] = f
|
||||||
|
}
|
||||||
|
return types.NewStruct(g.tpkg(typ), fields)
|
||||||
|
|
||||||
|
case *types2.Interface:
|
||||||
|
embeddeds := make([]*types.Field, typ.NumEmbeddeds())
|
||||||
|
for i := range embeddeds {
|
||||||
|
// TODO(mdempsky): Get embedding position.
|
||||||
|
e := typ.EmbeddedType(i)
|
||||||
|
embeddeds[i] = types.NewField(src.NoXPos, nil, g.typ(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
methods := make([]*types.Field, typ.NumExplicitMethods())
|
||||||
|
for i := range methods {
|
||||||
|
m := typ.ExplicitMethod(i)
|
||||||
|
mtyp := g.signature(typecheck.FakeRecv(), m.Type().(*types2.Signature))
|
||||||
|
methods[i] = types.NewField(g.pos(m), g.selector(m), mtyp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.NewInterface(g.tpkg(typ), append(embeddeds, methods...))
|
||||||
|
|
||||||
|
case *types2.TypeParam:
|
||||||
|
tp := types.NewTypeParam(g.tpkg(typ), g.typ(typ.Bound()))
|
||||||
|
// Save the name of the type parameter in the sym of the type.
|
||||||
|
tp.SetSym(g.sym(typ.Obj()))
|
||||||
|
return tp
|
||||||
|
|
||||||
|
case *types2.Tuple:
|
||||||
|
// Tuples are used for the type of a function call (i.e. the
|
||||||
|
// return value of the function).
|
||||||
|
if typ == nil {
|
||||||
|
return (*types.Type)(nil)
|
||||||
|
}
|
||||||
|
fields := make([]*types.Field, typ.Len())
|
||||||
|
for i := range fields {
|
||||||
|
fields[i] = g.param(typ.At(i))
|
||||||
|
}
|
||||||
|
t := types.NewStruct(types.LocalPkg, fields)
|
||||||
|
types.CheckSize(t)
|
||||||
|
// Can only set after doing the types.CheckSize()
|
||||||
|
t.StructType().Funarg = types.FunargResults
|
||||||
|
return t
|
||||||
|
|
||||||
|
default:
|
||||||
|
base.FatalfAt(src.NoXPos, "unhandled type: %v (%T)", typ, typ)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) signature(recv *types.Field, sig *types2.Signature) *types.Type {
|
||||||
|
tparams2 := sig.TParams()
|
||||||
|
tparams := make([]*types.Field, len(tparams2))
|
||||||
|
for i := range tparams {
|
||||||
|
tp := tparams2[i]
|
||||||
|
tparams[i] = types.NewField(g.pos(tp), g.sym(tp), g.typ(tp.Type()))
|
||||||
|
}
|
||||||
|
|
||||||
|
do := func(typ *types2.Tuple) []*types.Field {
|
||||||
|
fields := make([]*types.Field, typ.Len())
|
||||||
|
for i := range fields {
|
||||||
|
fields[i] = g.param(typ.At(i))
|
||||||
|
}
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
params := do(sig.Params())
|
||||||
|
results := do(sig.Results())
|
||||||
|
if sig.Variadic() {
|
||||||
|
params[len(params)-1].SetIsDDD(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.NewSignature(g.tpkg(sig), recv, tparams, params, results)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) param(v *types2.Var) *types.Field {
|
||||||
|
return types.NewField(g.pos(v), g.sym(v), g.typ(v.Type()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) sym(obj types2.Object) *types.Sym {
|
||||||
|
if name := obj.Name(); name != "" {
|
||||||
|
return g.pkg(obj.Pkg()).Lookup(obj.Name())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) selector(obj types2.Object) *types.Sym {
|
||||||
|
pkg, name := g.pkg(obj.Pkg()), obj.Name()
|
||||||
|
if types.IsExported(name) {
|
||||||
|
pkg = types.LocalPkg
|
||||||
|
}
|
||||||
|
return pkg.Lookup(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tpkg returns the package that a function, interface, or struct type
|
||||||
|
// expression appeared in.
|
||||||
|
//
|
||||||
|
// Caveat: For the degenerate types "func()", "interface{}", and
|
||||||
|
// "struct{}", tpkg always returns LocalPkg. However, we only need the
|
||||||
|
// package information so that go/types can report it via its API, and
|
||||||
|
// the reason we fail to return the original package for these
|
||||||
|
// particular types is because go/types does *not* report it for
|
||||||
|
// them. So in practice this limitation is probably moot.
|
||||||
|
func (g *irgen) tpkg(typ types2.Type) *types.Pkg {
|
||||||
|
anyObj := func() types2.Object {
|
||||||
|
switch typ := typ.(type) {
|
||||||
|
case *types2.Signature:
|
||||||
|
if recv := typ.Recv(); recv != nil {
|
||||||
|
return recv
|
||||||
|
}
|
||||||
|
if params := typ.Params(); params.Len() > 0 {
|
||||||
|
return params.At(0)
|
||||||
|
}
|
||||||
|
if results := typ.Results(); results.Len() > 0 {
|
||||||
|
return results.At(0)
|
||||||
|
}
|
||||||
|
case *types2.Struct:
|
||||||
|
if typ.NumFields() > 0 {
|
||||||
|
return typ.Field(0)
|
||||||
|
}
|
||||||
|
case *types2.Interface:
|
||||||
|
if typ.NumExplicitMethods() > 0 {
|
||||||
|
return typ.ExplicitMethod(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj := anyObj(); obj != nil {
|
||||||
|
return g.pkg(obj.Pkg())
|
||||||
|
}
|
||||||
|
return types.LocalPkg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) basic(typ *types2.Basic) *types.Type {
|
||||||
|
switch typ.Name() {
|
||||||
|
case "byte":
|
||||||
|
return types.ByteType
|
||||||
|
case "rune":
|
||||||
|
return types.RuneType
|
||||||
|
}
|
||||||
|
return *basics[typ.Kind()]
|
||||||
|
}
|
||||||
|
|
||||||
|
var basics = [...]**types.Type{
|
||||||
|
types2.Invalid: new(*types.Type),
|
||||||
|
types2.Bool: &types.Types[types.TBOOL],
|
||||||
|
types2.Int: &types.Types[types.TINT],
|
||||||
|
types2.Int8: &types.Types[types.TINT8],
|
||||||
|
types2.Int16: &types.Types[types.TINT16],
|
||||||
|
types2.Int32: &types.Types[types.TINT32],
|
||||||
|
types2.Int64: &types.Types[types.TINT64],
|
||||||
|
types2.Uint: &types.Types[types.TUINT],
|
||||||
|
types2.Uint8: &types.Types[types.TUINT8],
|
||||||
|
types2.Uint16: &types.Types[types.TUINT16],
|
||||||
|
types2.Uint32: &types.Types[types.TUINT32],
|
||||||
|
types2.Uint64: &types.Types[types.TUINT64],
|
||||||
|
types2.Uintptr: &types.Types[types.TUINTPTR],
|
||||||
|
types2.Float32: &types.Types[types.TFLOAT32],
|
||||||
|
types2.Float64: &types.Types[types.TFLOAT64],
|
||||||
|
types2.Complex64: &types.Types[types.TCOMPLEX64],
|
||||||
|
types2.Complex128: &types.Types[types.TCOMPLEX128],
|
||||||
|
types2.String: &types.Types[types.TSTRING],
|
||||||
|
types2.UnsafePointer: &types.Types[types.TUNSAFEPTR],
|
||||||
|
types2.UntypedBool: &types.UntypedBool,
|
||||||
|
types2.UntypedInt: &types.UntypedInt,
|
||||||
|
types2.UntypedRune: &types.UntypedRune,
|
||||||
|
types2.UntypedFloat: &types.UntypedFloat,
|
||||||
|
types2.UntypedComplex: &types.UntypedComplex,
|
||||||
|
types2.UntypedString: &types.UntypedString,
|
||||||
|
types2.UntypedNil: &types.Types[types.TNIL],
|
||||||
|
}
|
||||||
|
|
||||||
|
var dirs = [...]types.ChanDir{
|
||||||
|
types2.SendRecv: types.Cboth,
|
||||||
|
types2.SendOnly: types.Csend,
|
||||||
|
types2.RecvOnly: types.Crecv,
|
||||||
|
}
|
||||||
113
src/cmd/compile/internal/noder/validate.go
Normal file
113
src/cmd/compile/internal/noder/validate.go
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package noder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/constant"
|
||||||
|
|
||||||
|
"cmd/compile/internal/base"
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"cmd/compile/internal/types"
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// match reports whether types t1 and t2 are consistent
|
||||||
|
// representations for a given expression's type.
|
||||||
|
func (g *irgen) match(t1 *types.Type, t2 types2.Type, hasOK bool) bool {
|
||||||
|
tuple, ok := t2.(*types2.Tuple)
|
||||||
|
if !ok {
|
||||||
|
// Not a tuple; can use simple type identity comparison.
|
||||||
|
return types.Identical(t1, g.typ(t2))
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasOK {
|
||||||
|
// For has-ok values, types2 represents the expression's type as
|
||||||
|
// a 2-element tuple, whereas ir just uses the first type and
|
||||||
|
// infers that the second type is boolean.
|
||||||
|
return tuple.Len() == 2 && types.Identical(t1, g.typ(tuple.At(0).Type()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if t1 == nil || tuple == nil {
|
||||||
|
return t1 == nil && tuple == nil
|
||||||
|
}
|
||||||
|
if !t1.IsFuncArgStruct() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if t1.NumFields() != tuple.Len() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, result := range t1.FieldSlice() {
|
||||||
|
if !types.Identical(result.Type, g.typ(tuple.At(i).Type())) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) validate(n syntax.Node) {
|
||||||
|
switch n := n.(type) {
|
||||||
|
case *syntax.CallExpr:
|
||||||
|
tv := g.info.Types[n.Fun]
|
||||||
|
if tv.IsBuiltin() {
|
||||||
|
switch builtin := n.Fun.(type) {
|
||||||
|
case *syntax.Name:
|
||||||
|
g.validateBuiltin(builtin.Value, n)
|
||||||
|
case *syntax.SelectorExpr:
|
||||||
|
g.validateBuiltin(builtin.Sel.Value, n)
|
||||||
|
default:
|
||||||
|
g.unhandled("builtin", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *irgen) validateBuiltin(name string, call *syntax.CallExpr) {
|
||||||
|
switch name {
|
||||||
|
case "Alignof", "Offsetof", "Sizeof":
|
||||||
|
// Check that types2+gcSizes calculates sizes the same
|
||||||
|
// as cmd/compile does.
|
||||||
|
|
||||||
|
got, ok := constant.Int64Val(g.info.Types[call].Value)
|
||||||
|
if !ok {
|
||||||
|
base.FatalfAt(g.pos(call), "expected int64 constant value")
|
||||||
|
}
|
||||||
|
|
||||||
|
want := g.unsafeExpr(name, call.ArgList[0])
|
||||||
|
if got != want {
|
||||||
|
base.FatalfAt(g.pos(call), "got %v from types2, but want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsafeExpr evaluates the given unsafe builtin function on arg.
|
||||||
|
func (g *irgen) unsafeExpr(name string, arg syntax.Expr) int64 {
|
||||||
|
switch name {
|
||||||
|
case "Alignof":
|
||||||
|
return g.typ(g.info.Types[arg].Type).Alignment()
|
||||||
|
case "Sizeof":
|
||||||
|
return g.typ(g.info.Types[arg].Type).Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offsetof
|
||||||
|
|
||||||
|
sel := arg.(*syntax.SelectorExpr)
|
||||||
|
selection := g.info.Selections[sel]
|
||||||
|
|
||||||
|
typ := g.typ(g.info.Types[sel.X].Type)
|
||||||
|
if typ.IsPtr() {
|
||||||
|
typ = typ.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset int64
|
||||||
|
for _, i := range selection.Index() {
|
||||||
|
// Ensure field offsets have been calculated.
|
||||||
|
types.CalcSize(typ)
|
||||||
|
|
||||||
|
f := typ.Field(i)
|
||||||
|
offset += f.Offset
|
||||||
|
typ = f.Type
|
||||||
|
}
|
||||||
|
return offset
|
||||||
|
}
|
||||||
|
|
@ -289,7 +289,7 @@ func hashfor(t *types.Type) ir.Node {
|
||||||
|
|
||||||
n := typecheck.NewName(sym)
|
n := typecheck.NewName(sym)
|
||||||
ir.MarkFunc(n)
|
ir.MarkFunc(n)
|
||||||
n.SetType(types.NewSignature(types.NoPkg, nil, []*types.Field{
|
n.SetType(types.NewSignature(types.NoPkg, nil, nil, []*types.Field{
|
||||||
types.NewField(base.Pos, nil, types.NewPtr(t)),
|
types.NewField(base.Pos, nil, types.NewPtr(t)),
|
||||||
types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
|
types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
|
||||||
}, []*types.Field{
|
}, []*types.Field{
|
||||||
|
|
@ -777,7 +777,7 @@ func hashmem(t *types.Type) ir.Node {
|
||||||
|
|
||||||
n := typecheck.NewName(sym)
|
n := typecheck.NewName(sym)
|
||||||
ir.MarkFunc(n)
|
ir.MarkFunc(n)
|
||||||
n.SetType(types.NewSignature(types.NoPkg, nil, []*types.Field{
|
n.SetType(types.NewSignature(types.NoPkg, nil, nil, []*types.Field{
|
||||||
types.NewField(base.Pos, nil, types.NewPtr(t)),
|
types.NewField(base.Pos, nil, types.NewPtr(t)),
|
||||||
types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
|
types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
|
||||||
types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
|
types.NewField(base.Pos, nil, types.Types[types.TUINTPTR]),
|
||||||
|
|
|
||||||
|
|
@ -1288,6 +1288,11 @@ func ITabSym(it *obj.LSym, offset int64) *obj.LSym {
|
||||||
|
|
||||||
// NeedRuntimeType ensures that a runtime type descriptor is emitted for t.
|
// NeedRuntimeType ensures that a runtime type descriptor is emitted for t.
|
||||||
func NeedRuntimeType(t *types.Type) {
|
func NeedRuntimeType(t *types.Type) {
|
||||||
|
if t.HasTParam() {
|
||||||
|
// Generic types don't have a runtime type descriptor (but will
|
||||||
|
// have a dictionary)
|
||||||
|
return
|
||||||
|
}
|
||||||
if _, ok := signatset[t]; !ok {
|
if _, ok := signatset[t]; !ok {
|
||||||
signatset[t] = struct{}{}
|
signatset[t] = struct{}{}
|
||||||
signatslice = append(signatslice, t)
|
signatslice = append(signatslice, t)
|
||||||
|
|
@ -1399,7 +1404,7 @@ func WriteBasicTypes() {
|
||||||
// The latter is the type of an auto-generated wrapper.
|
// The latter is the type of an auto-generated wrapper.
|
||||||
writeType(types.NewPtr(types.ErrorType))
|
writeType(types.NewPtr(types.ErrorType))
|
||||||
|
|
||||||
writeType(types.NewSignature(types.NoPkg, nil, []*types.Field{
|
writeType(types.NewSignature(types.NoPkg, nil, nil, []*types.Field{
|
||||||
types.NewField(base.Pos, nil, types.ErrorType),
|
types.NewField(base.Pos, nil, types.ErrorType),
|
||||||
}, []*types.Field{
|
}, []*types.Field{
|
||||||
types.NewField(base.Pos, nil, types.Types[types.TSTRING]),
|
types.NewField(base.Pos, nil, types.Types[types.TSTRING]),
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ func Fdump(w io.Writer, n Node) (err error) {
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if e := recover(); e != nil {
|
if e := recover(); e != nil {
|
||||||
err = e.(localError).err // re-panics if it's not a localError
|
err = e.(writeError).err // re-panics if it's not a writeError
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -82,16 +82,16 @@ func (p *dumper) Write(data []byte) (n int, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// localError wraps locally caught errors so we can distinguish
|
// writeError wraps locally caught write errors so we can distinguish
|
||||||
// them from genuine panics which we don't want to return as errors.
|
// them from genuine panics which we don't want to return as errors.
|
||||||
type localError struct {
|
type writeError struct {
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
// printf is a convenience wrapper that takes care of print errors.
|
// printf is a convenience wrapper that takes care of print errors.
|
||||||
func (p *dumper) printf(format string, args ...interface{}) {
|
func (p *dumper) printf(format string, args ...interface{}) {
|
||||||
if _, err := fmt.Fprintf(p, format, args...); err != nil {
|
if _, err := fmt.Fprintf(p, format, args...); err != nil {
|
||||||
panic(localError{err})
|
panic(writeError{err})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,10 @@ func testSyntaxErrors(t *testing.T, filename string) {
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
|
var mode Mode
|
||||||
|
if strings.HasSuffix(filename, ".go2") {
|
||||||
|
mode = AllowGenerics
|
||||||
|
}
|
||||||
ParseFile(filename, func(err error) {
|
ParseFile(filename, func(err error) {
|
||||||
e, ok := err.(Error)
|
e, ok := err.(Error)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
@ -162,7 +166,7 @@ func testSyntaxErrors(t *testing.T, filename string) {
|
||||||
} else {
|
} else {
|
||||||
t.Errorf("%s: unexpected error: %s", orig, e.Msg)
|
t.Errorf("%s: unexpected error: %s", orig, e.Msg)
|
||||||
}
|
}
|
||||||
}, nil, 0)
|
}, nil, mode)
|
||||||
|
|
||||||
if *print {
|
if *print {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ type File struct {
|
||||||
Pragma Pragma
|
Pragma Pragma
|
||||||
PkgName *Name
|
PkgName *Name
|
||||||
DeclList []Decl
|
DeclList []Decl
|
||||||
Lines uint
|
EOF Pos
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,7 +56,7 @@ type (
|
||||||
Group *Group // nil means not part of a group
|
Group *Group // nil means not part of a group
|
||||||
Pragma Pragma
|
Pragma Pragma
|
||||||
LocalPkgName *Name // including "."; nil means no rename present
|
LocalPkgName *Name // including "."; nil means no rename present
|
||||||
Path *BasicLit
|
Path *BasicLit // Path.Bad || Path.Kind == StringLit; nil means no path
|
||||||
decl
|
decl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,6 +77,7 @@ type (
|
||||||
Group *Group // nil means not part of a group
|
Group *Group // nil means not part of a group
|
||||||
Pragma Pragma
|
Pragma Pragma
|
||||||
Name *Name
|
Name *Name
|
||||||
|
TParamList []*Field // nil means no type parameters
|
||||||
Alias bool
|
Alias bool
|
||||||
Type Expr
|
Type Expr
|
||||||
decl
|
decl
|
||||||
|
|
@ -102,6 +103,7 @@ type (
|
||||||
Pragma Pragma
|
Pragma Pragma
|
||||||
Recv *Field // nil means regular function
|
Recv *Field // nil means regular function
|
||||||
Name *Name
|
Name *Name
|
||||||
|
TParamList []*Field // nil means no type parameters
|
||||||
Type *FuncType
|
Type *FuncType
|
||||||
Body *BlockStmt // nil means no body (forward declaration)
|
Body *BlockStmt // nil means no body (forward declaration)
|
||||||
decl
|
decl
|
||||||
|
|
@ -120,6 +122,13 @@ type Group struct {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Expressions
|
// Expressions
|
||||||
|
|
||||||
|
func NewName(pos Pos, value string) *Name {
|
||||||
|
n := new(Name)
|
||||||
|
n.pos = pos
|
||||||
|
n.Value = value
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Expr interface {
|
Expr interface {
|
||||||
Node
|
Node
|
||||||
|
|
@ -182,6 +191,7 @@ type (
|
||||||
}
|
}
|
||||||
|
|
||||||
// X[Index]
|
// X[Index]
|
||||||
|
// X[T1, T2, ...] (with Ti = Index.(*ListExpr).ElemList[i])
|
||||||
IndexExpr struct {
|
IndexExpr struct {
|
||||||
X Expr
|
X Expr
|
||||||
Index Expr
|
Index Expr
|
||||||
|
|
@ -272,7 +282,7 @@ type (
|
||||||
|
|
||||||
// interface { MethodList[0]; MethodList[1]; ... }
|
// interface { MethodList[0]; MethodList[1]; ... }
|
||||||
InterfaceType struct {
|
InterfaceType struct {
|
||||||
MethodList []*Field
|
MethodList []*Field // a field named "type" means a type constraint
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -357,7 +367,7 @@ type (
|
||||||
|
|
||||||
AssignStmt struct {
|
AssignStmt struct {
|
||||||
Op Operator // 0 means no operation
|
Op Operator // 0 means no operation
|
||||||
Lhs, Rhs Expr // Rhs == ImplicitOne means Lhs++ (Op == Add) or Lhs-- (Op == Sub)
|
Lhs, Rhs Expr // Rhs == nil means Lhs++ (Op == Add) or Lhs-- (Op == Sub)
|
||||||
simpleStmt
|
simpleStmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -26,10 +26,35 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
func TestParse(t *testing.T) {
|
||||||
ParseFile(*src_, func(err error) { t.Error(err) }, nil, 0)
|
ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStdLib(t *testing.T) {
|
func TestVerify(t *testing.T) {
|
||||||
|
ast, err := ParseFile(*src_, func(err error) { t.Error(err) }, nil, AllowGenerics)
|
||||||
|
if err != nil {
|
||||||
|
return // error already reported
|
||||||
|
}
|
||||||
|
verifyPrint(t, *src_, ast)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseGo2(t *testing.T) {
|
||||||
|
dir := filepath.Join(testdata, "go2")
|
||||||
|
list, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, fi := range list {
|
||||||
|
name := fi.Name()
|
||||||
|
if !fi.IsDir() && !strings.HasPrefix(name, ".") {
|
||||||
|
ParseFile(filepath.Join(dir, name), func(err error) { t.Error(err) }, nil, AllowGenerics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdLib(t *testing.T) { testStdLib(t, 0) }
|
||||||
|
func TestStdLibGeneric(t *testing.T) { testStdLib(t, AllowGenerics) }
|
||||||
|
|
||||||
|
func testStdLib(t *testing.T, mode Mode) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("skipping test in short mode")
|
t.Skip("skipping test in short mode")
|
||||||
}
|
}
|
||||||
|
|
@ -68,15 +93,15 @@ func TestStdLib(t *testing.T) {
|
||||||
if debug {
|
if debug {
|
||||||
fmt.Printf("parsing %s\n", filename)
|
fmt.Printf("parsing %s\n", filename)
|
||||||
}
|
}
|
||||||
ast, err := ParseFile(filename, nil, nil, 0)
|
ast, err := ParseFile(filename, nil, nil, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if *verify {
|
if *verify {
|
||||||
verifyPrint(filename, ast)
|
verifyPrint(t, filename, ast)
|
||||||
}
|
}
|
||||||
results <- parseResult{filename, ast.Lines}
|
results <- parseResult{filename, ast.EOF.Line()}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -142,12 +167,13 @@ func walkDirs(t *testing.T, dir string, action func(string)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyPrint(filename string, ast1 *File) {
|
func verifyPrint(t *testing.T, filename string, ast1 *File) {
|
||||||
var buf1 bytes.Buffer
|
var buf1 bytes.Buffer
|
||||||
_, err := Fprint(&buf1, ast1, true)
|
_, err := Fprint(&buf1, ast1, LineForm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
bytes1 := buf1.Bytes()
|
||||||
|
|
||||||
ast2, err := Parse(NewFileBase(filename), &buf1, nil, nil, 0)
|
ast2, err := Parse(NewFileBase(filename), &buf1, nil, nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -155,20 +181,22 @@ func verifyPrint(filename string, ast1 *File) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf2 bytes.Buffer
|
var buf2 bytes.Buffer
|
||||||
_, err = Fprint(&buf2, ast2, true)
|
_, err = Fprint(&buf2, ast2, LineForm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
bytes2 := buf2.Bytes()
|
||||||
|
|
||||||
if bytes.Compare(buf1.Bytes(), buf2.Bytes()) != 0 {
|
if bytes.Compare(bytes1, bytes2) != 0 {
|
||||||
fmt.Printf("--- %s ---\n", filename)
|
fmt.Printf("--- %s ---\n", filename)
|
||||||
fmt.Printf("%s\n", buf1.Bytes())
|
fmt.Printf("%s\n", bytes1)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
fmt.Printf("--- %s ---\n", filename)
|
fmt.Printf("--- %s ---\n", filename)
|
||||||
fmt.Printf("%s\n", buf2.Bytes())
|
fmt.Printf("%s\n", bytes2)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
panic("not equal")
|
|
||||||
|
t.Error("printed syntax trees do not match")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ func MakePos(base *PosBase, line, col uint) Pos { return Pos{base, sat32(line),
|
||||||
// TODO(gri) IsKnown makes an assumption about linebase < 1.
|
// TODO(gri) IsKnown makes an assumption about linebase < 1.
|
||||||
// Maybe we should check for Base() != nil instead.
|
// Maybe we should check for Base() != nil instead.
|
||||||
|
|
||||||
|
func (pos Pos) Pos() Pos { return pos }
|
||||||
func (pos Pos) IsKnown() bool { return pos.line > 0 }
|
func (pos Pos) IsKnown() bool { return pos.line > 0 }
|
||||||
func (pos Pos) Base() *PosBase { return pos.base }
|
func (pos Pos) Base() *PosBase { return pos.base }
|
||||||
func (pos Pos) Line() uint { return uint(pos.line) }
|
func (pos Pos) Line() uint { return uint(pos.line) }
|
||||||
|
|
|
||||||
|
|
@ -13,19 +13,28 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(gri) Consider removing the linebreaks flag from this signature.
|
// Form controls print formatting.
|
||||||
// Its likely rarely used in common cases.
|
type Form uint
|
||||||
|
|
||||||
func Fprint(w io.Writer, x Node, linebreaks bool) (n int, err error) {
|
const (
|
||||||
|
_ Form = iota // default
|
||||||
|
LineForm // use spaces instead of linebreaks where possible
|
||||||
|
ShortForm // like LineForm but print "…" for non-empty function or composite literal bodies
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fprint prints node x to w in the specified form.
|
||||||
|
// It returns the number of bytes written, and whether there was an error.
|
||||||
|
func Fprint(w io.Writer, x Node, form Form) (n int, err error) {
|
||||||
p := printer{
|
p := printer{
|
||||||
output: w,
|
output: w,
|
||||||
linebreaks: linebreaks,
|
form: form,
|
||||||
|
linebreaks: form == 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
n = p.written
|
n = p.written
|
||||||
if e := recover(); e != nil {
|
if e := recover(); e != nil {
|
||||||
err = e.(localError).err // re-panics if it's not a localError
|
err = e.(writeError).err // re-panics if it's not a writeError
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -35,11 +44,13 @@ func Fprint(w io.Writer, x Node, linebreaks bool) (n int, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String is a convenience functions that prints n in ShortForm
|
||||||
|
// and returns the printed string.
|
||||||
func String(n Node) string {
|
func String(n Node) string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
_, err := Fprint(&buf, n, false)
|
_, err := Fprint(&buf, n, ShortForm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // TODO(gri) print something sensible into buf instead
|
fmt.Fprintf(&buf, "<<< ERROR: %s", err)
|
||||||
}
|
}
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
@ -66,6 +77,7 @@ type whitespace struct {
|
||||||
type printer struct {
|
type printer struct {
|
||||||
output io.Writer
|
output io.Writer
|
||||||
written int // number of bytes written
|
written int // number of bytes written
|
||||||
|
form Form
|
||||||
linebreaks bool // print linebreaks instead of semis
|
linebreaks bool // print linebreaks instead of semis
|
||||||
|
|
||||||
indent int // current indentation level
|
indent int // current indentation level
|
||||||
|
|
@ -81,7 +93,7 @@ func (p *printer) write(data []byte) {
|
||||||
n, err := p.output.Write(data)
|
n, err := p.output.Write(data)
|
||||||
p.written += n
|
p.written += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(localError{err})
|
panic(writeError{err})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -355,18 +367,35 @@ func (p *printer) printRawNode(n Node) {
|
||||||
p.print(_Name, n.Value) // _Name requires actual value following immediately
|
p.print(_Name, n.Value) // _Name requires actual value following immediately
|
||||||
|
|
||||||
case *FuncLit:
|
case *FuncLit:
|
||||||
p.print(n.Type, blank, n.Body)
|
p.print(n.Type, blank)
|
||||||
|
if n.Body != nil {
|
||||||
|
if p.form == ShortForm {
|
||||||
|
p.print(_Lbrace)
|
||||||
|
if len(n.Body.List) > 0 {
|
||||||
|
p.print(_Name, "…")
|
||||||
|
}
|
||||||
|
p.print(_Rbrace)
|
||||||
|
} else {
|
||||||
|
p.print(n.Body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case *CompositeLit:
|
case *CompositeLit:
|
||||||
if n.Type != nil {
|
if n.Type != nil {
|
||||||
p.print(n.Type)
|
p.print(n.Type)
|
||||||
}
|
}
|
||||||
p.print(_Lbrace)
|
p.print(_Lbrace)
|
||||||
|
if p.form == ShortForm {
|
||||||
|
if len(n.ElemList) > 0 {
|
||||||
|
p.print(_Name, "…")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if n.NKeys > 0 && n.NKeys == len(n.ElemList) {
|
if n.NKeys > 0 && n.NKeys == len(n.ElemList) {
|
||||||
p.printExprLines(n.ElemList)
|
p.printExprLines(n.ElemList)
|
||||||
} else {
|
} else {
|
||||||
p.printExprList(n.ElemList)
|
p.printExprList(n.ElemList)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
p.print(_Rbrace)
|
p.print(_Rbrace)
|
||||||
|
|
||||||
case *ParenExpr:
|
case *ParenExpr:
|
||||||
|
|
@ -450,9 +479,13 @@ func (p *printer) printRawNode(n Node) {
|
||||||
}
|
}
|
||||||
p.print(_Lbrace)
|
p.print(_Lbrace)
|
||||||
if len(n.FieldList) > 0 {
|
if len(n.FieldList) > 0 {
|
||||||
|
if p.linebreaks {
|
||||||
p.print(newline, indent)
|
p.print(newline, indent)
|
||||||
p.printFieldList(n.FieldList, n.TagList)
|
p.printFieldList(n.FieldList, n.TagList)
|
||||||
p.print(outdent, newline)
|
p.print(outdent, newline)
|
||||||
|
} else {
|
||||||
|
p.printFieldList(n.FieldList, n.TagList)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p.print(_Rbrace)
|
p.print(_Rbrace)
|
||||||
|
|
||||||
|
|
@ -467,9 +500,13 @@ func (p *printer) printRawNode(n Node) {
|
||||||
}
|
}
|
||||||
p.print(_Lbrace)
|
p.print(_Lbrace)
|
||||||
if len(n.MethodList) > 0 {
|
if len(n.MethodList) > 0 {
|
||||||
|
if p.linebreaks {
|
||||||
p.print(newline, indent)
|
p.print(newline, indent)
|
||||||
p.printMethodList(n.MethodList)
|
p.printMethodList(n.MethodList)
|
||||||
p.print(outdent, newline)
|
p.print(outdent, newline)
|
||||||
|
} else {
|
||||||
|
p.printMethodList(n.MethodList)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p.print(_Rbrace)
|
p.print(_Rbrace)
|
||||||
|
|
||||||
|
|
@ -484,7 +521,15 @@ func (p *printer) printRawNode(n Node) {
|
||||||
if n.Dir == SendOnly {
|
if n.Dir == SendOnly {
|
||||||
p.print(_Arrow)
|
p.print(_Arrow)
|
||||||
}
|
}
|
||||||
p.print(blank, n.Elem)
|
p.print(blank)
|
||||||
|
if e, _ := n.Elem.(*ChanType); n.Dir == 0 && e != nil && e.Dir == RecvOnly {
|
||||||
|
// don't print chan (<-chan T) as chan <-chan T
|
||||||
|
p.print(_Lparen)
|
||||||
|
p.print(n.Elem)
|
||||||
|
p.print(_Rparen)
|
||||||
|
} else {
|
||||||
|
p.print(n.Elem)
|
||||||
|
}
|
||||||
|
|
||||||
// statements
|
// statements
|
||||||
case *DeclStmt:
|
case *DeclStmt:
|
||||||
|
|
@ -504,7 +549,7 @@ func (p *printer) printRawNode(n Node) {
|
||||||
|
|
||||||
case *AssignStmt:
|
case *AssignStmt:
|
||||||
p.print(n.Lhs)
|
p.print(n.Lhs)
|
||||||
if n.Rhs == ImplicitOne {
|
if n.Rhs == nil {
|
||||||
// TODO(gri) This is going to break the mayCombine
|
// TODO(gri) This is going to break the mayCombine
|
||||||
// check once we enable that again.
|
// check once we enable that again.
|
||||||
p.print(n.Op, n.Op) // ++ or --
|
p.print(n.Op, n.Op) // ++ or --
|
||||||
|
|
|
||||||
|
|
@ -25,17 +25,60 @@ func TestPrint(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ast != nil {
|
if ast != nil {
|
||||||
Fprint(testOut(), ast, true)
|
Fprint(testOut(), ast, LineForm)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrintString(t *testing.T) {
|
type shortBuffer struct {
|
||||||
for _, want := range []string{
|
buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *shortBuffer) Write(data []byte) (n int, err error) {
|
||||||
|
w.buf = append(w.buf, data...)
|
||||||
|
n = len(data)
|
||||||
|
if len(w.buf) > 10 {
|
||||||
|
err = io.ErrShortBuffer
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrintError(t *testing.T) {
|
||||||
|
const src = "package p; var x int"
|
||||||
|
ast, err := Parse(nil, strings.NewReader(src), nil, nil, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf shortBuffer
|
||||||
|
_, err = Fprint(&buf, ast, 0)
|
||||||
|
if err == nil || err != io.ErrShortBuffer {
|
||||||
|
t.Errorf("got err = %s, want %s", err, io.ErrShortBuffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stringTests = []string{
|
||||||
"package p",
|
"package p",
|
||||||
"package p; type _ = int; type T1 = struct{}; type ( _ = *struct{}; T2 = float32 )",
|
"package p; type _ int; type T1 = struct{}; type ( _ *struct{}; T2 = float32 )",
|
||||||
|
|
||||||
|
// channels
|
||||||
|
"package p; type _ chan chan int",
|
||||||
|
"package p; type _ chan (<-chan int)",
|
||||||
|
"package p; type _ chan chan<- int",
|
||||||
|
|
||||||
|
"package p; type _ <-chan chan int",
|
||||||
|
"package p; type _ <-chan <-chan int",
|
||||||
|
"package p; type _ <-chan chan<- int",
|
||||||
|
|
||||||
|
"package p; type _ chan<- chan int",
|
||||||
|
"package p; type _ chan<- <-chan int",
|
||||||
|
"package p; type _ chan<- chan<- int",
|
||||||
|
|
||||||
// TODO(gri) expand
|
// TODO(gri) expand
|
||||||
} {
|
}
|
||||||
|
|
||||||
|
func TestPrintString(t *testing.T) {
|
||||||
|
for _, want := range stringTests {
|
||||||
ast, err := Parse(nil, strings.NewReader(want), nil, nil, 0)
|
ast, err := Parse(nil, strings.NewReader(want), nil, nil, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -53,3 +96,90 @@ func testOut() io.Writer {
|
||||||
}
|
}
|
||||||
return ioutil.Discard
|
return ioutil.Discard
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dup(s string) [2]string { return [2]string{s, s} }
|
||||||
|
|
||||||
|
var exprTests = [][2]string{
|
||||||
|
// basic type literals
|
||||||
|
dup("x"),
|
||||||
|
dup("true"),
|
||||||
|
dup("42"),
|
||||||
|
dup("3.1415"),
|
||||||
|
dup("2.71828i"),
|
||||||
|
dup(`'a'`),
|
||||||
|
dup(`"foo"`),
|
||||||
|
dup("`bar`"),
|
||||||
|
|
||||||
|
// func and composite literals
|
||||||
|
dup("func() {}"),
|
||||||
|
dup("[]int{}"),
|
||||||
|
{"func(x int) complex128 { return 0 }", "func(x int) complex128 {…}"},
|
||||||
|
{"[]int{1, 2, 3}", "[]int{…}"},
|
||||||
|
|
||||||
|
// non-type expressions
|
||||||
|
dup("(x)"),
|
||||||
|
dup("x.f"),
|
||||||
|
dup("a[i]"),
|
||||||
|
|
||||||
|
dup("s[:]"),
|
||||||
|
dup("s[i:]"),
|
||||||
|
dup("s[:j]"),
|
||||||
|
dup("s[i:j]"),
|
||||||
|
dup("s[:j:k]"),
|
||||||
|
dup("s[i:j:k]"),
|
||||||
|
|
||||||
|
dup("x.(T)"),
|
||||||
|
|
||||||
|
dup("x.([10]int)"),
|
||||||
|
dup("x.([...]int)"),
|
||||||
|
|
||||||
|
dup("x.(struct{})"),
|
||||||
|
dup("x.(struct{x int; y, z float32; E})"),
|
||||||
|
|
||||||
|
dup("x.(func())"),
|
||||||
|
dup("x.(func(x int))"),
|
||||||
|
dup("x.(func() int)"),
|
||||||
|
dup("x.(func(x, y int, z float32) (r int))"),
|
||||||
|
dup("x.(func(a, b, c int))"),
|
||||||
|
dup("x.(func(x ...T))"),
|
||||||
|
|
||||||
|
dup("x.(interface{})"),
|
||||||
|
dup("x.(interface{m(); n(x int); E})"),
|
||||||
|
dup("x.(interface{m(); n(x int) T; E; F})"),
|
||||||
|
|
||||||
|
dup("x.(map[K]V)"),
|
||||||
|
|
||||||
|
dup("x.(chan E)"),
|
||||||
|
dup("x.(<-chan E)"),
|
||||||
|
dup("x.(chan<- chan int)"),
|
||||||
|
dup("x.(chan<- <-chan int)"),
|
||||||
|
dup("x.(<-chan chan int)"),
|
||||||
|
dup("x.(chan (<-chan int))"),
|
||||||
|
|
||||||
|
dup("f()"),
|
||||||
|
dup("f(x)"),
|
||||||
|
dup("int(x)"),
|
||||||
|
dup("f(x, x + y)"),
|
||||||
|
dup("f(s...)"),
|
||||||
|
dup("f(a, s...)"),
|
||||||
|
|
||||||
|
dup("*x"),
|
||||||
|
dup("&x"),
|
||||||
|
dup("x + y"),
|
||||||
|
dup("x + y << (2 * s)"),
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShortString(t *testing.T) {
|
||||||
|
for _, test := range exprTests {
|
||||||
|
src := "package p; var _ = " + test[0]
|
||||||
|
ast, err := Parse(nil, strings.NewReader(src), nil, nil, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %s", test[0], err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
x := ast.DeclList[0].(*VarDecl).Values
|
||||||
|
if got := String(x); got != test[1] {
|
||||||
|
t.Errorf("%s: got %s, want %s", test[0], got, test[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ type Mode uint
|
||||||
// Modes supported by the parser.
|
// Modes supported by the parser.
|
||||||
const (
|
const (
|
||||||
CheckBranches Mode = 1 << iota // check correct use of labels, break, continue, and goto statements
|
CheckBranches Mode = 1 << iota // check correct use of labels, break, continue, and goto statements
|
||||||
|
AllowGenerics
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error describes a syntax error. Error implements the error interface.
|
// Error describes a syntax error. Error implements the error interface.
|
||||||
|
|
|
||||||
62
src/cmd/compile/internal/syntax/testdata/go2/chans.go2
vendored
Normal file
62
src/cmd/compile/internal/syntax/testdata/go2/chans.go2
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
package chans
|
||||||
|
|
||||||
|
import "runtime"
|
||||||
|
|
||||||
|
// Ranger returns a Sender and a Receiver. The Receiver provides a
|
||||||
|
// Next method to retrieve values. The Sender provides a Send method
|
||||||
|
// to send values and a Close method to stop sending values. The Next
|
||||||
|
// method indicates when the Sender has been closed, and the Send
|
||||||
|
// method indicates when the Receiver has been freed.
|
||||||
|
//
|
||||||
|
// This is a convenient way to exit a goroutine sending values when
|
||||||
|
// the receiver stops reading them.
|
||||||
|
func Ranger[T any]() (*Sender[T], *Receiver[T]) {
|
||||||
|
c := make(chan T)
|
||||||
|
d := make(chan bool)
|
||||||
|
s := &Sender[T]{values: c, done: d}
|
||||||
|
r := &Receiver[T]{values: c, done: d}
|
||||||
|
runtime.SetFinalizer(r, r.finalize)
|
||||||
|
return s, r
|
||||||
|
}
|
||||||
|
|
||||||
|
// A sender is used to send values to a Receiver.
|
||||||
|
type Sender[T any] struct {
|
||||||
|
values chan<- T
|
||||||
|
done <-chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send sends a value to the receiver. It returns whether any more
|
||||||
|
// values may be sent; if it returns false the value was not sent.
|
||||||
|
func (s *Sender[T]) Send(v T) bool {
|
||||||
|
select {
|
||||||
|
case s.values <- v:
|
||||||
|
return true
|
||||||
|
case <-s.done:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close tells the receiver that no more values will arrive.
|
||||||
|
// After Close is called, the Sender may no longer be used.
|
||||||
|
func (s *Sender[T]) Close() {
|
||||||
|
close(s.values)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Receiver receives values from a Sender.
|
||||||
|
type Receiver[T any] struct {
|
||||||
|
values <-chan T
|
||||||
|
done chan<- bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next value from the channel. The bool result
|
||||||
|
// indicates whether the value is valid, or whether the Sender has
|
||||||
|
// been closed and no more values will be received.
|
||||||
|
func (r *Receiver[T]) Next() (T, bool) {
|
||||||
|
v, ok := <-r.values
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// finalize is a finalizer for the receiver.
|
||||||
|
func (r *Receiver[T]) finalize() {
|
||||||
|
close(r.done)
|
||||||
|
}
|
||||||
83
src/cmd/compile/internal/syntax/testdata/go2/linalg.go2
vendored
Normal file
83
src/cmd/compile/internal/syntax/testdata/go2/linalg.go2
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package linalg
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
// Numeric is type bound that matches any numeric type.
|
||||||
|
// It would likely be in a constraints package in the standard library.
|
||||||
|
type Numeric interface {
|
||||||
|
type int, int8, int16, int32, int64,
|
||||||
|
uint, uint8, uint16, uint32, uint64, uintptr,
|
||||||
|
float32, float64,
|
||||||
|
complex64, complex128
|
||||||
|
}
|
||||||
|
|
||||||
|
func DotProduct[T Numeric](s1, s2 []T) T {
|
||||||
|
if len(s1) != len(s2) {
|
||||||
|
panic("DotProduct: slices of unequal length")
|
||||||
|
}
|
||||||
|
var r T
|
||||||
|
for i := range s1 {
|
||||||
|
r += s1[i] * s2[i]
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumericAbs matches numeric types with an Abs method.
|
||||||
|
type NumericAbs[T any] interface {
|
||||||
|
Numeric
|
||||||
|
|
||||||
|
Abs() T
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsDifference computes the absolute value of the difference of
|
||||||
|
// a and b, where the absolute value is determined by the Abs method.
|
||||||
|
func AbsDifference[T NumericAbs[T]](a, b T) T {
|
||||||
|
d := a - b
|
||||||
|
return d.Abs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrderedNumeric is a type bound that matches numeric types that support the < operator.
|
||||||
|
type OrderedNumeric interface {
|
||||||
|
type int, int8, int16, int32, int64,
|
||||||
|
uint, uint8, uint16, uint32, uint64, uintptr,
|
||||||
|
float32, float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complex is a type bound that matches the two complex types, which do not have a < operator.
|
||||||
|
type Complex interface {
|
||||||
|
type complex64, complex128
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrderedAbs is a helper type that defines an Abs method for
|
||||||
|
// ordered numeric types.
|
||||||
|
type OrderedAbs[T OrderedNumeric] T
|
||||||
|
|
||||||
|
func (a OrderedAbs[T]) Abs() OrderedAbs[T] {
|
||||||
|
if a < 0 {
|
||||||
|
return -a
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// ComplexAbs is a helper type that defines an Abs method for
|
||||||
|
// complex types.
|
||||||
|
type ComplexAbs[T Complex] T
|
||||||
|
|
||||||
|
func (a ComplexAbs[T]) Abs() ComplexAbs[T] {
|
||||||
|
r := float64(real(a))
|
||||||
|
i := float64(imag(a))
|
||||||
|
d := math.Sqrt(r * r + i * i)
|
||||||
|
return ComplexAbs[T](complex(d, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
func OrderedAbsDifference[T OrderedNumeric](a, b T) T {
|
||||||
|
return T(AbsDifference(OrderedAbs[T](a), OrderedAbs[T](b)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ComplexAbsDifference[T Complex](a, b T) T {
|
||||||
|
return T(AbsDifference(ComplexAbs[T](a), ComplexAbs[T](b)))
|
||||||
|
}
|
||||||
113
src/cmd/compile/internal/syntax/testdata/go2/map.go2
vendored
Normal file
113
src/cmd/compile/internal/syntax/testdata/go2/map.go2
vendored
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package orderedmap provides an ordered map, implemented as a binary tree.
|
||||||
|
package orderedmap
|
||||||
|
|
||||||
|
// TODO(gri) fix imports for tests
|
||||||
|
import "chans" // ERROR could not import
|
||||||
|
|
||||||
|
// Map is an ordered map.
|
||||||
|
type Map[K, V any] struct {
|
||||||
|
root *node[K, V]
|
||||||
|
compare func(K, K) int
|
||||||
|
}
|
||||||
|
|
||||||
|
// node is the type of a node in the binary tree.
|
||||||
|
type node[K, V any] struct {
|
||||||
|
key K
|
||||||
|
val V
|
||||||
|
left, right *node[K, V]
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new map.
|
||||||
|
func New[K, V any](compare func(K, K) int) *Map[K, V] {
|
||||||
|
return &Map[K, V]{compare: compare}
|
||||||
|
}
|
||||||
|
|
||||||
|
// find looks up key in the map, and returns either a pointer
|
||||||
|
// to the node holding key, or a pointer to the location where
|
||||||
|
// such a node would go.
|
||||||
|
func (m *Map[K, V]) find(key K) **node[K, V] {
|
||||||
|
pn := &m.root
|
||||||
|
for *pn != nil {
|
||||||
|
switch cmp := m.compare(key, (*pn).key); {
|
||||||
|
case cmp < 0:
|
||||||
|
pn = &(*pn).left
|
||||||
|
case cmp > 0:
|
||||||
|
pn = &(*pn).right
|
||||||
|
default:
|
||||||
|
return pn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert inserts a new key/value into the map.
|
||||||
|
// If the key is already present, the value is replaced.
|
||||||
|
// Returns true if this is a new key, false if already present.
|
||||||
|
func (m *Map[K, V]) Insert(key K, val V) bool {
|
||||||
|
pn := m.find(key)
|
||||||
|
if *pn != nil {
|
||||||
|
(*pn).val = val
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*pn = &node[K, V]{key: key, val: val}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find returns the value associated with a key, or zero if not present.
|
||||||
|
// The found result reports whether the key was found.
|
||||||
|
func (m *Map[K, V]) Find(key K) (V, bool) {
|
||||||
|
pn := m.find(key)
|
||||||
|
if *pn == nil {
|
||||||
|
var zero V // see the discussion of zero values, above
|
||||||
|
return zero, false
|
||||||
|
}
|
||||||
|
return (*pn).val, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// keyValue is a pair of key and value used when iterating.
|
||||||
|
type keyValue[K, V any] struct {
|
||||||
|
key K
|
||||||
|
val V
|
||||||
|
}
|
||||||
|
|
||||||
|
// InOrder returns an iterator that does an in-order traversal of the map.
|
||||||
|
func (m *Map[K, V]) InOrder() *Iterator[K, V] {
|
||||||
|
sender, receiver := chans.Ranger[keyValue[K, V]]()
|
||||||
|
var f func(*node[K, V]) bool
|
||||||
|
f = func(n *node[K, V]) bool {
|
||||||
|
if n == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Stop sending values if sender.Send returns false,
|
||||||
|
// meaning that nothing is listening at the receiver end.
|
||||||
|
return f(n.left) &&
|
||||||
|
sender.Send(keyValue[K, V]{n.key, n.val}) &&
|
||||||
|
f(n.right)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
f(m.root)
|
||||||
|
sender.Close()
|
||||||
|
}()
|
||||||
|
return &Iterator[K, V]{receiver}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterator is used to iterate over the map.
|
||||||
|
type Iterator[K, V any] struct {
|
||||||
|
r *chans.Receiver[keyValue[K, V]]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next key and value pair, and a boolean indicating
|
||||||
|
// whether they are valid or whether we have reached the end.
|
||||||
|
func (it *Iterator[K, V]) Next() (K, V, bool) {
|
||||||
|
keyval, ok := it.r.Next()
|
||||||
|
if !ok {
|
||||||
|
var zerok K
|
||||||
|
var zerov V
|
||||||
|
return zerok, zerov, false
|
||||||
|
}
|
||||||
|
return keyval.key, keyval.val, true
|
||||||
|
}
|
||||||
146
src/cmd/compile/internal/syntax/testdata/go2/map2.go2
vendored
Normal file
146
src/cmd/compile/internal/syntax/testdata/go2/map2.go2
vendored
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file is like map.go2, but instead if importing chans, it contains
|
||||||
|
// the necessary functionality at the end of the file.
|
||||||
|
|
||||||
|
// Package orderedmap provides an ordered map, implemented as a binary tree.
|
||||||
|
package orderedmap
|
||||||
|
|
||||||
|
// Map is an ordered map.
|
||||||
|
type Map[K, V any] struct {
|
||||||
|
root *node[K, V]
|
||||||
|
compare func(K, K) int
|
||||||
|
}
|
||||||
|
|
||||||
|
// node is the type of a node in the binary tree.
|
||||||
|
type node[K, V any] struct {
|
||||||
|
key K
|
||||||
|
val V
|
||||||
|
left, right *node[K, V]
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new map.
|
||||||
|
func New[K, V any](compare func(K, K) int) *Map[K, V] {
|
||||||
|
return &Map[K, V]{compare: compare}
|
||||||
|
}
|
||||||
|
|
||||||
|
// find looks up key in the map, and returns either a pointer
|
||||||
|
// to the node holding key, or a pointer to the location where
|
||||||
|
// such a node would go.
|
||||||
|
func (m *Map[K, V]) find(key K) **node[K, V] {
|
||||||
|
pn := &m.root
|
||||||
|
for *pn != nil {
|
||||||
|
switch cmp := m.compare(key, (*pn).key); {
|
||||||
|
case cmp < 0:
|
||||||
|
pn = &(*pn).left
|
||||||
|
case cmp > 0:
|
||||||
|
pn = &(*pn).right
|
||||||
|
default:
|
||||||
|
return pn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert inserts a new key/value into the map.
|
||||||
|
// If the key is already present, the value is replaced.
|
||||||
|
// Returns true if this is a new key, false if already present.
|
||||||
|
func (m *Map[K, V]) Insert(key K, val V) bool {
|
||||||
|
pn := m.find(key)
|
||||||
|
if *pn != nil {
|
||||||
|
(*pn).val = val
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*pn = &node[K, V]{key: key, val: val}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find returns the value associated with a key, or zero if not present.
|
||||||
|
// The found result reports whether the key was found.
|
||||||
|
func (m *Map[K, V]) Find(key K) (V, bool) {
|
||||||
|
pn := m.find(key)
|
||||||
|
if *pn == nil {
|
||||||
|
var zero V // see the discussion of zero values, above
|
||||||
|
return zero, false
|
||||||
|
}
|
||||||
|
return (*pn).val, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// keyValue is a pair of key and value used when iterating.
|
||||||
|
type keyValue[K, V any] struct {
|
||||||
|
key K
|
||||||
|
val V
|
||||||
|
}
|
||||||
|
|
||||||
|
// InOrder returns an iterator that does an in-order traversal of the map.
|
||||||
|
func (m *Map[K, V]) InOrder() *Iterator[K, V] {
|
||||||
|
sender, receiver := chans_Ranger[keyValue[K, V]]()
|
||||||
|
var f func(*node[K, V]) bool
|
||||||
|
f = func(n *node[K, V]) bool {
|
||||||
|
if n == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Stop sending values if sender.Send returns false,
|
||||||
|
// meaning that nothing is listening at the receiver end.
|
||||||
|
return f(n.left) &&
|
||||||
|
sender.Send(keyValue[K, V]{n.key, n.val}) &&
|
||||||
|
f(n.right)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
f(m.root)
|
||||||
|
sender.Close()
|
||||||
|
}()
|
||||||
|
return &Iterator[K, V]{receiver}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterator is used to iterate over the map.
|
||||||
|
type Iterator[K, V any] struct {
|
||||||
|
r *chans_Receiver[keyValue[K, V]]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next key and value pair, and a boolean indicating
|
||||||
|
// whether they are valid or whether we have reached the end.
|
||||||
|
func (it *Iterator[K, V]) Next() (K, V, bool) {
|
||||||
|
keyval, ok := it.r.Next()
|
||||||
|
if !ok {
|
||||||
|
var zerok K
|
||||||
|
var zerov V
|
||||||
|
return zerok, zerov, false
|
||||||
|
}
|
||||||
|
return keyval.key, keyval.val, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// chans
|
||||||
|
|
||||||
|
func chans_Ranger[T any]() (*chans_Sender[T], *chans_Receiver[T])
|
||||||
|
|
||||||
|
// A sender is used to send values to a Receiver.
|
||||||
|
type chans_Sender[T any] struct {
|
||||||
|
values chan<- T
|
||||||
|
done <-chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *chans_Sender[T]) Send(v T) bool {
|
||||||
|
select {
|
||||||
|
case s.values <- v:
|
||||||
|
return true
|
||||||
|
case <-s.done:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *chans_Sender[T]) Close() {
|
||||||
|
close(s.values)
|
||||||
|
}
|
||||||
|
|
||||||
|
type chans_Receiver[T any] struct {
|
||||||
|
values <-chan T
|
||||||
|
done chan<- bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *chans_Receiver[T]) Next() (T, bool) {
|
||||||
|
v, ok := <-r.values
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
68
src/cmd/compile/internal/syntax/testdata/go2/slices.go2
vendored
Normal file
68
src/cmd/compile/internal/syntax/testdata/go2/slices.go2
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package slices implements various slice algorithms.
|
||||||
|
package slices
|
||||||
|
|
||||||
|
// Map turns a []T1 to a []T2 using a mapping function.
|
||||||
|
func Map[T1, T2 any](s []T1, f func(T1) T2) []T2 {
|
||||||
|
r := make([]T2, len(s))
|
||||||
|
for i, v := range s {
|
||||||
|
r[i] = f(v)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce reduces a []T1 to a single value using a reduction function.
|
||||||
|
func Reduce[T1, T2 any](s []T1, initializer T2, f func(T2, T1) T2) T2 {
|
||||||
|
r := initializer
|
||||||
|
for _, v := range s {
|
||||||
|
r = f(r, v)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter filters values from a slice using a filter function.
|
||||||
|
func Filter[T any](s []T, f func(T) bool) []T {
|
||||||
|
var r []T
|
||||||
|
for _, v := range s {
|
||||||
|
if f(v) {
|
||||||
|
r = append(r, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example uses
|
||||||
|
|
||||||
|
func limiter(x int) byte {
|
||||||
|
switch {
|
||||||
|
case x < 0:
|
||||||
|
return 0
|
||||||
|
default:
|
||||||
|
return byte(x)
|
||||||
|
case x > 255:
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var input = []int{-4, 68954, 7, 44, 0, -555, 6945}
|
||||||
|
var limited1 = Map[int, byte](input, limiter)
|
||||||
|
var limited2 = Map(input, limiter) // using type inference
|
||||||
|
|
||||||
|
func reducer(x float64, y int) float64 {
|
||||||
|
return x + float64(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
var reduced1 = Reduce[int, float64](input, 0, reducer)
|
||||||
|
var reduced2 = Reduce(input, 1i /* ERROR overflows */, reducer) // using type inference
|
||||||
|
var reduced3 = Reduce(input, 1, reducer) // using type inference
|
||||||
|
|
||||||
|
func filter(x int) bool {
|
||||||
|
return x&1 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var filtered1 = Filter[int](input, filter)
|
||||||
|
var filtered2 = Filter(input, filter) // using type inference
|
||||||
|
|
||||||
83
src/cmd/compile/internal/syntax/testdata/go2/smoketest.go2
vendored
Normal file
83
src/cmd/compile/internal/syntax/testdata/go2/smoketest.go2
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file contains basic generic code snippets.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
// type parameter lists
|
||||||
|
type B[P any] struct{}
|
||||||
|
type _[P interface{}] struct{}
|
||||||
|
type _[P B] struct{}
|
||||||
|
type _[P B[P]] struct{}
|
||||||
|
|
||||||
|
type _[A, B, C any] struct{}
|
||||||
|
type _[A, B, C B] struct{}
|
||||||
|
type _[A, B, C B[A, B, C]] struct{}
|
||||||
|
type _[A1, A2 B1, A3 B2, A4, A5, A6 B3] struct{}
|
||||||
|
|
||||||
|
type _[A interface{}] struct{}
|
||||||
|
type _[A, B interface{ m() }] struct{}
|
||||||
|
|
||||||
|
type _[A, B, C any] struct{}
|
||||||
|
|
||||||
|
// in functions
|
||||||
|
func _[P any]()
|
||||||
|
func _[P interface{}]()
|
||||||
|
func _[P B]()
|
||||||
|
func _[P B[P]]()
|
||||||
|
|
||||||
|
// in methods
|
||||||
|
func (T) _[P any]()
|
||||||
|
func (T) _[P interface{}]()
|
||||||
|
func (T) _[P B]()
|
||||||
|
func (T) _[P B[P]]()
|
||||||
|
|
||||||
|
// type instantiations
|
||||||
|
type _ T[int]
|
||||||
|
|
||||||
|
// in expressions
|
||||||
|
var _ = T[int]{}
|
||||||
|
|
||||||
|
// in embedded types
|
||||||
|
type _ struct{ T[int] }
|
||||||
|
|
||||||
|
// interfaces
|
||||||
|
type _ interface{
|
||||||
|
m()
|
||||||
|
type int
|
||||||
|
}
|
||||||
|
|
||||||
|
type _ interface{
|
||||||
|
type int, float, string
|
||||||
|
type complex128
|
||||||
|
underlying(underlying underlying) underlying
|
||||||
|
}
|
||||||
|
|
||||||
|
type _ interface{
|
||||||
|
T
|
||||||
|
T[int]
|
||||||
|
}
|
||||||
|
|
||||||
|
// tricky cases
|
||||||
|
func _(T[P], T[P1, P2])
|
||||||
|
func _(a [N]T)
|
||||||
|
|
||||||
|
type _ struct{
|
||||||
|
T[P]
|
||||||
|
T[P1, P2]
|
||||||
|
f [N]
|
||||||
|
}
|
||||||
|
type _ interface{
|
||||||
|
m()
|
||||||
|
|
||||||
|
// generic methods - disabled for now
|
||||||
|
// m[] /* ERROR empty type parameter list */ ()
|
||||||
|
// m[ /* ERROR cannot have type parameters */ P any](P)
|
||||||
|
|
||||||
|
// instantiated types
|
||||||
|
// T[] /* ERROR empty type argument list */
|
||||||
|
T[P]
|
||||||
|
T[P1, P2]
|
||||||
|
}
|
||||||
60
src/cmd/compile/internal/syntax/testdata/go2/typeinst.go2
vendored
Normal file
60
src/cmd/compile/internal/syntax/testdata/go2/typeinst.go2
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
type myInt int
|
||||||
|
|
||||||
|
// Parameterized type declarations
|
||||||
|
|
||||||
|
type T1[P any] P
|
||||||
|
|
||||||
|
type T2[P any] struct {
|
||||||
|
f P
|
||||||
|
g int // int should still be in scope chain
|
||||||
|
}
|
||||||
|
|
||||||
|
type List[P any] []P
|
||||||
|
|
||||||
|
// Alias type declarations cannot have type parameters. Syntax error.
|
||||||
|
// TODO(gri) Disabled for now as we don't check syntax error here.
|
||||||
|
// type A1[P any] = /* ERROR cannot be alias */ P
|
||||||
|
|
||||||
|
// But an alias may refer to a generic, uninstantiated type.
|
||||||
|
type A2 = List
|
||||||
|
var _ A2[int]
|
||||||
|
var _ A2 /* ERROR without instantiation */
|
||||||
|
|
||||||
|
type A3 = List[int]
|
||||||
|
var _ A3
|
||||||
|
|
||||||
|
// Parameterized type instantiations
|
||||||
|
|
||||||
|
var x int
|
||||||
|
type _ x /* ERROR not a type */ [int]
|
||||||
|
|
||||||
|
type _ int /* ERROR not a generic type */ [int]
|
||||||
|
type _ myInt /* ERROR not a generic type */ [int]
|
||||||
|
|
||||||
|
// TODO(gri) better error messages
|
||||||
|
type _ T1[int]
|
||||||
|
type _ T1[x /* ERROR not a type */ ]
|
||||||
|
type _ T1 /* ERROR got 2 arguments but 1 type parameters */ [int, float32]
|
||||||
|
|
||||||
|
var _ T2[int] = T2[int]{}
|
||||||
|
|
||||||
|
var _ List[int] = []int{1, 2, 3}
|
||||||
|
var _ List[[]int] = [][]int{{1, 2, 3}}
|
||||||
|
var _ List[List[List[int]]]
|
||||||
|
|
||||||
|
// Parameterized types containing parameterized types
|
||||||
|
|
||||||
|
type T3[P any] List[P]
|
||||||
|
|
||||||
|
var _ T3[int] = T3[int](List[int]{1, 2, 3})
|
||||||
|
|
||||||
|
// Self-recursive generic types are not permitted
|
||||||
|
|
||||||
|
type self1[P any] self1 /* ERROR illegal cycle */ [P]
|
||||||
|
type self2[P any] *self2[P] // this is ok
|
||||||
256
src/cmd/compile/internal/syntax/testdata/go2/typeinst2.go2
vendored
Normal file
256
src/cmd/compile/internal/syntax/testdata/go2/typeinst2.go2
vendored
Normal file
|
|
@ -0,0 +1,256 @@
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
type List[E any] []E
|
||||||
|
var _ List[List[List[int]]]
|
||||||
|
var _ List[List[List[int]]] = []List[List[int]]{}
|
||||||
|
|
||||||
|
type (
|
||||||
|
T1[P1 any] struct {
|
||||||
|
f1 T2[P1, float32]
|
||||||
|
}
|
||||||
|
|
||||||
|
T2[P2, P3 any] struct {
|
||||||
|
f2 P2
|
||||||
|
f3 P3
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var x1 T1[int]
|
||||||
|
var x2 T2[int, float32]
|
||||||
|
|
||||||
|
x1.f1.f2 = 0
|
||||||
|
x1.f1 = x2
|
||||||
|
}
|
||||||
|
|
||||||
|
type T3[P any] T1[T2[P, P]]
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var x1 T3[int]
|
||||||
|
var x2 T2[int, int]
|
||||||
|
x1.f1.f2 = x2
|
||||||
|
}
|
||||||
|
|
||||||
|
func f[P any] (x P) List[P] {
|
||||||
|
return List[P]{x}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ []int = f(0)
|
||||||
|
_ []float32 = f[float32](10)
|
||||||
|
_ List[complex128] = f(1i)
|
||||||
|
_ []List[int] = f(List[int]{})
|
||||||
|
_ List[List[int]] = []List[int]{}
|
||||||
|
_ = []List[int]{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parameterized types with methods
|
||||||
|
|
||||||
|
func (l List[E]) Head() (_ E, _ bool) {
|
||||||
|
if len(l) > 0 {
|
||||||
|
return l[0], true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// A test case for instantiating types with other types (extracted from map.go2)
|
||||||
|
|
||||||
|
type Pair[K any] struct {
|
||||||
|
key K
|
||||||
|
}
|
||||||
|
|
||||||
|
type Receiver[T any] struct {
|
||||||
|
values T
|
||||||
|
}
|
||||||
|
|
||||||
|
type Iterator[K any] struct {
|
||||||
|
r Receiver[Pair[K]]
|
||||||
|
}
|
||||||
|
|
||||||
|
func Values [T any] (r Receiver[T]) T {
|
||||||
|
return r.values
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it Iterator[K]) Next() K {
|
||||||
|
return Values[Pair[K]](it.r).key
|
||||||
|
}
|
||||||
|
|
||||||
|
// A more complex test case testing type bounds (extracted from linalg.go2 and reduced to essence)
|
||||||
|
|
||||||
|
type NumericAbs[T any] interface {
|
||||||
|
Abs() T
|
||||||
|
}
|
||||||
|
|
||||||
|
func AbsDifference[T NumericAbs[T]](x T)
|
||||||
|
|
||||||
|
type OrderedAbs[T any] T
|
||||||
|
|
||||||
|
func (a OrderedAbs[T]) Abs() OrderedAbs[T]
|
||||||
|
|
||||||
|
func OrderedAbsDifference[T any](x T) {
|
||||||
|
AbsDifference(OrderedAbs[T](x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// same code, reduced to essence
|
||||||
|
|
||||||
|
func g[P interface{ m() P }](x P)
|
||||||
|
|
||||||
|
type T4[P any] P
|
||||||
|
|
||||||
|
func (_ T4[P]) m() T4[P]
|
||||||
|
|
||||||
|
func _[Q any](x Q) {
|
||||||
|
g(T4[Q](x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Another test case that caused problems in the past
|
||||||
|
|
||||||
|
type T5[_ interface { a() }, _ interface{}] struct{}
|
||||||
|
|
||||||
|
type A[P any] struct{ x P }
|
||||||
|
|
||||||
|
func (_ A[P]) a() {}
|
||||||
|
|
||||||
|
var _ T5[A[int], int]
|
||||||
|
|
||||||
|
// Invoking methods with parameterized receiver types uses
|
||||||
|
// type inference to determine the actual type arguments matching
|
||||||
|
// the receiver type parameters from the actual receiver argument.
|
||||||
|
// Go does implicit address-taking and dereferenciation depending
|
||||||
|
// on the actual receiver and the method's receiver type. To make
|
||||||
|
// type inference work, the type-checker matches "pointer-ness"
|
||||||
|
// of the actual receiver and the method's receiver type.
|
||||||
|
// The following code tests this mechanism.
|
||||||
|
|
||||||
|
type R1[A any] struct{}
|
||||||
|
func (_ R1[A]) vm()
|
||||||
|
func (_ *R1[A]) pm()
|
||||||
|
|
||||||
|
func _[T any](r R1[T], p *R1[T]) {
|
||||||
|
r.vm()
|
||||||
|
r.pm()
|
||||||
|
p.vm()
|
||||||
|
p.pm()
|
||||||
|
}
|
||||||
|
|
||||||
|
type R2[A, B any] struct{}
|
||||||
|
func (_ R2[A, B]) vm()
|
||||||
|
func (_ *R2[A, B]) pm()
|
||||||
|
|
||||||
|
func _[T any](r R2[T, int], p *R2[string, T]) {
|
||||||
|
r.vm()
|
||||||
|
r.pm()
|
||||||
|
p.vm()
|
||||||
|
p.pm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// An interface can (explicitly) declare at most one type list.
|
||||||
|
type _ interface {
|
||||||
|
m0()
|
||||||
|
type int, string, bool
|
||||||
|
type /* ERROR multiple type lists */ float32, float64
|
||||||
|
m1()
|
||||||
|
m2()
|
||||||
|
type /* ERROR multiple type lists */ complex64, complex128
|
||||||
|
type /* ERROR multiple type lists */ rune
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface type lists may contain each type at most once.
|
||||||
|
// (If there are multiple lists, we assume the author intended
|
||||||
|
// for them to be all in a single list, and we report the error
|
||||||
|
// as well.)
|
||||||
|
type _ interface {
|
||||||
|
type int, int /* ERROR duplicate type int */
|
||||||
|
type /* ERROR multiple type lists */ int /* ERROR duplicate type int */
|
||||||
|
}
|
||||||
|
|
||||||
|
type _ interface {
|
||||||
|
type struct{f int}, struct{g int}, struct /* ERROR duplicate type */ {f int}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface type lists can contain any type, incl. *Named types.
|
||||||
|
// Verify that we use the underlying type to compute the operational type.
|
||||||
|
type MyInt int
|
||||||
|
func add1[T interface{type MyInt}](x T) T {
|
||||||
|
return x + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyString string
|
||||||
|
func double[T interface{type MyInt, MyString}](x T) T {
|
||||||
|
return x + x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Embedding of interfaces with type lists leads to interfaces
|
||||||
|
// with type lists that are the intersection of the embedded
|
||||||
|
// type lists.
|
||||||
|
|
||||||
|
type E0 interface {
|
||||||
|
type int, bool, string
|
||||||
|
}
|
||||||
|
|
||||||
|
type E1 interface {
|
||||||
|
type int, float64, string
|
||||||
|
}
|
||||||
|
|
||||||
|
type E2 interface {
|
||||||
|
type float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type I0 interface {
|
||||||
|
E0
|
||||||
|
}
|
||||||
|
|
||||||
|
func f0[T I0]()
|
||||||
|
var _ = f0[int]
|
||||||
|
var _ = f0[bool]
|
||||||
|
var _ = f0[string]
|
||||||
|
var _ = f0[float64 /* ERROR does not satisfy I0 */ ]
|
||||||
|
|
||||||
|
type I01 interface {
|
||||||
|
E0
|
||||||
|
E1
|
||||||
|
}
|
||||||
|
|
||||||
|
func f01[T I01]()
|
||||||
|
var _ = f01[int]
|
||||||
|
var _ = f01[bool /* ERROR does not satisfy I0 */ ]
|
||||||
|
var _ = f01[string]
|
||||||
|
var _ = f01[float64 /* ERROR does not satisfy I0 */ ]
|
||||||
|
|
||||||
|
type I012 interface {
|
||||||
|
E0
|
||||||
|
E1
|
||||||
|
E2
|
||||||
|
}
|
||||||
|
|
||||||
|
func f012[T I012]()
|
||||||
|
var _ = f012[int /* ERROR does not satisfy I012 */ ]
|
||||||
|
var _ = f012[bool /* ERROR does not satisfy I012 */ ]
|
||||||
|
var _ = f012[string /* ERROR does not satisfy I012 */ ]
|
||||||
|
var _ = f012[float64 /* ERROR does not satisfy I012 */ ]
|
||||||
|
|
||||||
|
type I12 interface {
|
||||||
|
E1
|
||||||
|
E2
|
||||||
|
}
|
||||||
|
|
||||||
|
func f12[T I12]()
|
||||||
|
var _ = f12[int /* ERROR does not satisfy I12 */ ]
|
||||||
|
var _ = f12[bool /* ERROR does not satisfy I12 */ ]
|
||||||
|
var _ = f12[string /* ERROR does not satisfy I12 */ ]
|
||||||
|
var _ = f12[float64]
|
||||||
|
|
||||||
|
type I0_ interface {
|
||||||
|
E0
|
||||||
|
type int
|
||||||
|
}
|
||||||
|
|
||||||
|
func f0_[T I0_]()
|
||||||
|
var _ = f0_[int]
|
||||||
|
var _ = f0_[bool /* ERROR does not satisfy I0_ */ ]
|
||||||
|
var _ = f0_[string /* ERROR does not satisfy I0_ */ ]
|
||||||
|
var _ = f0_[float64 /* ERROR does not satisfy I0_ */ ]
|
||||||
451
src/cmd/compile/internal/syntax/testdata/go2/typeparams.go2
vendored
Normal file
451
src/cmd/compile/internal/syntax/testdata/go2/typeparams.go2
vendored
Normal file
|
|
@ -0,0 +1,451 @@
|
||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
// import "io" // for type assertion tests
|
||||||
|
|
||||||
|
// The predeclared identifier "any" is only visible as a constraint
|
||||||
|
// in a type parameter list.
|
||||||
|
var _ any // ERROR undeclared
|
||||||
|
func _[_ any /* ok here */ , _ interface{any /* ERROR undeclared */ }](any /* ERROR undeclared */ ) {
|
||||||
|
var _ any /* ERROR undeclared */
|
||||||
|
}
|
||||||
|
|
||||||
|
func identity[T any](x T) T { return x }
|
||||||
|
|
||||||
|
func _[_ any](x int) int
|
||||||
|
func _[T any](T /* ERROR redeclared */ T)()
|
||||||
|
func _[T, T /* ERROR redeclared */ any]()
|
||||||
|
|
||||||
|
func reverse[T any](list []T) []T {
|
||||||
|
rlist := make([]T, len(list))
|
||||||
|
i := len(list)
|
||||||
|
for _, x := range list {
|
||||||
|
i--
|
||||||
|
rlist[i] = x
|
||||||
|
}
|
||||||
|
return rlist
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = reverse /* ERROR cannot use generic function reverse */
|
||||||
|
var _ = reverse[int, float32 /* ERROR got 2 type arguments */ ] ([]int{1, 2, 3})
|
||||||
|
var _ = reverse[int]([ /* ERROR cannot use */ ]float32{1, 2, 3})
|
||||||
|
var f = reverse[chan int]
|
||||||
|
var _ = f(0 /* ERROR cannot convert 0 .* to \[\]chan int */ )
|
||||||
|
|
||||||
|
func swap[A, B any](a A, b B) (B, A) { return b, a }
|
||||||
|
|
||||||
|
var _ = swap /* ERROR single value is expected */ [int, float32](1, 2)
|
||||||
|
var f32, i = swap[int, float32](swap(float32, int)(1, 2))
|
||||||
|
var _ float32 = f32
|
||||||
|
var _ int = i
|
||||||
|
|
||||||
|
func swapswap[A, B any](a A, b B) (A, B) {
|
||||||
|
return swap[B, A](b, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
type F[A, B any] func(A, B) (B, A)
|
||||||
|
|
||||||
|
func min[T interface{ type int }](x, y T) T {
|
||||||
|
if x < y {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
func _[T interface{type int, float32}](x, y T) bool { return x < y }
|
||||||
|
func _[T any](x, y T) bool { return x /* ERROR cannot compare */ < y }
|
||||||
|
func _[T interface{type int, float32, bool}](x, y T) bool { return x /* ERROR cannot compare */ < y }
|
||||||
|
|
||||||
|
func _[T C1[T]](x, y T) bool { return x /* ERROR cannot compare */ < y }
|
||||||
|
func _[T C2[T]](x, y T) bool { return x < y }
|
||||||
|
|
||||||
|
type C1[T any] interface{}
|
||||||
|
type C2[T any] interface{ type int, float32 }
|
||||||
|
|
||||||
|
func new[T any]() *T {
|
||||||
|
var x T
|
||||||
|
return &x
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = new /* ERROR cannot use generic function new */
|
||||||
|
var _ *int = new[int]()
|
||||||
|
|
||||||
|
func _[T any](map[T /* ERROR invalid map key type T \(missing comparable constraint\) */]int) // w/o constraint we don't know if T is comparable
|
||||||
|
|
||||||
|
func f1[T1 any](struct{T1}) int
|
||||||
|
var _ = f1(int)(struct{T1}{})
|
||||||
|
type T1 = int
|
||||||
|
|
||||||
|
func f2[t1 any](struct{t1; x float32}) int
|
||||||
|
var _ = f2(t1)(struct{t1; x float32}{})
|
||||||
|
type t1 = int
|
||||||
|
|
||||||
|
|
||||||
|
func f3[A, B, C any](A, struct{x B}, func(A, struct{x B}, *C)) int
|
||||||
|
|
||||||
|
var _ = f3[int, rune, bool](1, struct{x rune}{}, nil)
|
||||||
|
|
||||||
|
// indexing
|
||||||
|
|
||||||
|
func _[T any] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
|
||||||
|
func _[T interface{ type int }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
|
||||||
|
func _[T interface{ type string }] (x T, i int) { _ = x[i] }
|
||||||
|
func _[T interface{ type []int }] (x T, i int) { _ = x[i] }
|
||||||
|
func _[T interface{ type [10]int, *[20]int, map[string]int }] (x T, i int) { _ = x[i] }
|
||||||
|
func _[T interface{ type string, []byte }] (x T, i int) { _ = x[i] }
|
||||||
|
func _[T interface{ type []int, [1]rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
|
||||||
|
func _[T interface{ type string, []rune }] (x T, i int) { _ = x /* ERROR "cannot index" */ [i] }
|
||||||
|
|
||||||
|
// slicing
|
||||||
|
// TODO(gri) implement this
|
||||||
|
|
||||||
|
func _[T interface{ type string }] (x T, i, j, k int) { _ = x /* ERROR invalid operation */ [i:j:k] }
|
||||||
|
|
||||||
|
// len/cap built-ins
|
||||||
|
|
||||||
|
func _[T any](x T) { _ = len(x /* ERROR invalid argument */ ) }
|
||||||
|
func _[T interface{ type int }](x T) { _ = len(x /* ERROR invalid argument */ ) }
|
||||||
|
func _[T interface{ type string, []byte, int }](x T) { _ = len(x /* ERROR invalid argument */ ) }
|
||||||
|
func _[T interface{ type string }](x T) { _ = len(x) }
|
||||||
|
func _[T interface{ type [10]int }](x T) { _ = len(x) }
|
||||||
|
func _[T interface{ type []byte }](x T) { _ = len(x) }
|
||||||
|
func _[T interface{ type map[int]int }](x T) { _ = len(x) }
|
||||||
|
func _[T interface{ type chan int }](x T) { _ = len(x) }
|
||||||
|
func _[T interface{ type string, []byte, chan int }](x T) { _ = len(x) }
|
||||||
|
|
||||||
|
func _[T any](x T) { _ = cap(x /* ERROR invalid argument */ ) }
|
||||||
|
func _[T interface{ type int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
|
||||||
|
func _[T interface{ type string, []byte, int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
|
||||||
|
func _[T interface{ type string }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
|
||||||
|
func _[T interface{ type [10]int }](x T) { _ = cap(x) }
|
||||||
|
func _[T interface{ type []byte }](x T) { _ = cap(x) }
|
||||||
|
func _[T interface{ type map[int]int }](x T) { _ = cap(x /* ERROR invalid argument */ ) }
|
||||||
|
func _[T interface{ type chan int }](x T) { _ = cap(x) }
|
||||||
|
func _[T interface{ type []byte, chan int }](x T) { _ = cap(x) }
|
||||||
|
|
||||||
|
// range iteration
|
||||||
|
|
||||||
|
func _[T interface{}](x T) {
|
||||||
|
for range x /* ERROR cannot range */ {}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _[T interface{ type string, []string }](x T) {
|
||||||
|
for range x {}
|
||||||
|
for i := range x { _ = i }
|
||||||
|
for i, _ := range x { _ = i }
|
||||||
|
for i, e := range x /* ERROR must have the same element type */ { _ = i }
|
||||||
|
for _, e := range x /* ERROR must have the same element type */ {}
|
||||||
|
var e rune
|
||||||
|
_ = e
|
||||||
|
for _, (e) = range x /* ERROR must have the same element type */ {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func _[T interface{ type string, []rune, map[int]rune }](x T) {
|
||||||
|
for _, e := range x { _ = e }
|
||||||
|
for i, e := range x { _ = i; _ = e }
|
||||||
|
}
|
||||||
|
|
||||||
|
func _[T interface{ type string, []rune, map[string]rune }](x T) {
|
||||||
|
for _, e := range x { _ = e }
|
||||||
|
for i, e := range x /* ERROR must have the same key type */ { _ = e }
|
||||||
|
}
|
||||||
|
|
||||||
|
func _[T interface{ type string, chan int }](x T) {
|
||||||
|
for range x {}
|
||||||
|
for i := range x { _ = i }
|
||||||
|
for i, _ := range x { _ = i } // TODO(gri) should get an error here: channels only return one value
|
||||||
|
}
|
||||||
|
|
||||||
|
func _[T interface{ type string, chan<-int }](x T) {
|
||||||
|
for i := range x /* ERROR send-only channel */ { _ = i }
|
||||||
|
}
|
||||||
|
|
||||||
|
// type inference checks
|
||||||
|
|
||||||
|
var _ = new() /* ERROR cannot infer T */
|
||||||
|
|
||||||
|
func f4[A, B, C any](A, B) C
|
||||||
|
|
||||||
|
var _ = f4(1, 2) /* ERROR cannot infer C */
|
||||||
|
var _ = f4[int, float32, complex128](1, 2)
|
||||||
|
|
||||||
|
func f5[A, B, C any](A, []*B, struct{f []C}) int
|
||||||
|
|
||||||
|
var _ = f5[int, float32, complex128](0, nil, struct{f []complex128}{})
|
||||||
|
var _ = f5(0, nil, struct{f []complex128}{}) // ERROR cannot infer
|
||||||
|
var _ = f5(0, []*float32{new[float32]()}, struct{f []complex128}{})
|
||||||
|
|
||||||
|
func f6[A any](A, []A) int
|
||||||
|
|
||||||
|
var _ = f6(0, nil)
|
||||||
|
|
||||||
|
func f6nil[A any](A) int
|
||||||
|
|
||||||
|
var _ = f6nil(nil) // ERROR cannot infer
|
||||||
|
|
||||||
|
// type inference with variadic functions
|
||||||
|
|
||||||
|
func f7[T any](...T) T
|
||||||
|
|
||||||
|
var _ int = f7() /* ERROR cannot infer T */
|
||||||
|
var _ int = f7(1)
|
||||||
|
var _ int = f7(1, 2)
|
||||||
|
var _ int = f7([]int{}...)
|
||||||
|
var _ int = f7 /* ERROR cannot use */ ([]float64{}...)
|
||||||
|
var _ float64 = f7([]float64{}...)
|
||||||
|
var _ = f7[float64](1, 2.3)
|
||||||
|
var _ = f7(float64(1), 2.3)
|
||||||
|
var _ = f7(1, 2.3 /* ERROR does not match */ )
|
||||||
|
var _ = f7(1.2, 3 /* ERROR does not match */ )
|
||||||
|
|
||||||
|
func f8[A, B any](A, B, ...B) int
|
||||||
|
|
||||||
|
var _ = f8(1) /* ERROR not enough arguments */
|
||||||
|
var _ = f8(1, 2.3)
|
||||||
|
var _ = f8(1, 2.3, 3.4, 4.5)
|
||||||
|
var _ = f8(1, 2.3, 3.4, 4 /* ERROR does not match */ )
|
||||||
|
var _ = f8(int, float64)(1, 2.3, 3.4, 4)
|
||||||
|
|
||||||
|
var _ = f8(int, float64)(0, 0, nil...) // test case for #18268
|
||||||
|
|
||||||
|
// init functions cannot have type parameters
|
||||||
|
|
||||||
|
func init() {}
|
||||||
|
func init[/* ERROR func init must have no type parameters */ _ any]() {}
|
||||||
|
func init[/* ERROR func init must have no type parameters */ P any]() {}
|
||||||
|
|
||||||
|
type T struct {}
|
||||||
|
|
||||||
|
func (T) m1() {}
|
||||||
|
// The type checker accepts method type parameters if configured accordingly.
|
||||||
|
func (T) m2[_ any]() {}
|
||||||
|
func (T) m3[P any]() {}
|
||||||
|
|
||||||
|
// type inference across parameterized types
|
||||||
|
|
||||||
|
type S1[P any] struct { f P }
|
||||||
|
|
||||||
|
func f9[P any](x S1[P])
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
f9[int](S1[int]{42})
|
||||||
|
f9(S1[int]{42})
|
||||||
|
}
|
||||||
|
|
||||||
|
type S2[A, B, C any] struct{}
|
||||||
|
|
||||||
|
func f10[X, Y, Z any](a S2[X, int, Z], b S2[X, Y, bool])
|
||||||
|
|
||||||
|
func _[P any]() {
|
||||||
|
f10[int, float32, string](S2[int, int, string]{}, S2[int, float32, bool]{})
|
||||||
|
f10(S2[int, int, string]{}, S2[int, float32, bool]{})
|
||||||
|
f10(S2[P, int, P]{}, S2[P, float32, bool]{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// corner case for type inference
|
||||||
|
// (was bug: after instanting f11, the type-checker didn't mark f11 as non-generic)
|
||||||
|
|
||||||
|
func f11[T any]()
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
f11[int]()
|
||||||
|
}
|
||||||
|
|
||||||
|
// the previous example was extracted from
|
||||||
|
|
||||||
|
func f12[T interface{m() T}]()
|
||||||
|
|
||||||
|
type A[T any] T
|
||||||
|
|
||||||
|
func (a A[T]) m() A[T]
|
||||||
|
|
||||||
|
func _[T any]() {
|
||||||
|
f12(A[T])()
|
||||||
|
}
|
||||||
|
|
||||||
|
// method expressions
|
||||||
|
|
||||||
|
func (_ S1[P]) m()
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
m := S1[int].m
|
||||||
|
m(struct { f int }{42})
|
||||||
|
}
|
||||||
|
|
||||||
|
func _[T any] (x T) {
|
||||||
|
m := S1[T].m
|
||||||
|
m(S1[T]{x})
|
||||||
|
}
|
||||||
|
|
||||||
|
// type parameters in methods (generalization)
|
||||||
|
|
||||||
|
type R0 struct{}
|
||||||
|
|
||||||
|
func (R0) _[T any](x T)
|
||||||
|
func (R0 /* ERROR invalid receiver */ ) _[R0 any]() // scope of type parameters starts at "func"
|
||||||
|
|
||||||
|
type R1[A, B any] struct{}
|
||||||
|
|
||||||
|
func (_ R1[A, B]) m0(A, B)
|
||||||
|
func (_ R1[A, B]) m1[T any](A, B, T) T
|
||||||
|
func (_ R1 /* ERROR not a generic type */ [R1, _]) _()
|
||||||
|
func (_ R1[A, B]) _[A /* ERROR redeclared */ any](B)
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var r R1[int, string]
|
||||||
|
r.m1[rune](42, "foo", 'a')
|
||||||
|
r.m1[rune](42, "foo", 1.2 /* ERROR truncated to rune */)
|
||||||
|
r.m1(42, "foo", 1.2) // using type inference
|
||||||
|
var _ float64 = r.m1(42, "foo", 1.2)
|
||||||
|
}
|
||||||
|
|
||||||
|
type I1[A any] interface {
|
||||||
|
m1(A)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ I1[int] = r1[int]{}
|
||||||
|
|
||||||
|
type r1[T any] struct{}
|
||||||
|
|
||||||
|
func (_ r1[T]) m1(T)
|
||||||
|
|
||||||
|
type I2[A, B any] interface {
|
||||||
|
m1(A)
|
||||||
|
m2(A) B
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ I2[int, float32] = R2[int, float32]{}
|
||||||
|
|
||||||
|
type R2[P, Q any] struct{}
|
||||||
|
|
||||||
|
func (_ R2[X, Y]) m1(X)
|
||||||
|
func (_ R2[X, Y]) m2(X) Y
|
||||||
|
|
||||||
|
// type assertions and type switches over generic types
|
||||||
|
// NOTE: These are currently disabled because it's unclear what the correct
|
||||||
|
// approach is, and one can always work around by assigning the variable to
|
||||||
|
// an interface first.
|
||||||
|
|
||||||
|
// // ReadByte1 corresponds to the ReadByte example in the draft design.
|
||||||
|
// func ReadByte1[T io.Reader](r T) (byte, error) {
|
||||||
|
// if br, ok := r.(io.ByteReader); ok {
|
||||||
|
// return br.ReadByte()
|
||||||
|
// }
|
||||||
|
// var b [1]byte
|
||||||
|
// _, err := r.Read(b[:])
|
||||||
|
// return b[0], err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // ReadBytes2 is like ReadByte1 but uses a type switch instead.
|
||||||
|
// func ReadByte2[T io.Reader](r T) (byte, error) {
|
||||||
|
// switch br := r.(type) {
|
||||||
|
// case io.ByteReader:
|
||||||
|
// return br.ReadByte()
|
||||||
|
// }
|
||||||
|
// var b [1]byte
|
||||||
|
// _, err := r.Read(b[:])
|
||||||
|
// return b[0], err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // type assertions and type switches over generic types are strict
|
||||||
|
// type I3 interface {
|
||||||
|
// m(int)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// type I4 interface {
|
||||||
|
// m() int // different signature from I3.m
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func _[T I3](x I3, p T) {
|
||||||
|
// // type assertions and type switches over interfaces are not strict
|
||||||
|
// _ = x.(I4)
|
||||||
|
// switch x.(type) {
|
||||||
|
// case I4:
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // type assertions and type switches over generic types are strict
|
||||||
|
// _ = p /* ERROR cannot have dynamic type I4 */.(I4)
|
||||||
|
// switch p.(type) {
|
||||||
|
// case I4 /* ERROR cannot have dynamic type I4 */ :
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// type assertions and type switches over generic types lead to errors for now
|
||||||
|
|
||||||
|
func _[T any](x T) {
|
||||||
|
_ = x /* ERROR not an interface */ .(int)
|
||||||
|
switch x /* ERROR not an interface */ .(type) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// work-around
|
||||||
|
var t interface{} = x
|
||||||
|
_ = t.(int)
|
||||||
|
switch t.(type) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _[T interface{type int}](x T) {
|
||||||
|
_ = x /* ERROR not an interface */ .(int)
|
||||||
|
switch x /* ERROR not an interface */ .(type) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// work-around
|
||||||
|
var t interface{} = x
|
||||||
|
_ = t.(int)
|
||||||
|
switch t.(type) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// error messages related to type bounds mention those bounds
|
||||||
|
type C[P any] interface{}
|
||||||
|
|
||||||
|
func _[P C[P]] (x P) {
|
||||||
|
x.m /* ERROR x.m undefined */ ()
|
||||||
|
}
|
||||||
|
|
||||||
|
type I interface {}
|
||||||
|
|
||||||
|
func _[P I] (x P) {
|
||||||
|
x.m /* ERROR interface I has no method m */ ()
|
||||||
|
}
|
||||||
|
|
||||||
|
func _[P interface{}] (x P) {
|
||||||
|
x.m /* ERROR type bound for P has no method m */ ()
|
||||||
|
}
|
||||||
|
|
||||||
|
func _[P any] (x P) {
|
||||||
|
x.m /* ERROR type bound for P has no method m */ ()
|
||||||
|
}
|
||||||
|
|
||||||
|
// automatic distinguishing between array and generic types
|
||||||
|
// NOTE: Disabled when using unified parameter list syntax.
|
||||||
|
/*
|
||||||
|
const P = 10
|
||||||
|
type A1 [P]byte
|
||||||
|
func _(a A1) {
|
||||||
|
assert(len(a) == 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
type A2 [P]struct{
|
||||||
|
f [P]byte
|
||||||
|
}
|
||||||
|
func _(a A2) {
|
||||||
|
assert(len(a) == 10)
|
||||||
|
assert(len(a[0].f) == 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
type A3 [P]func(x [P]A3)
|
||||||
|
func _(a A3) {
|
||||||
|
assert(len(a) == 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
type T2[P] struct{ P }
|
||||||
|
var _ T2[int]
|
||||||
|
|
||||||
|
type T3[P] func(P)
|
||||||
|
var _ T3[int]
|
||||||
|
*/
|
||||||
22
src/cmd/compile/internal/syntax/testdata/tparams.go2
vendored
Normal file
22
src/cmd/compile/internal/syntax/testdata/tparams.go2
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
type t[ /* ERROR type parameters must be named */ a, b] struct{}
|
||||||
|
type t[a t, b t, /* ERROR type parameters must be named */ c] struct{}
|
||||||
|
type t struct {
|
||||||
|
t [n]byte
|
||||||
|
t[a]
|
||||||
|
t[a, b]
|
||||||
|
}
|
||||||
|
type t interface {
|
||||||
|
t[a]
|
||||||
|
m /* ERROR method cannot have type parameters */ [_ _, /* ERROR mixed */ _]()
|
||||||
|
t[a, b]
|
||||||
|
}
|
||||||
|
|
||||||
|
func f[ /* ERROR empty type parameter list */ ]()
|
||||||
|
func f[ /* ERROR type parameters must be named */ a, b]()
|
||||||
|
func f[a t, b t, /* ERROR type parameters must be named */ c]()
|
||||||
72
src/cmd/compile/internal/syntax/testing.go
Normal file
72
src/cmd/compile/internal/syntax/testing.go
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements testing support.
|
||||||
|
|
||||||
|
package syntax
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CommentsDo parses the given source and calls the provided handler for each
|
||||||
|
// comment or error. If the text provided to handler starts with a '/' it is
|
||||||
|
// the comment text; otherwise it is the error message.
|
||||||
|
func CommentsDo(src io.Reader, handler func(line, col uint, text string)) {
|
||||||
|
var s scanner
|
||||||
|
s.init(src, handler, comments)
|
||||||
|
for s.tok != _EOF {
|
||||||
|
s.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ERROR comments must start with text `ERROR "msg"` or `ERROR msg`.
|
||||||
|
// Space around "msg" or msg is ignored.
|
||||||
|
var errRx = regexp.MustCompile(`^ *ERROR *"?([^"]*)"?`)
|
||||||
|
|
||||||
|
// ErrorMap collects all comments with comment text of the form
|
||||||
|
// `ERROR "msg"` or `ERROR msg` from the given src and returns them
|
||||||
|
// as []Error lists in a map indexed by line number. The position
|
||||||
|
// for each Error is the position of the token immediately preceding
|
||||||
|
// the comment, the Error message is the message msg extracted from
|
||||||
|
// the comment, with all errors that are on the same line collected
|
||||||
|
// in a slice. If there is no preceding token (the `ERROR` comment
|
||||||
|
// appears in the beginning of the file), then the recorded position
|
||||||
|
// is unknown (line, col = 0, 0). If there are no ERROR comments, the
|
||||||
|
// result is nil.
|
||||||
|
func ErrorMap(src io.Reader) (errmap map[uint][]Error) {
|
||||||
|
// position of previous token
|
||||||
|
var base *PosBase
|
||||||
|
var prev struct{ line, col uint }
|
||||||
|
|
||||||
|
var s scanner
|
||||||
|
s.init(src, func(_, _ uint, text string) {
|
||||||
|
if text[0] != '/' {
|
||||||
|
return // error, ignore
|
||||||
|
}
|
||||||
|
if text[1] == '*' {
|
||||||
|
text = text[:len(text)-2] // strip trailing */
|
||||||
|
}
|
||||||
|
if s := errRx.FindStringSubmatch(text[2:]); len(s) == 2 {
|
||||||
|
pos := MakePos(base, prev.line, prev.col)
|
||||||
|
err := Error{pos, strings.TrimSpace(s[1])}
|
||||||
|
if errmap == nil {
|
||||||
|
errmap = make(map[uint][]Error)
|
||||||
|
}
|
||||||
|
errmap[prev.line] = append(errmap[prev.line], err)
|
||||||
|
}
|
||||||
|
}, comments)
|
||||||
|
|
||||||
|
for s.tok != _EOF {
|
||||||
|
s.next()
|
||||||
|
if s.tok == _Semi && s.lit != "semicolon" {
|
||||||
|
continue // ignore automatically inserted semicolons
|
||||||
|
}
|
||||||
|
prev.line, prev.col = s.line, s.col
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
45
src/cmd/compile/internal/syntax/testing_test.go
Normal file
45
src/cmd/compile/internal/syntax/testing_test.go
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package syntax
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestErrorMap(t *testing.T) {
|
||||||
|
const src = `/* ERROR 0:0 */ /* ERROR "0:0" */ // ERROR 0:0
|
||||||
|
// ERROR "0:0"
|
||||||
|
x /* ERROR 3:1 */ // ignore automatically inserted semicolon here
|
||||||
|
/* ERROR 3:1 */ // position of x on previous line
|
||||||
|
x /* ERROR 5:4 */ ; // do not ignore this semicolon
|
||||||
|
/* ERROR 5:22 */ // position of ; on previous line
|
||||||
|
package /* ERROR 7:2 */ // indented with tab
|
||||||
|
import /* ERROR 8:9 */ // indented with blanks
|
||||||
|
`
|
||||||
|
m := ErrorMap(strings.NewReader(src))
|
||||||
|
got := 0 // number of errors found
|
||||||
|
for line, errlist := range m {
|
||||||
|
for _, err := range errlist {
|
||||||
|
if err.Pos.Line() != line {
|
||||||
|
t.Errorf("%v: got map line %d; want %d", err, err.Pos.Line(), line)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// err.Pos.Line() == line
|
||||||
|
msg := fmt.Sprintf("%d:%d", line, err.Pos.Col())
|
||||||
|
if err.Msg != msg {
|
||||||
|
t.Errorf("%v: got msg %q; want %q", err, err.Msg, msg)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
got += len(errlist)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := strings.Count(src, "ERROR")
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("ErrorMap got %d errors; want %d", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
318
src/cmd/compile/internal/syntax/walk.go
Normal file
318
src/cmd/compile/internal/syntax/walk.go
Normal file
|
|
@ -0,0 +1,318 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements syntax tree walking.
|
||||||
|
|
||||||
|
package syntax
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Walk traverses a syntax in pre-order: It starts by calling f(root);
|
||||||
|
// root must not be nil. If f returns false (== "continue"), Walk calls
|
||||||
|
// f recursively for each of the non-nil children of that node; if f
|
||||||
|
// returns true (== "stop"), Walk does not traverse the respective node's
|
||||||
|
// children.
|
||||||
|
// Some nodes may be shared among multiple parent nodes (e.g., types in
|
||||||
|
// field lists such as type T in "a, b, c T"). Such shared nodes are
|
||||||
|
// walked multiple times.
|
||||||
|
// TODO(gri) Revisit this design. It may make sense to walk those nodes
|
||||||
|
// only once. A place where this matters is types2.TestResolveIdents.
|
||||||
|
func Walk(root Node, f func(Node) bool) {
|
||||||
|
w := walker{f}
|
||||||
|
w.node(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
type walker struct {
|
||||||
|
f func(Node) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *walker) node(n Node) {
|
||||||
|
if n == nil {
|
||||||
|
panic("invalid syntax tree: nil node")
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.f(n) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch n := n.(type) {
|
||||||
|
// packages
|
||||||
|
case *File:
|
||||||
|
w.node(n.PkgName)
|
||||||
|
w.declList(n.DeclList)
|
||||||
|
|
||||||
|
// declarations
|
||||||
|
case *ImportDecl:
|
||||||
|
if n.LocalPkgName != nil {
|
||||||
|
w.node(n.LocalPkgName)
|
||||||
|
}
|
||||||
|
w.node(n.Path)
|
||||||
|
|
||||||
|
case *ConstDecl:
|
||||||
|
w.nameList(n.NameList)
|
||||||
|
if n.Type != nil {
|
||||||
|
w.node(n.Type)
|
||||||
|
}
|
||||||
|
if n.Values != nil {
|
||||||
|
w.node(n.Values)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *TypeDecl:
|
||||||
|
w.node(n.Name)
|
||||||
|
w.fieldList(n.TParamList)
|
||||||
|
w.node(n.Type)
|
||||||
|
|
||||||
|
case *VarDecl:
|
||||||
|
w.nameList(n.NameList)
|
||||||
|
if n.Type != nil {
|
||||||
|
w.node(n.Type)
|
||||||
|
}
|
||||||
|
if n.Values != nil {
|
||||||
|
w.node(n.Values)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *FuncDecl:
|
||||||
|
if n.Recv != nil {
|
||||||
|
w.node(n.Recv)
|
||||||
|
}
|
||||||
|
w.node(n.Name)
|
||||||
|
w.fieldList(n.TParamList)
|
||||||
|
w.node(n.Type)
|
||||||
|
if n.Body != nil {
|
||||||
|
w.node(n.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// expressions
|
||||||
|
case *BadExpr: // nothing to do
|
||||||
|
case *Name: // nothing to do
|
||||||
|
case *BasicLit: // nothing to do
|
||||||
|
|
||||||
|
case *CompositeLit:
|
||||||
|
if n.Type != nil {
|
||||||
|
w.node(n.Type)
|
||||||
|
}
|
||||||
|
w.exprList(n.ElemList)
|
||||||
|
|
||||||
|
case *KeyValueExpr:
|
||||||
|
w.node(n.Key)
|
||||||
|
w.node(n.Value)
|
||||||
|
|
||||||
|
case *FuncLit:
|
||||||
|
w.node(n.Type)
|
||||||
|
w.node(n.Body)
|
||||||
|
|
||||||
|
case *ParenExpr:
|
||||||
|
w.node(n.X)
|
||||||
|
|
||||||
|
case *SelectorExpr:
|
||||||
|
w.node(n.X)
|
||||||
|
w.node(n.Sel)
|
||||||
|
|
||||||
|
case *IndexExpr:
|
||||||
|
w.node(n.X)
|
||||||
|
w.node(n.Index)
|
||||||
|
|
||||||
|
case *SliceExpr:
|
||||||
|
w.node(n.X)
|
||||||
|
for _, x := range n.Index {
|
||||||
|
if x != nil {
|
||||||
|
w.node(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *AssertExpr:
|
||||||
|
w.node(n.X)
|
||||||
|
w.node(n.Type)
|
||||||
|
|
||||||
|
case *TypeSwitchGuard:
|
||||||
|
if n.Lhs != nil {
|
||||||
|
w.node(n.Lhs)
|
||||||
|
}
|
||||||
|
w.node(n.X)
|
||||||
|
|
||||||
|
case *Operation:
|
||||||
|
w.node(n.X)
|
||||||
|
if n.Y != nil {
|
||||||
|
w.node(n.Y)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *CallExpr:
|
||||||
|
w.node(n.Fun)
|
||||||
|
w.exprList(n.ArgList)
|
||||||
|
|
||||||
|
case *ListExpr:
|
||||||
|
w.exprList(n.ElemList)
|
||||||
|
|
||||||
|
// types
|
||||||
|
case *ArrayType:
|
||||||
|
if n.Len != nil {
|
||||||
|
w.node(n.Len)
|
||||||
|
}
|
||||||
|
w.node(n.Elem)
|
||||||
|
|
||||||
|
case *SliceType:
|
||||||
|
w.node(n.Elem)
|
||||||
|
|
||||||
|
case *DotsType:
|
||||||
|
w.node(n.Elem)
|
||||||
|
|
||||||
|
case *StructType:
|
||||||
|
w.fieldList(n.FieldList)
|
||||||
|
for _, t := range n.TagList {
|
||||||
|
if t != nil {
|
||||||
|
w.node(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Field:
|
||||||
|
if n.Name != nil {
|
||||||
|
w.node(n.Name)
|
||||||
|
}
|
||||||
|
w.node(n.Type)
|
||||||
|
|
||||||
|
case *InterfaceType:
|
||||||
|
w.fieldList(n.MethodList)
|
||||||
|
|
||||||
|
case *FuncType:
|
||||||
|
w.fieldList(n.ParamList)
|
||||||
|
w.fieldList(n.ResultList)
|
||||||
|
|
||||||
|
case *MapType:
|
||||||
|
w.node(n.Key)
|
||||||
|
w.node(n.Value)
|
||||||
|
|
||||||
|
case *ChanType:
|
||||||
|
w.node(n.Elem)
|
||||||
|
|
||||||
|
// statements
|
||||||
|
case *EmptyStmt: // nothing to do
|
||||||
|
|
||||||
|
case *LabeledStmt:
|
||||||
|
w.node(n.Label)
|
||||||
|
w.node(n.Stmt)
|
||||||
|
|
||||||
|
case *BlockStmt:
|
||||||
|
w.stmtList(n.List)
|
||||||
|
|
||||||
|
case *ExprStmt:
|
||||||
|
w.node(n.X)
|
||||||
|
|
||||||
|
case *SendStmt:
|
||||||
|
w.node(n.Chan)
|
||||||
|
w.node(n.Value)
|
||||||
|
|
||||||
|
case *DeclStmt:
|
||||||
|
w.declList(n.DeclList)
|
||||||
|
|
||||||
|
case *AssignStmt:
|
||||||
|
w.node(n.Lhs)
|
||||||
|
if n.Rhs != nil {
|
||||||
|
w.node(n.Rhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *BranchStmt:
|
||||||
|
if n.Label != nil {
|
||||||
|
w.node(n.Label)
|
||||||
|
}
|
||||||
|
// Target points to nodes elsewhere in the syntax tree
|
||||||
|
|
||||||
|
case *CallStmt:
|
||||||
|
w.node(n.Call)
|
||||||
|
|
||||||
|
case *ReturnStmt:
|
||||||
|
if n.Results != nil {
|
||||||
|
w.node(n.Results)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *IfStmt:
|
||||||
|
if n.Init != nil {
|
||||||
|
w.node(n.Init)
|
||||||
|
}
|
||||||
|
w.node(n.Cond)
|
||||||
|
w.node(n.Then)
|
||||||
|
if n.Else != nil {
|
||||||
|
w.node(n.Else)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ForStmt:
|
||||||
|
if n.Init != nil {
|
||||||
|
w.node(n.Init)
|
||||||
|
}
|
||||||
|
if n.Cond != nil {
|
||||||
|
w.node(n.Cond)
|
||||||
|
}
|
||||||
|
if n.Post != nil {
|
||||||
|
w.node(n.Post)
|
||||||
|
}
|
||||||
|
w.node(n.Body)
|
||||||
|
|
||||||
|
case *SwitchStmt:
|
||||||
|
if n.Init != nil {
|
||||||
|
w.node(n.Init)
|
||||||
|
}
|
||||||
|
if n.Tag != nil {
|
||||||
|
w.node(n.Tag)
|
||||||
|
}
|
||||||
|
for _, s := range n.Body {
|
||||||
|
w.node(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *SelectStmt:
|
||||||
|
for _, s := range n.Body {
|
||||||
|
w.node(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper nodes
|
||||||
|
case *RangeClause:
|
||||||
|
if n.Lhs != nil {
|
||||||
|
w.node(n.Lhs)
|
||||||
|
}
|
||||||
|
w.node(n.X)
|
||||||
|
|
||||||
|
case *CaseClause:
|
||||||
|
if n.Cases != nil {
|
||||||
|
w.node(n.Cases)
|
||||||
|
}
|
||||||
|
w.stmtList(n.Body)
|
||||||
|
|
||||||
|
case *CommClause:
|
||||||
|
if n.Comm != nil {
|
||||||
|
w.node(n.Comm)
|
||||||
|
}
|
||||||
|
w.stmtList(n.Body)
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("internal error: unknown node type %T", n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *walker) declList(list []Decl) {
|
||||||
|
for _, n := range list {
|
||||||
|
w.node(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *walker) exprList(list []Expr) {
|
||||||
|
for _, n := range list {
|
||||||
|
w.node(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *walker) stmtList(list []Stmt) {
|
||||||
|
for _, n := range list {
|
||||||
|
w.node(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *walker) nameList(list []*Name) {
|
||||||
|
for _, n := range list {
|
||||||
|
w.node(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *walker) fieldList(list []*Field) {
|
||||||
|
for _, n := range list {
|
||||||
|
w.node(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -57,7 +57,7 @@ func mkFuncType(rcvr *types.Type, ins []*types.Type, outs []*types.Type) *types.
|
||||||
if rcvr != nil {
|
if rcvr != nil {
|
||||||
rf = mkParamResultField(rcvr, q, ir.PPARAM)
|
rf = mkParamResultField(rcvr, q, ir.PPARAM)
|
||||||
}
|
}
|
||||||
return types.NewSignature(types.LocalPkg, rf, inf, outf)
|
return types.NewSignature(types.LocalPkg, rf, nil, inf, outf)
|
||||||
}
|
}
|
||||||
|
|
||||||
type expectedDump struct {
|
type expectedDump struct {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,12 @@ func TestDeps(t *testing.T) {
|
||||||
for _, dep := range strings.Fields(strings.Trim(string(out), "[]")) {
|
for _, dep := range strings.Fields(strings.Trim(string(out), "[]")) {
|
||||||
switch dep {
|
switch dep {
|
||||||
case "go/build", "go/scanner":
|
case "go/build", "go/scanner":
|
||||||
t.Errorf("undesired dependency on %q", dep)
|
// cmd/compile/internal/importer introduces a dependency
|
||||||
|
// on go/build and go/token; cmd/compile/internal/ uses
|
||||||
|
// go/constant which uses go/token in its API. Once we
|
||||||
|
// got rid of those dependencies, enable this check again.
|
||||||
|
// TODO(gri) fix this
|
||||||
|
// t.Errorf("undesired dependency on %q", dep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -205,139 +205,153 @@ var runtimeDecls = [...]struct {
|
||||||
{"arm64HasATOMICS", varTag, 6},
|
{"arm64HasATOMICS", varTag, 6},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Not inlining this function removes a significant chunk of init code.
|
||||||
|
//go:noinline
|
||||||
|
func newSig(params, results []*types.Field) *types.Type {
|
||||||
|
return types.NewSignature(types.NoPkg, nil, nil, params, results)
|
||||||
|
}
|
||||||
|
|
||||||
|
func params(tlist ...*types.Type) []*types.Field {
|
||||||
|
flist := make([]*types.Field, len(tlist))
|
||||||
|
for i, typ := range tlist {
|
||||||
|
flist[i] = types.NewField(src.NoXPos, nil, typ)
|
||||||
|
}
|
||||||
|
return flist
|
||||||
|
}
|
||||||
|
|
||||||
func runtimeTypes() []*types.Type {
|
func runtimeTypes() []*types.Type {
|
||||||
var typs [132]*types.Type
|
var typs [132]*types.Type
|
||||||
typs[0] = types.ByteType
|
typs[0] = types.ByteType
|
||||||
typs[1] = types.NewPtr(typs[0])
|
typs[1] = types.NewPtr(typs[0])
|
||||||
typs[2] = types.Types[types.TANY]
|
typs[2] = types.Types[types.TANY]
|
||||||
typs[3] = types.NewPtr(typs[2])
|
typs[3] = types.NewPtr(typs[2])
|
||||||
typs[4] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[3])})
|
typs[4] = newSig(params(typs[1]), params(typs[3]))
|
||||||
typs[5] = types.Types[types.TUINTPTR]
|
typs[5] = types.Types[types.TUINTPTR]
|
||||||
typs[6] = types.Types[types.TBOOL]
|
typs[6] = types.Types[types.TBOOL]
|
||||||
typs[7] = types.Types[types.TUNSAFEPTR]
|
typs[7] = types.Types[types.TUNSAFEPTR]
|
||||||
typs[8] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[5]), types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[6])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[7])})
|
typs[8] = newSig(params(typs[5], typs[1], typs[6]), params(typs[7]))
|
||||||
typs[9] = types.NewSignature(types.NoPkg, nil, nil, nil)
|
typs[9] = newSig(nil, nil)
|
||||||
typs[10] = types.Types[types.TINTER]
|
typs[10] = types.Types[types.TINTER]
|
||||||
typs[11] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[10])}, nil)
|
typs[11] = newSig(params(typs[10]), nil)
|
||||||
typs[12] = types.Types[types.TINT32]
|
typs[12] = types.Types[types.TINT32]
|
||||||
typs[13] = types.NewPtr(typs[12])
|
typs[13] = types.NewPtr(typs[12])
|
||||||
typs[14] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[13])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[10])})
|
typs[14] = newSig(params(typs[13]), params(typs[10]))
|
||||||
typs[15] = types.Types[types.TINT]
|
typs[15] = types.Types[types.TINT]
|
||||||
typs[16] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[15])}, nil)
|
typs[16] = newSig(params(typs[15], typs[15]), nil)
|
||||||
typs[17] = types.Types[types.TUINT]
|
typs[17] = types.Types[types.TUINT]
|
||||||
typs[18] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[17]), types.NewField(src.NoXPos, nil, typs[15])}, nil)
|
typs[18] = newSig(params(typs[17], typs[15]), nil)
|
||||||
typs[19] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])}, nil)
|
typs[19] = newSig(params(typs[6]), nil)
|
||||||
typs[20] = types.Types[types.TFLOAT64]
|
typs[20] = types.Types[types.TFLOAT64]
|
||||||
typs[21] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[20])}, nil)
|
typs[21] = newSig(params(typs[20]), nil)
|
||||||
typs[22] = types.Types[types.TINT64]
|
typs[22] = types.Types[types.TINT64]
|
||||||
typs[23] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[22])}, nil)
|
typs[23] = newSig(params(typs[22]), nil)
|
||||||
typs[24] = types.Types[types.TUINT64]
|
typs[24] = types.Types[types.TUINT64]
|
||||||
typs[25] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[24])}, nil)
|
typs[25] = newSig(params(typs[24]), nil)
|
||||||
typs[26] = types.Types[types.TCOMPLEX128]
|
typs[26] = types.Types[types.TCOMPLEX128]
|
||||||
typs[27] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[26])}, nil)
|
typs[27] = newSig(params(typs[26]), nil)
|
||||||
typs[28] = types.Types[types.TSTRING]
|
typs[28] = types.Types[types.TSTRING]
|
||||||
typs[29] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])}, nil)
|
typs[29] = newSig(params(typs[28]), nil)
|
||||||
typs[30] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[2])}, nil)
|
typs[30] = newSig(params(typs[2]), nil)
|
||||||
typs[31] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[5])}, nil)
|
typs[31] = newSig(params(typs[5]), nil)
|
||||||
typs[32] = types.NewArray(typs[0], 32)
|
typs[32] = types.NewArray(typs[0], 32)
|
||||||
typs[33] = types.NewPtr(typs[32])
|
typs[33] = types.NewPtr(typs[32])
|
||||||
typs[34] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
|
typs[34] = newSig(params(typs[33], typs[28], typs[28]), params(typs[28]))
|
||||||
typs[35] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
|
typs[35] = newSig(params(typs[33], typs[28], typs[28], typs[28]), params(typs[28]))
|
||||||
typs[36] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
|
typs[36] = newSig(params(typs[33], typs[28], typs[28], typs[28], typs[28]), params(typs[28]))
|
||||||
typs[37] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
|
typs[37] = newSig(params(typs[33], typs[28], typs[28], typs[28], typs[28], typs[28]), params(typs[28]))
|
||||||
typs[38] = types.NewSlice(typs[28])
|
typs[38] = types.NewSlice(typs[28])
|
||||||
typs[39] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[38])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
|
typs[39] = newSig(params(typs[33], typs[38]), params(typs[28]))
|
||||||
typs[40] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[15])})
|
typs[40] = newSig(params(typs[28], typs[28]), params(typs[15]))
|
||||||
typs[41] = types.NewArray(typs[0], 4)
|
typs[41] = types.NewArray(typs[0], 4)
|
||||||
typs[42] = types.NewPtr(typs[41])
|
typs[42] = types.NewPtr(typs[41])
|
||||||
typs[43] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[42]), types.NewField(src.NoXPos, nil, typs[22])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
|
typs[43] = newSig(params(typs[42], typs[22]), params(typs[28]))
|
||||||
typs[44] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[15])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
|
typs[44] = newSig(params(typs[33], typs[1], typs[15]), params(typs[28]))
|
||||||
typs[45] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[15])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
|
typs[45] = newSig(params(typs[1], typs[15]), params(typs[28]))
|
||||||
typs[46] = types.RuneType
|
typs[46] = types.RuneType
|
||||||
typs[47] = types.NewSlice(typs[46])
|
typs[47] = types.NewSlice(typs[46])
|
||||||
typs[48] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[47])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])})
|
typs[48] = newSig(params(typs[33], typs[47]), params(typs[28]))
|
||||||
typs[49] = types.NewSlice(typs[0])
|
typs[49] = types.NewSlice(typs[0])
|
||||||
typs[50] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[33]), types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[49])})
|
typs[50] = newSig(params(typs[33], typs[28]), params(typs[49]))
|
||||||
typs[51] = types.NewArray(typs[46], 32)
|
typs[51] = types.NewArray(typs[46], 32)
|
||||||
typs[52] = types.NewPtr(typs[51])
|
typs[52] = types.NewPtr(typs[51])
|
||||||
typs[53] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[52]), types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[47])})
|
typs[53] = newSig(params(typs[52], typs[28]), params(typs[47]))
|
||||||
typs[54] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[5])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[15])})
|
typs[54] = newSig(params(typs[3], typs[15], typs[3], typs[15], typs[5]), params(typs[15]))
|
||||||
typs[55] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[28]), types.NewField(src.NoXPos, nil, typs[15])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[46]), types.NewField(src.NoXPos, nil, typs[15])})
|
typs[55] = newSig(params(typs[28], typs[15]), params(typs[46], typs[15]))
|
||||||
typs[56] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[28])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[15])})
|
typs[56] = newSig(params(typs[28]), params(typs[15]))
|
||||||
typs[57] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[2])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[2])})
|
typs[57] = newSig(params(typs[1], typs[2]), params(typs[2]))
|
||||||
typs[58] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[2])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[7])})
|
typs[58] = newSig(params(typs[2]), params(typs[7]))
|
||||||
typs[59] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[2])})
|
typs[59] = newSig(params(typs[1], typs[3]), params(typs[2]))
|
||||||
typs[60] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[2])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[2]), types.NewField(src.NoXPos, nil, typs[6])})
|
typs[60] = newSig(params(typs[1], typs[2]), params(typs[2], typs[6]))
|
||||||
typs[61] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[1])}, nil)
|
typs[61] = newSig(params(typs[1], typs[1], typs[1]), nil)
|
||||||
typs[62] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1])}, nil)
|
typs[62] = newSig(params(typs[1]), nil)
|
||||||
typs[63] = types.NewPtr(typs[5])
|
typs[63] = types.NewPtr(typs[5])
|
||||||
typs[64] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[63]), types.NewField(src.NoXPos, nil, typs[7]), types.NewField(src.NoXPos, nil, typs[7])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
|
typs[64] = newSig(params(typs[63], typs[7], typs[7]), params(typs[6]))
|
||||||
typs[65] = types.Types[types.TUINT32]
|
typs[65] = types.Types[types.TUINT32]
|
||||||
typs[66] = types.NewSignature(types.NoPkg, nil, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[65])})
|
typs[66] = newSig(nil, params(typs[65]))
|
||||||
typs[67] = types.NewMap(typs[2], typs[2])
|
typs[67] = types.NewMap(typs[2], typs[2])
|
||||||
typs[68] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[22]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[67])})
|
typs[68] = newSig(params(typs[1], typs[22], typs[3]), params(typs[67]))
|
||||||
typs[69] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[67])})
|
typs[69] = newSig(params(typs[1], typs[15], typs[3]), params(typs[67]))
|
||||||
typs[70] = types.NewSignature(types.NoPkg, nil, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[67])})
|
typs[70] = newSig(nil, params(typs[67]))
|
||||||
typs[71] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[3])})
|
typs[71] = newSig(params(typs[1], typs[67], typs[3]), params(typs[3]))
|
||||||
typs[72] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[2])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[3])})
|
typs[72] = newSig(params(typs[1], typs[67], typs[2]), params(typs[3]))
|
||||||
typs[73] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[1])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[3])})
|
typs[73] = newSig(params(typs[1], typs[67], typs[3], typs[1]), params(typs[3]))
|
||||||
typs[74] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[6])})
|
typs[74] = newSig(params(typs[1], typs[67], typs[3]), params(typs[3], typs[6]))
|
||||||
typs[75] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[2])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[6])})
|
typs[75] = newSig(params(typs[1], typs[67], typs[2]), params(typs[3], typs[6]))
|
||||||
typs[76] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[1])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[6])})
|
typs[76] = newSig(params(typs[1], typs[67], typs[3], typs[1]), params(typs[3], typs[6]))
|
||||||
typs[77] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[3])}, nil)
|
typs[77] = newSig(params(typs[1], typs[67], typs[3]), nil)
|
||||||
typs[78] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67]), types.NewField(src.NoXPos, nil, typs[2])}, nil)
|
typs[78] = newSig(params(typs[1], typs[67], typs[2]), nil)
|
||||||
typs[79] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[3])}, nil)
|
typs[79] = newSig(params(typs[3]), nil)
|
||||||
typs[80] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[67])}, nil)
|
typs[80] = newSig(params(typs[1], typs[67]), nil)
|
||||||
typs[81] = types.NewChan(typs[2], types.Cboth)
|
typs[81] = types.NewChan(typs[2], types.Cboth)
|
||||||
typs[82] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[22])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[81])})
|
typs[82] = newSig(params(typs[1], typs[22]), params(typs[81]))
|
||||||
typs[83] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[15])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[81])})
|
typs[83] = newSig(params(typs[1], typs[15]), params(typs[81]))
|
||||||
typs[84] = types.NewChan(typs[2], types.Crecv)
|
typs[84] = types.NewChan(typs[2], types.Crecv)
|
||||||
typs[85] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[84]), types.NewField(src.NoXPos, nil, typs[3])}, nil)
|
typs[85] = newSig(params(typs[84], typs[3]), nil)
|
||||||
typs[86] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[84]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
|
typs[86] = newSig(params(typs[84], typs[3]), params(typs[6]))
|
||||||
typs[87] = types.NewChan(typs[2], types.Csend)
|
typs[87] = types.NewChan(typs[2], types.Csend)
|
||||||
typs[88] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[87]), types.NewField(src.NoXPos, nil, typs[3])}, nil)
|
typs[88] = newSig(params(typs[87], typs[3]), nil)
|
||||||
typs[89] = types.NewArray(typs[0], 3)
|
typs[89] = types.NewArray(typs[0], 3)
|
||||||
typs[90] = types.NewStruct(types.NoPkg, []*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[89]), types.NewField(src.NoXPos, Lookup("needed"), typs[6]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])})
|
typs[90] = types.NewStruct(types.NoPkg, []*types.Field{types.NewField(src.NoXPos, Lookup("enabled"), typs[6]), types.NewField(src.NoXPos, Lookup("pad"), typs[89]), types.NewField(src.NoXPos, Lookup("needed"), typs[6]), types.NewField(src.NoXPos, Lookup("cgo"), typs[6]), types.NewField(src.NoXPos, Lookup("alignme"), typs[24])})
|
||||||
typs[91] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[3])}, nil)
|
typs[91] = newSig(params(typs[1], typs[3], typs[3]), nil)
|
||||||
typs[92] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[3])}, nil)
|
typs[92] = newSig(params(typs[1], typs[3]), nil)
|
||||||
typs[93] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[15])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[15])})
|
typs[93] = newSig(params(typs[1], typs[3], typs[15], typs[3], typs[15]), params(typs[15]))
|
||||||
typs[94] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[87]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
|
typs[94] = newSig(params(typs[87], typs[3]), params(typs[6]))
|
||||||
typs[95] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[84])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
|
typs[95] = newSig(params(typs[3], typs[84]), params(typs[6]))
|
||||||
typs[96] = types.NewPtr(typs[6])
|
typs[96] = types.NewPtr(typs[6])
|
||||||
typs[97] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[96]), types.NewField(src.NoXPos, nil, typs[84])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
|
typs[97] = newSig(params(typs[3], typs[96], typs[84]), params(typs[6]))
|
||||||
typs[98] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[63])}, nil)
|
typs[98] = newSig(params(typs[63]), nil)
|
||||||
typs[99] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[63]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[6])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[6])})
|
typs[99] = newSig(params(typs[1], typs[1], typs[63], typs[15], typs[15], typs[6]), params(typs[15], typs[6]))
|
||||||
typs[100] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[15])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[7])})
|
typs[100] = newSig(params(typs[1], typs[15], typs[15]), params(typs[7]))
|
||||||
typs[101] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[22]), types.NewField(src.NoXPos, nil, typs[22])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[7])})
|
typs[101] = newSig(params(typs[1], typs[22], typs[22]), params(typs[7]))
|
||||||
typs[102] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[15]), types.NewField(src.NoXPos, nil, typs[7])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[7])})
|
typs[102] = newSig(params(typs[1], typs[15], typs[15], typs[7]), params(typs[7]))
|
||||||
typs[103] = types.NewSlice(typs[2])
|
typs[103] = types.NewSlice(typs[2])
|
||||||
typs[104] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[103]), types.NewField(src.NoXPos, nil, typs[15])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[103])})
|
typs[104] = newSig(params(typs[1], typs[103], typs[15]), params(typs[103]))
|
||||||
typs[105] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[5])}, nil)
|
typs[105] = newSig(params(typs[3], typs[3], typs[5]), nil)
|
||||||
typs[106] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[7]), types.NewField(src.NoXPos, nil, typs[5])}, nil)
|
typs[106] = newSig(params(typs[7], typs[5]), nil)
|
||||||
typs[107] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[5])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
|
typs[107] = newSig(params(typs[3], typs[3], typs[5]), params(typs[6]))
|
||||||
typs[108] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[3]), types.NewField(src.NoXPos, nil, typs[3])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
|
typs[108] = newSig(params(typs[3], typs[3]), params(typs[6]))
|
||||||
typs[109] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[7]), types.NewField(src.NoXPos, nil, typs[7])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[6])})
|
typs[109] = newSig(params(typs[7], typs[7]), params(typs[6]))
|
||||||
typs[110] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[7]), types.NewField(src.NoXPos, nil, typs[5]), types.NewField(src.NoXPos, nil, typs[5])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[5])})
|
typs[110] = newSig(params(typs[7], typs[5], typs[5]), params(typs[5]))
|
||||||
typs[111] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[7]), types.NewField(src.NoXPos, nil, typs[5])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[5])})
|
typs[111] = newSig(params(typs[7], typs[5]), params(typs[5]))
|
||||||
typs[112] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[22]), types.NewField(src.NoXPos, nil, typs[22])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[22])})
|
typs[112] = newSig(params(typs[22], typs[22]), params(typs[22]))
|
||||||
typs[113] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[24]), types.NewField(src.NoXPos, nil, typs[24])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[24])})
|
typs[113] = newSig(params(typs[24], typs[24]), params(typs[24]))
|
||||||
typs[114] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[20])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[22])})
|
typs[114] = newSig(params(typs[20]), params(typs[22]))
|
||||||
typs[115] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[20])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[24])})
|
typs[115] = newSig(params(typs[20]), params(typs[24]))
|
||||||
typs[116] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[20])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[65])})
|
typs[116] = newSig(params(typs[20]), params(typs[65]))
|
||||||
typs[117] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[22])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[20])})
|
typs[117] = newSig(params(typs[22]), params(typs[20]))
|
||||||
typs[118] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[24])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[20])})
|
typs[118] = newSig(params(typs[24]), params(typs[20]))
|
||||||
typs[119] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[65])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[20])})
|
typs[119] = newSig(params(typs[65]), params(typs[20]))
|
||||||
typs[120] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[26]), types.NewField(src.NoXPos, nil, typs[26])}, []*types.Field{types.NewField(src.NoXPos, nil, typs[26])})
|
typs[120] = newSig(params(typs[26], typs[26]), params(typs[26]))
|
||||||
typs[121] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[5]), types.NewField(src.NoXPos, nil, typs[5])}, nil)
|
typs[121] = newSig(params(typs[5], typs[5]), nil)
|
||||||
typs[122] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[5]), types.NewField(src.NoXPos, nil, typs[5]), types.NewField(src.NoXPos, nil, typs[5])}, nil)
|
typs[122] = newSig(params(typs[5], typs[5], typs[5]), nil)
|
||||||
typs[123] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[7]), types.NewField(src.NoXPos, nil, typs[1]), types.NewField(src.NoXPos, nil, typs[5])}, nil)
|
typs[123] = newSig(params(typs[7], typs[1], typs[5]), nil)
|
||||||
typs[124] = types.NewSlice(typs[7])
|
typs[124] = types.NewSlice(typs[7])
|
||||||
typs[125] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[7]), types.NewField(src.NoXPos, nil, typs[124])}, nil)
|
typs[125] = newSig(params(typs[7], typs[124]), nil)
|
||||||
typs[126] = types.Types[types.TUINT8]
|
typs[126] = types.Types[types.TUINT8]
|
||||||
typs[127] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[126]), types.NewField(src.NoXPos, nil, typs[126])}, nil)
|
typs[127] = newSig(params(typs[126], typs[126]), nil)
|
||||||
typs[128] = types.Types[types.TUINT16]
|
typs[128] = types.Types[types.TUINT16]
|
||||||
typs[129] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[128]), types.NewField(src.NoXPos, nil, typs[128])}, nil)
|
typs[129] = newSig(params(typs[128], typs[128]), nil)
|
||||||
typs[130] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[65]), types.NewField(src.NoXPos, nil, typs[65])}, nil)
|
typs[130] = newSig(params(typs[65], typs[65]), nil)
|
||||||
typs[131] = types.NewSignature(types.NoPkg, nil, []*types.Field{types.NewField(src.NoXPos, nil, typs[24]), types.NewField(src.NoXPos, nil, typs[24])}, nil)
|
typs[131] = newSig(params(typs[24], typs[24]), nil)
|
||||||
return typs[:]
|
return typs[:]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,7 +449,7 @@ func EvalConst(n ir.Node) ir.Node {
|
||||||
n := n.(*ir.BinaryExpr)
|
n := n.(*ir.BinaryExpr)
|
||||||
nl, nr := n.X, n.Y
|
nl, nr := n.X, n.Y
|
||||||
if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL {
|
if nl.Op() == ir.OLITERAL && nr.Op() == ir.OLITERAL {
|
||||||
// shiftBound from go/types; "so we can express smallestFloat64"
|
// shiftBound from go/types; "so we can express smallestFloat64" (see issue #44057)
|
||||||
const shiftBound = 1023 - 1 + 52
|
const shiftBound = 1023 - 1 + 52
|
||||||
s, ok := constant.Uint64Val(nr.Val())
|
s, ok := constant.Uint64Val(nr.Val())
|
||||||
if !ok || s > shiftBound {
|
if !ok || s > shiftBound {
|
||||||
|
|
|
||||||
|
|
@ -470,5 +470,5 @@ func NewMethodType(sig *types.Type, recv *types.Type) *types.Type {
|
||||||
results[i] = types.NewField(base.Pos, nil, t.Type)
|
results[i] = types.NewField(base.Pos, nil, t.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
return types.NewSignature(types.LocalPkg, nil, params, results)
|
return types.NewSignature(types.LocalPkg, nil, nil, params, results)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -607,7 +607,7 @@ func (r *importReader) signature(recv *types.Field) *types.Type {
|
||||||
if n := len(params); n > 0 {
|
if n := len(params); n > 0 {
|
||||||
params[n-1].SetIsDDD(r.bool())
|
params[n-1].SetIsDDD(r.bool())
|
||||||
}
|
}
|
||||||
return types.NewSignature(r.currPkg, recv, params, results)
|
return types.NewSignature(r.currPkg, recv, nil, params, results)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *importReader) paramList() []*types.Field {
|
func (r *importReader) paramList() []*types.Field {
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,21 @@ func mkbuiltin(w io.Writer, name string) {
|
||||||
}
|
}
|
||||||
fmt.Fprintln(w, "}")
|
fmt.Fprintln(w, "}")
|
||||||
|
|
||||||
|
fmt.Fprintln(w, `
|
||||||
|
// Not inlining this function removes a significant chunk of init code.
|
||||||
|
//go:noinline
|
||||||
|
func newSig(params, results []*types.Field) *types.Type {
|
||||||
|
return types.NewSignature(types.NoPkg, nil, nil, params, results)
|
||||||
|
}
|
||||||
|
|
||||||
|
func params(tlist ...*types.Type) []*types.Field {
|
||||||
|
flist := make([]*types.Field, len(tlist))
|
||||||
|
for i, typ := range tlist {
|
||||||
|
flist[i] = types.NewField(src.NoXPos, nil, typ)
|
||||||
|
}
|
||||||
|
return flist
|
||||||
|
}`)
|
||||||
|
|
||||||
fmt.Fprintln(w)
|
fmt.Fprintln(w)
|
||||||
fmt.Fprintf(w, "func %sTypes() []*types.Type {\n", name)
|
fmt.Fprintf(w, "func %sTypes() []*types.Type {\n", name)
|
||||||
fmt.Fprintf(w, "var typs [%d]*types.Type\n", len(interner.typs))
|
fmt.Fprintf(w, "var typs [%d]*types.Type\n", len(interner.typs))
|
||||||
|
|
@ -169,7 +184,7 @@ func (i *typeInterner) mktype(t ast.Expr) string {
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("types.NewChan(%s, %s)", i.subtype(t.Value), dir)
|
return fmt.Sprintf("types.NewChan(%s, %s)", i.subtype(t.Value), dir)
|
||||||
case *ast.FuncType:
|
case *ast.FuncType:
|
||||||
return fmt.Sprintf("types.NewSignature(types.NoPkg, nil, %s, %s)", i.fields(t.Params, false), i.fields(t.Results, false))
|
return fmt.Sprintf("newSig(%s, %s)", i.fields(t.Params, false), i.fields(t.Results, false))
|
||||||
case *ast.InterfaceType:
|
case *ast.InterfaceType:
|
||||||
if len(t.Methods.List) != 0 {
|
if len(t.Methods.List) != 0 {
|
||||||
log.Fatal("non-empty interfaces unsupported")
|
log.Fatal("non-empty interfaces unsupported")
|
||||||
|
|
@ -192,23 +207,28 @@ func (i *typeInterner) fields(fl *ast.FieldList, keepNames bool) string {
|
||||||
if fl == nil || len(fl.List) == 0 {
|
if fl == nil || len(fl.List) == 0 {
|
||||||
return "nil"
|
return "nil"
|
||||||
}
|
}
|
||||||
|
|
||||||
var res []string
|
var res []string
|
||||||
for _, f := range fl.List {
|
for _, f := range fl.List {
|
||||||
typ := i.subtype(f.Type)
|
typ := i.subtype(f.Type)
|
||||||
if len(f.Names) == 0 {
|
if len(f.Names) == 0 {
|
||||||
res = append(res, fmt.Sprintf("types.NewField(src.NoXPos, nil, %s)", typ))
|
res = append(res, typ)
|
||||||
} else {
|
} else {
|
||||||
for _, name := range f.Names {
|
for _, name := range f.Names {
|
||||||
if keepNames {
|
if keepNames {
|
||||||
res = append(res, fmt.Sprintf("types.NewField(src.NoXPos, Lookup(%q), %s)", name.Name, typ))
|
res = append(res, fmt.Sprintf("types.NewField(src.NoXPos, Lookup(%q), %s)", name.Name, typ))
|
||||||
} else {
|
} else {
|
||||||
res = append(res, fmt.Sprintf("types.NewField(src.NoXPos, nil, %s)", typ))
|
res = append(res, typ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if keepNames {
|
||||||
return fmt.Sprintf("[]*types.Field{%s}", strings.Join(res, ", "))
|
return fmt.Sprintf("[]*types.Field{%s}", strings.Join(res, ", "))
|
||||||
}
|
}
|
||||||
|
return fmt.Sprintf("params(%s)", strings.Join(res, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
func intconst(e ast.Expr) int64 {
|
func intconst(e ast.Expr) int64 {
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ func tcFuncType(n *ir.FuncType) ir.Node {
|
||||||
recv = tcField(n.Recv, misc)
|
recv = tcField(n.Recv, misc)
|
||||||
}
|
}
|
||||||
|
|
||||||
t := types.NewSignature(types.LocalPkg, recv, tcFields(n.Params, misc), tcFields(n.Results, misc))
|
t := types.NewSignature(types.LocalPkg, recv, nil, tcFields(n.Params, misc), tcFields(n.Results, misc))
|
||||||
checkdupfields("argument", t.Recvs().FieldSlice(), t.Params().FieldSlice(), t.Results().FieldSlice())
|
checkdupfields("argument", t.Recvs().FieldSlice(), t.Params().FieldSlice(), t.Results().FieldSlice())
|
||||||
|
|
||||||
base.Pos = lno
|
base.Pos = lno
|
||||||
|
|
|
||||||
|
|
@ -329,7 +329,7 @@ func InitUniverse() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeErrorInterface() *types.Type {
|
func makeErrorInterface() *types.Type {
|
||||||
sig := types.NewSignature(types.NoPkg, fakeRecvField(), nil, []*types.Field{
|
sig := types.NewSignature(types.NoPkg, fakeRecvField(), nil, nil, []*types.Field{
|
||||||
types.NewField(src.NoXPos, nil, types.Types[types.TSTRING]),
|
types.NewField(src.NoXPos, nil, types.Types[types.TSTRING]),
|
||||||
})
|
})
|
||||||
method := types.NewField(src.NoXPos, Lookup("Error"), sig)
|
method := types.NewField(src.NoXPos, Lookup("Error"), sig)
|
||||||
|
|
|
||||||
|
|
@ -318,7 +318,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unless the 'L' flag was specified, if the type has a name, just print that name.
|
// Unless the 'L' flag was specified, if the type has a name, just print that name.
|
||||||
if verb != 'L' && t.Sym() != nil && t != Types[t.Kind()] {
|
if verb != 'L' && t.Sym() != nil && t != Types[t.Kind()] && t.Kind() != TTYPEPARAM {
|
||||||
switch mode {
|
switch mode {
|
||||||
case fmtTypeID, fmtTypeIDName:
|
case fmtTypeID, fmtTypeIDName:
|
||||||
if verb == 'S' {
|
if verb == 'S' {
|
||||||
|
|
@ -478,6 +478,9 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
|
||||||
}
|
}
|
||||||
b.WriteString("func")
|
b.WriteString("func")
|
||||||
}
|
}
|
||||||
|
if t.NumTParams() > 0 {
|
||||||
|
tconv2(b, t.TParams(), 0, mode, visited)
|
||||||
|
}
|
||||||
tconv2(b, t.Params(), 0, mode, visited)
|
tconv2(b, t.Params(), 0, mode, visited)
|
||||||
|
|
||||||
switch t.NumResults() {
|
switch t.NumResults() {
|
||||||
|
|
@ -515,7 +518,11 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
|
||||||
}
|
}
|
||||||
|
|
||||||
if funarg := t.StructType().Funarg; funarg != FunargNone {
|
if funarg := t.StructType().Funarg; funarg != FunargNone {
|
||||||
b.WriteByte('(')
|
open, close := '(', ')'
|
||||||
|
if funarg == FunargTparams {
|
||||||
|
open, close = '[', ']'
|
||||||
|
}
|
||||||
|
b.WriteByte(byte(open))
|
||||||
fieldVerb := 'v'
|
fieldVerb := 'v'
|
||||||
switch mode {
|
switch mode {
|
||||||
case fmtTypeID, fmtTypeIDName, fmtGo:
|
case fmtTypeID, fmtTypeIDName, fmtGo:
|
||||||
|
|
@ -528,7 +535,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
|
||||||
}
|
}
|
||||||
fldconv(b, f, fieldVerb, mode, visited, funarg)
|
fldconv(b, f, fieldVerb, mode, visited, funarg)
|
||||||
}
|
}
|
||||||
b.WriteByte(')')
|
b.WriteByte(byte(close))
|
||||||
} else {
|
} else {
|
||||||
b.WriteString("struct {")
|
b.WriteString("struct {")
|
||||||
for i, f := range t.Fields().Slice() {
|
for i, f := range t.Fields().Slice() {
|
||||||
|
|
@ -554,6 +561,15 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
|
||||||
case TUNSAFEPTR:
|
case TUNSAFEPTR:
|
||||||
b.WriteString("unsafe.Pointer")
|
b.WriteString("unsafe.Pointer")
|
||||||
|
|
||||||
|
case TTYPEPARAM:
|
||||||
|
if t.Sym() != nil {
|
||||||
|
sconv2(b, t.Sym(), 'v', mode)
|
||||||
|
} else {
|
||||||
|
b.WriteString("tp")
|
||||||
|
// Print out the pointer value for now to disambiguate type params
|
||||||
|
b.WriteString(fmt.Sprintf("%p", t))
|
||||||
|
}
|
||||||
|
|
||||||
case Txxx:
|
case Txxx:
|
||||||
b.WriteString("Txxx")
|
b.WriteString("Txxx")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,20 +37,21 @@ func _() {
|
||||||
_ = x[TANY-26]
|
_ = x[TANY-26]
|
||||||
_ = x[TSTRING-27]
|
_ = x[TSTRING-27]
|
||||||
_ = x[TUNSAFEPTR-28]
|
_ = x[TUNSAFEPTR-28]
|
||||||
_ = x[TIDEAL-29]
|
_ = x[TTYPEPARAM-29]
|
||||||
_ = x[TNIL-30]
|
_ = x[TIDEAL-30]
|
||||||
_ = x[TBLANK-31]
|
_ = x[TNIL-31]
|
||||||
_ = x[TFUNCARGS-32]
|
_ = x[TBLANK-32]
|
||||||
_ = x[TCHANARGS-33]
|
_ = x[TFUNCARGS-33]
|
||||||
_ = x[TSSA-34]
|
_ = x[TCHANARGS-34]
|
||||||
_ = x[TTUPLE-35]
|
_ = x[TSSA-35]
|
||||||
_ = x[TRESULTS-36]
|
_ = x[TTUPLE-36]
|
||||||
_ = x[NTYPE-37]
|
_ = x[TRESULTS-37]
|
||||||
|
_ = x[NTYPE-38]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _Kind_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRIDEALNILBLANKFUNCARGSCHANARGSSSATUPLERESULTSNTYPE"
|
const _Kind_name = "xxxINT8UINT8INT16UINT16INT32UINT32INT64UINT64INTUINTUINTPTRCOMPLEX64COMPLEX128FLOAT32FLOAT64BOOLPTRFUNCSLICEARRAYSTRUCTCHANMAPINTERFORWANYSTRINGUNSAFEPTRTYPEPARAMIDEALNILBLANKFUNCARGSCHANARGSSSATUPLERESULTSNTYPE"
|
||||||
|
|
||||||
var _Kind_index = [...]uint8{0, 3, 7, 12, 17, 23, 28, 34, 39, 45, 48, 52, 59, 68, 78, 85, 92, 96, 99, 103, 108, 113, 119, 123, 126, 131, 135, 138, 144, 153, 158, 161, 166, 174, 182, 185, 190, 197, 202}
|
var _Kind_index = [...]uint8{0, 3, 7, 12, 17, 23, 28, 34, 39, 45, 48, 52, 59, 68, 78, 85, 92, 96, 99, 103, 108, 113, 119, 123, 126, 131, 135, 138, 144, 153, 162, 167, 170, 175, 183, 191, 194, 199, 206, 211}
|
||||||
|
|
||||||
func (i Kind) String() string {
|
func (i Kind) String() string {
|
||||||
if i >= Kind(len(_Kind_index)-1) {
|
if i >= Kind(len(_Kind_index)-1) {
|
||||||
|
|
|
||||||
|
|
@ -499,6 +499,11 @@ func CalcSize(t *Type) {
|
||||||
base.Warn("bad type %v %d\n", t1, w)
|
base.Warn("bad type %v %d\n", t1, w)
|
||||||
}
|
}
|
||||||
t.Align = 1
|
t.Align = 1
|
||||||
|
|
||||||
|
case TTYPEPARAM:
|
||||||
|
// TODO(danscales) - remove when we eliminate the need
|
||||||
|
// to do CalcSize in noder2 (which shouldn't be needed in the noder)
|
||||||
|
w = int64(PtrSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
if PtrSize == 4 && w != int64(int32(w)) {
|
if PtrSize == 4 && w != int64(int32(w)) {
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,10 @@ func TestSizeof(t *testing.T) {
|
||||||
_64bit uintptr // size on 64bit platforms
|
_64bit uintptr // size on 64bit platforms
|
||||||
}{
|
}{
|
||||||
{Sym{}, 44, 72},
|
{Sym{}, 44, 72},
|
||||||
{Type{}, 56, 96},
|
{Type{}, 68, 120},
|
||||||
{Map{}, 20, 40},
|
{Map{}, 20, 40},
|
||||||
{Forward{}, 20, 32},
|
{Forward{}, 20, 32},
|
||||||
{Func{}, 24, 40},
|
{Func{}, 28, 48},
|
||||||
{Struct{}, 16, 32},
|
{Struct{}, 16, 32},
|
||||||
{Interface{}, 8, 16},
|
{Interface{}, 8, 16},
|
||||||
{Chan{}, 8, 16},
|
{Chan{}, 8, 16},
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ const (
|
||||||
TANY
|
TANY
|
||||||
TSTRING
|
TSTRING
|
||||||
TUNSAFEPTR
|
TUNSAFEPTR
|
||||||
|
TTYPEPARAM
|
||||||
|
|
||||||
// pseudo-types for literals
|
// pseudo-types for literals
|
||||||
TIDEAL // untyped numeric constants
|
TIDEAL // untyped numeric constants
|
||||||
|
|
@ -150,6 +151,7 @@ type Type struct {
|
||||||
// TARRAY: *Array
|
// TARRAY: *Array
|
||||||
// TSLICE: Slice
|
// TSLICE: Slice
|
||||||
// TSSA: string
|
// TSSA: string
|
||||||
|
// TTYPEPARAM: *Interface (though we may not need to store/use the Interface info)
|
||||||
Extra interface{}
|
Extra interface{}
|
||||||
|
|
||||||
// Width is the width of this Type in bytes.
|
// Width is the width of this Type in bytes.
|
||||||
|
|
@ -174,6 +176,11 @@ type Type struct {
|
||||||
Align uint8 // the required alignment of this type, in bytes (0 means Width and Align have not yet been computed)
|
Align uint8 // the required alignment of this type, in bytes (0 means Width and Align have not yet been computed)
|
||||||
|
|
||||||
flags bitset8
|
flags bitset8
|
||||||
|
|
||||||
|
// Type params (in order) of this named type that need to be instantiated.
|
||||||
|
// TODO(danscales): for space reasons, should probably be a pointer to a
|
||||||
|
// slice, possibly change the name of this field.
|
||||||
|
RParams []*Type
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Type) CanBeAnSSAAux() {}
|
func (*Type) CanBeAnSSAAux() {}
|
||||||
|
|
@ -184,6 +191,7 @@ const (
|
||||||
typeNoalg // suppress hash and eq algorithm generation
|
typeNoalg // suppress hash and eq algorithm generation
|
||||||
typeDeferwidth // width computation has been deferred and type is on deferredTypeStack
|
typeDeferwidth // width computation has been deferred and type is on deferredTypeStack
|
||||||
typeRecur
|
typeRecur
|
||||||
|
typeHasTParam // there is a typeparam somewhere in the type (generic function or type)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t *Type) NotInHeap() bool { return t.flags&typeNotInHeap != 0 }
|
func (t *Type) NotInHeap() bool { return t.flags&typeNotInHeap != 0 }
|
||||||
|
|
@ -191,18 +199,21 @@ func (t *Type) Broke() bool { return t.flags&typeBroke != 0 }
|
||||||
func (t *Type) Noalg() bool { return t.flags&typeNoalg != 0 }
|
func (t *Type) Noalg() bool { return t.flags&typeNoalg != 0 }
|
||||||
func (t *Type) Deferwidth() bool { return t.flags&typeDeferwidth != 0 }
|
func (t *Type) Deferwidth() bool { return t.flags&typeDeferwidth != 0 }
|
||||||
func (t *Type) Recur() bool { return t.flags&typeRecur != 0 }
|
func (t *Type) Recur() bool { return t.flags&typeRecur != 0 }
|
||||||
|
func (t *Type) HasTParam() bool { return t.flags&typeHasTParam != 0 }
|
||||||
|
|
||||||
func (t *Type) SetNotInHeap(b bool) { t.flags.set(typeNotInHeap, b) }
|
func (t *Type) SetNotInHeap(b bool) { t.flags.set(typeNotInHeap, b) }
|
||||||
func (t *Type) SetBroke(b bool) { t.flags.set(typeBroke, b) }
|
func (t *Type) SetBroke(b bool) { t.flags.set(typeBroke, b) }
|
||||||
func (t *Type) SetNoalg(b bool) { t.flags.set(typeNoalg, b) }
|
func (t *Type) SetNoalg(b bool) { t.flags.set(typeNoalg, b) }
|
||||||
func (t *Type) SetDeferwidth(b bool) { t.flags.set(typeDeferwidth, b) }
|
func (t *Type) SetDeferwidth(b bool) { t.flags.set(typeDeferwidth, b) }
|
||||||
func (t *Type) SetRecur(b bool) { t.flags.set(typeRecur, b) }
|
func (t *Type) SetRecur(b bool) { t.flags.set(typeRecur, b) }
|
||||||
|
func (t *Type) SetHasTParam(b bool) { t.flags.set(typeHasTParam, b) }
|
||||||
|
|
||||||
// Kind returns the kind of type t.
|
// Kind returns the kind of type t.
|
||||||
func (t *Type) Kind() Kind { return t.kind }
|
func (t *Type) Kind() Kind { return t.kind }
|
||||||
|
|
||||||
// Sym returns the name of type t.
|
// Sym returns the name of type t.
|
||||||
func (t *Type) Sym() *Sym { return t.sym }
|
func (t *Type) Sym() *Sym { return t.sym }
|
||||||
|
func (t *Type) SetSym(sym *Sym) { t.sym = sym }
|
||||||
|
|
||||||
// Underlying returns the underlying type of type t.
|
// Underlying returns the underlying type of type t.
|
||||||
func (t *Type) Underlying() *Type { return t.underlying }
|
func (t *Type) Underlying() *Type { return t.underlying }
|
||||||
|
|
@ -283,6 +294,7 @@ type Func struct {
|
||||||
Receiver *Type // function receiver
|
Receiver *Type // function receiver
|
||||||
Results *Type // function results
|
Results *Type // function results
|
||||||
Params *Type // function params
|
Params *Type // function params
|
||||||
|
TParams *Type // type params of receiver (if method) or function
|
||||||
|
|
||||||
pkg *Pkg
|
pkg *Pkg
|
||||||
|
|
||||||
|
|
@ -318,6 +330,7 @@ const (
|
||||||
FunargRcvr // receiver
|
FunargRcvr // receiver
|
||||||
FunargParams // input parameters
|
FunargParams // input parameters
|
||||||
FunargResults // output results
|
FunargResults // output results
|
||||||
|
FunargTparams // type params
|
||||||
)
|
)
|
||||||
|
|
||||||
// StructType returns t's extra struct-specific fields.
|
// StructType returns t's extra struct-specific fields.
|
||||||
|
|
@ -508,6 +521,8 @@ func New(et Kind) *Type {
|
||||||
t.Extra = new(Tuple)
|
t.Extra = new(Tuple)
|
||||||
case TRESULTS:
|
case TRESULTS:
|
||||||
t.Extra = new(Results)
|
t.Extra = new(Results)
|
||||||
|
case TTYPEPARAM:
|
||||||
|
t.Extra = new(Interface)
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
@ -520,6 +535,9 @@ func NewArray(elem *Type, bound int64) *Type {
|
||||||
t := New(TARRAY)
|
t := New(TARRAY)
|
||||||
t.Extra = &Array{Elem: elem, Bound: bound}
|
t.Extra = &Array{Elem: elem, Bound: bound}
|
||||||
t.SetNotInHeap(elem.NotInHeap())
|
t.SetNotInHeap(elem.NotInHeap())
|
||||||
|
if elem.HasTParam() {
|
||||||
|
t.SetHasTParam(true)
|
||||||
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -535,6 +553,9 @@ func NewSlice(elem *Type) *Type {
|
||||||
t := New(TSLICE)
|
t := New(TSLICE)
|
||||||
t.Extra = Slice{Elem: elem}
|
t.Extra = Slice{Elem: elem}
|
||||||
elem.cache.slice = t
|
elem.cache.slice = t
|
||||||
|
if elem.HasTParam() {
|
||||||
|
t.SetHasTParam(true)
|
||||||
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -544,6 +565,9 @@ func NewChan(elem *Type, dir ChanDir) *Type {
|
||||||
ct := t.ChanType()
|
ct := t.ChanType()
|
||||||
ct.Elem = elem
|
ct.Elem = elem
|
||||||
ct.Dir = dir
|
ct.Dir = dir
|
||||||
|
if elem.HasTParam() {
|
||||||
|
t.SetHasTParam(true)
|
||||||
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -551,6 +575,9 @@ func NewTuple(t1, t2 *Type) *Type {
|
||||||
t := New(TTUPLE)
|
t := New(TTUPLE)
|
||||||
t.Extra.(*Tuple).first = t1
|
t.Extra.(*Tuple).first = t1
|
||||||
t.Extra.(*Tuple).second = t2
|
t.Extra.(*Tuple).second = t2
|
||||||
|
if t1.HasTParam() || t2.HasTParam() {
|
||||||
|
t.SetHasTParam(true)
|
||||||
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -572,6 +599,9 @@ func NewMap(k, v *Type) *Type {
|
||||||
mt := t.MapType()
|
mt := t.MapType()
|
||||||
mt.Key = k
|
mt.Key = k
|
||||||
mt.Elem = v
|
mt.Elem = v
|
||||||
|
if k.HasTParam() || v.HasTParam() {
|
||||||
|
t.SetHasTParam(true)
|
||||||
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -590,6 +620,12 @@ func NewPtr(elem *Type) *Type {
|
||||||
if t.Elem() != elem {
|
if t.Elem() != elem {
|
||||||
base.Fatalf("NewPtr: elem mismatch")
|
base.Fatalf("NewPtr: elem mismatch")
|
||||||
}
|
}
|
||||||
|
if elem.HasTParam() {
|
||||||
|
// Extra check when reusing the cache, since the elem
|
||||||
|
// might have still been undetermined (i.e. a TFORW type)
|
||||||
|
// when this entry was cached.
|
||||||
|
t.SetHasTParam(true)
|
||||||
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -600,6 +636,9 @@ func NewPtr(elem *Type) *Type {
|
||||||
if NewPtrCacheEnabled {
|
if NewPtrCacheEnabled {
|
||||||
elem.cache.ptr = t
|
elem.cache.ptr = t
|
||||||
}
|
}
|
||||||
|
if elem.HasTParam() {
|
||||||
|
t.SetHasTParam(true)
|
||||||
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -765,10 +804,12 @@ func (t *Type) wantEtype(et Kind) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Type) Recvs() *Type { return t.FuncType().Receiver }
|
func (t *Type) Recvs() *Type { return t.FuncType().Receiver }
|
||||||
|
func (t *Type) TParams() *Type { return t.FuncType().TParams }
|
||||||
func (t *Type) Params() *Type { return t.FuncType().Params }
|
func (t *Type) Params() *Type { return t.FuncType().Params }
|
||||||
func (t *Type) Results() *Type { return t.FuncType().Results }
|
func (t *Type) Results() *Type { return t.FuncType().Results }
|
||||||
|
|
||||||
func (t *Type) NumRecvs() int { return t.FuncType().Receiver.NumFields() }
|
func (t *Type) NumRecvs() int { return t.FuncType().Receiver.NumFields() }
|
||||||
|
func (t *Type) NumTParams() int { return t.FuncType().TParams.NumFields() }
|
||||||
func (t *Type) NumParams() int { return t.FuncType().Params.NumFields() }
|
func (t *Type) NumParams() int { return t.FuncType().Params.NumFields() }
|
||||||
func (t *Type) NumResults() int { return t.FuncType().Results.NumFields() }
|
func (t *Type) NumResults() int { return t.FuncType().Results.NumFields() }
|
||||||
|
|
||||||
|
|
@ -1602,6 +1643,9 @@ func (t *Type) SetUnderlying(underlying *Type) {
|
||||||
if underlying.Broke() {
|
if underlying.Broke() {
|
||||||
t.SetBroke(true)
|
t.SetBroke(true)
|
||||||
}
|
}
|
||||||
|
if underlying.HasTParam() {
|
||||||
|
t.SetHasTParam(true)
|
||||||
|
}
|
||||||
|
|
||||||
// spec: "The declared type does not inherit any methods bound
|
// spec: "The declared type does not inherit any methods bound
|
||||||
// to the existing type, but the method set of an interface
|
// to the existing type, but the method set of an interface
|
||||||
|
|
@ -1624,6 +1668,15 @@ func (t *Type) SetUnderlying(underlying *Type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fieldsHasTParam(fields []*Field) bool {
|
||||||
|
for _, f := range fields {
|
||||||
|
if f.Type != nil && f.Type.HasTParam() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// NewBasic returns a new basic type of the given kind.
|
// NewBasic returns a new basic type of the given kind.
|
||||||
func NewBasic(kind Kind, obj Object) *Type {
|
func NewBasic(kind Kind, obj Object) *Type {
|
||||||
t := New(kind)
|
t := New(kind)
|
||||||
|
|
@ -1644,9 +1697,20 @@ func NewInterface(pkg *Pkg, methods []*Field) *Type {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewTypeParam returns a new type param with the given constraint (which may
|
||||||
|
// not really be needed except for the type checker).
|
||||||
|
func NewTypeParam(pkg *Pkg, constraint *Type) *Type {
|
||||||
|
t := New(TTYPEPARAM)
|
||||||
|
constraint.wantEtype(TINTER)
|
||||||
|
t.methods = constraint.methods
|
||||||
|
t.Extra.(*Interface).pkg = pkg
|
||||||
|
t.SetHasTParam(true)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
// NewSignature returns a new function type for the given receiver,
|
// NewSignature returns a new function type for the given receiver,
|
||||||
// parameters, and results, any of which may be nil.
|
// parametes, results, and type parameters, any of which may be nil.
|
||||||
func NewSignature(pkg *Pkg, recv *Field, params, results []*Field) *Type {
|
func NewSignature(pkg *Pkg, recv *Field, tparams, params, results []*Field) *Type {
|
||||||
var recvs []*Field
|
var recvs []*Field
|
||||||
if recv != nil {
|
if recv != nil {
|
||||||
recvs = []*Field{recv}
|
recvs = []*Field{recv}
|
||||||
|
|
@ -1665,9 +1729,14 @@ func NewSignature(pkg *Pkg, recv *Field, params, results []*Field) *Type {
|
||||||
}
|
}
|
||||||
|
|
||||||
ft.Receiver = funargs(recvs, FunargRcvr)
|
ft.Receiver = funargs(recvs, FunargRcvr)
|
||||||
|
ft.TParams = funargs(tparams, FunargTparams)
|
||||||
ft.Params = funargs(params, FunargParams)
|
ft.Params = funargs(params, FunargParams)
|
||||||
ft.Results = funargs(results, FunargResults)
|
ft.Results = funargs(results, FunargResults)
|
||||||
ft.pkg = pkg
|
ft.pkg = pkg
|
||||||
|
if len(tparams) > 0 || fieldsHasTParam(recvs) || fieldsHasTParam(params) ||
|
||||||
|
fieldsHasTParam(results) {
|
||||||
|
t.SetHasTParam(true)
|
||||||
|
}
|
||||||
|
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
@ -1680,6 +1749,9 @@ func NewStruct(pkg *Pkg, fields []*Field) *Type {
|
||||||
t.SetBroke(true)
|
t.SetBroke(true)
|
||||||
}
|
}
|
||||||
t.Extra.(*Struct).pkg = pkg
|
t.Extra.(*Struct).pkg = pkg
|
||||||
|
if fieldsHasTParam(fields) {
|
||||||
|
t.SetHasTParam(true)
|
||||||
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
443
src/cmd/compile/internal/types2/api.go
Normal file
443
src/cmd/compile/internal/types2/api.go
Normal file
|
|
@ -0,0 +1,443 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package types declares the data types and implements
|
||||||
|
// the algorithms for type-checking of Go packages. Use
|
||||||
|
// Config.Check to invoke the type checker for a package.
|
||||||
|
// Alternatively, create a new type checker with NewChecker
|
||||||
|
// and invoke it incrementally by calling Checker.Files.
|
||||||
|
//
|
||||||
|
// Type-checking consists of several interdependent phases:
|
||||||
|
//
|
||||||
|
// Name resolution maps each identifier (syntax.Name) in the program to the
|
||||||
|
// language object (Object) it denotes.
|
||||||
|
// Use Info.{Defs,Uses,Implicits} for the results of name resolution.
|
||||||
|
//
|
||||||
|
// Constant folding computes the exact constant value (constant.Value)
|
||||||
|
// for every expression (syntax.Expr) that is a compile-time constant.
|
||||||
|
// Use Info.Types[expr].Value for the results of constant folding.
|
||||||
|
//
|
||||||
|
// Type inference computes the type (Type) of every expression (syntax.Expr)
|
||||||
|
// and checks for compliance with the language specification.
|
||||||
|
// Use Info.Types[expr].Type for the results of type inference.
|
||||||
|
//
|
||||||
|
// For a tutorial, see https://golang.org/s/types-tutorial.
|
||||||
|
//
|
||||||
|
package types2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"fmt"
|
||||||
|
"go/constant"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An Error describes a type-checking error; it implements the error interface.
|
||||||
|
// A "soft" error is an error that still permits a valid interpretation of a
|
||||||
|
// package (such as "unused variable"); "hard" errors may lead to unpredictable
|
||||||
|
// behavior if ignored.
|
||||||
|
type Error struct {
|
||||||
|
Pos syntax.Pos // error position
|
||||||
|
Msg string // default error message, user-friendly
|
||||||
|
Full string // full error message, for debugging (may contain internal details)
|
||||||
|
Soft bool // if set, error is "soft"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns an error string formatted as follows:
|
||||||
|
// filename:line:column: message
|
||||||
|
func (err Error) Error() string {
|
||||||
|
return fmt.Sprintf("%s: %s", err.Pos, err.Msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FullError returns an error string like Error, buy it may contain
|
||||||
|
// type-checker internal details such as subscript indices for type
|
||||||
|
// parameters and more. Useful for debugging.
|
||||||
|
func (err Error) FullError() string {
|
||||||
|
return fmt.Sprintf("%s: %s", err.Pos, err.Full)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Importer resolves import paths to Packages.
|
||||||
|
//
|
||||||
|
// CAUTION: This interface does not support the import of locally
|
||||||
|
// vendored packages. See https://golang.org/s/go15vendor.
|
||||||
|
// If possible, external implementations should implement ImporterFrom.
|
||||||
|
type Importer interface {
|
||||||
|
// Import returns the imported package for the given import path.
|
||||||
|
// The semantics is like for ImporterFrom.ImportFrom except that
|
||||||
|
// dir and mode are ignored (since they are not present).
|
||||||
|
Import(path string) (*Package, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportMode is reserved for future use.
|
||||||
|
type ImportMode int
|
||||||
|
|
||||||
|
// An ImporterFrom resolves import paths to packages; it
|
||||||
|
// supports vendoring per https://golang.org/s/go15vendor.
|
||||||
|
// Use go/importer to obtain an ImporterFrom implementation.
|
||||||
|
type ImporterFrom interface {
|
||||||
|
// Importer is present for backward-compatibility. Calling
|
||||||
|
// Import(path) is the same as calling ImportFrom(path, "", 0);
|
||||||
|
// i.e., locally vendored packages may not be found.
|
||||||
|
// The types package does not call Import if an ImporterFrom
|
||||||
|
// is present.
|
||||||
|
Importer
|
||||||
|
|
||||||
|
// ImportFrom returns the imported package for the given import
|
||||||
|
// path when imported by a package file located in dir.
|
||||||
|
// If the import failed, besides returning an error, ImportFrom
|
||||||
|
// is encouraged to cache and return a package anyway, if one
|
||||||
|
// was created. This will reduce package inconsistencies and
|
||||||
|
// follow-on type checker errors due to the missing package.
|
||||||
|
// The mode value must be 0; it is reserved for future use.
|
||||||
|
// Two calls to ImportFrom with the same path and dir must
|
||||||
|
// return the same package.
|
||||||
|
ImportFrom(path, dir string, mode ImportMode) (*Package, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Config specifies the configuration for type checking.
|
||||||
|
// The zero value for Config is a ready-to-use default configuration.
|
||||||
|
type Config struct {
|
||||||
|
// GoVersion describes the accepted Go language version. The string
|
||||||
|
// must follow the format "go%d.%d" (e.g. "go1.12") or ist must be
|
||||||
|
// empty; an empty string indicates the latest language version.
|
||||||
|
// If the format is invalid, invoking the type checker will cause a
|
||||||
|
// panic.
|
||||||
|
GoVersion string
|
||||||
|
|
||||||
|
// If IgnoreFuncBodies is set, function bodies are not
|
||||||
|
// type-checked.
|
||||||
|
IgnoreFuncBodies bool
|
||||||
|
|
||||||
|
// If AcceptMethodTypeParams is set, methods may have type parameters.
|
||||||
|
AcceptMethodTypeParams bool
|
||||||
|
|
||||||
|
// If InferFromConstraints is set, constraint type inference is used
|
||||||
|
// if some function type arguments are missing.
|
||||||
|
InferFromConstraints bool
|
||||||
|
|
||||||
|
// If FakeImportC is set, `import "C"` (for packages requiring Cgo)
|
||||||
|
// declares an empty "C" package and errors are omitted for qualified
|
||||||
|
// identifiers referring to package C (which won't find an object).
|
||||||
|
// This feature is intended for the standard library cmd/api tool.
|
||||||
|
//
|
||||||
|
// Caution: Effects may be unpredictable due to follow-on errors.
|
||||||
|
// Do not use casually!
|
||||||
|
FakeImportC bool
|
||||||
|
|
||||||
|
// If IgnoreLabels is set, correct label use is not checked.
|
||||||
|
// TODO(gri) Consolidate label checking and remove this flag.
|
||||||
|
IgnoreLabels bool
|
||||||
|
|
||||||
|
// If CompilerErrorMessages is set, errors are reported using
|
||||||
|
// cmd/compile error strings to match $GOROOT/test errors.
|
||||||
|
// TODO(gri) Consolidate error messages and remove this flag.
|
||||||
|
CompilerErrorMessages bool
|
||||||
|
|
||||||
|
// If go115UsesCgo is set, the type checker expects the
|
||||||
|
// _cgo_gotypes.go file generated by running cmd/cgo to be
|
||||||
|
// provided as a package source file. Qualified identifiers
|
||||||
|
// referring to package C will be resolved to cgo-provided
|
||||||
|
// declarations within _cgo_gotypes.go.
|
||||||
|
//
|
||||||
|
// It is an error to set both FakeImportC and go115UsesCgo.
|
||||||
|
go115UsesCgo bool
|
||||||
|
|
||||||
|
// If Trace is set, a debug trace is printed to stdout.
|
||||||
|
Trace bool
|
||||||
|
|
||||||
|
// If Error != nil, it is called with each error found
|
||||||
|
// during type checking; err has dynamic type Error.
|
||||||
|
// Secondary errors (for instance, to enumerate all types
|
||||||
|
// involved in an invalid recursive type declaration) have
|
||||||
|
// error strings that start with a '\t' character.
|
||||||
|
// If Error == nil, type-checking stops with the first
|
||||||
|
// error found.
|
||||||
|
Error func(err error)
|
||||||
|
|
||||||
|
// An importer is used to import packages referred to from
|
||||||
|
// import declarations.
|
||||||
|
// If the installed importer implements ImporterFrom, the type
|
||||||
|
// checker calls ImportFrom instead of Import.
|
||||||
|
// The type checker reports an error if an importer is needed
|
||||||
|
// but none was installed.
|
||||||
|
Importer Importer
|
||||||
|
|
||||||
|
// If Sizes != nil, it provides the sizing functions for package unsafe.
|
||||||
|
// Otherwise SizesFor("gc", "amd64") is used instead.
|
||||||
|
Sizes Sizes
|
||||||
|
|
||||||
|
// If DisableUnusedImportCheck is set, packages are not checked
|
||||||
|
// for unused imports.
|
||||||
|
DisableUnusedImportCheck bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func srcimporter_setUsesCgo(conf *Config) {
|
||||||
|
conf.go115UsesCgo = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info holds result type information for a type-checked package.
|
||||||
|
// Only the information for which a map is provided is collected.
|
||||||
|
// If the package has type errors, the collected information may
|
||||||
|
// be incomplete.
|
||||||
|
type Info struct {
|
||||||
|
// Types maps expressions to their types, and for constant
|
||||||
|
// expressions, also their values. Invalid expressions are
|
||||||
|
// omitted.
|
||||||
|
//
|
||||||
|
// For (possibly parenthesized) identifiers denoting built-in
|
||||||
|
// functions, the recorded signatures are call-site specific:
|
||||||
|
// if the call result is not a constant, the recorded type is
|
||||||
|
// an argument-specific signature. Otherwise, the recorded type
|
||||||
|
// is invalid.
|
||||||
|
//
|
||||||
|
// The Types map does not record the type of every identifier,
|
||||||
|
// only those that appear where an arbitrary expression is
|
||||||
|
// permitted. For instance, the identifier f in a selector
|
||||||
|
// expression x.f is found only in the Selections map, the
|
||||||
|
// identifier z in a variable declaration 'var z int' is found
|
||||||
|
// only in the Defs map, and identifiers denoting packages in
|
||||||
|
// qualified identifiers are collected in the Uses map.
|
||||||
|
Types map[syntax.Expr]TypeAndValue
|
||||||
|
|
||||||
|
// Inferred maps calls of parameterized functions that use
|
||||||
|
// type inference to the inferred type arguments and signature
|
||||||
|
// of the function called. The recorded "call" expression may be
|
||||||
|
// an *ast.CallExpr (as in f(x)), or an *ast.IndexExpr (s in f[T]).
|
||||||
|
Inferred map[syntax.Expr]Inferred
|
||||||
|
|
||||||
|
// Defs maps identifiers to the objects they define (including
|
||||||
|
// package names, dots "." of dot-imports, and blank "_" identifiers).
|
||||||
|
// For identifiers that do not denote objects (e.g., the package name
|
||||||
|
// in package clauses, or symbolic variables t in t := x.(type) of
|
||||||
|
// type switch headers), the corresponding objects are nil.
|
||||||
|
//
|
||||||
|
// For an embedded field, Defs returns the field *Var it defines.
|
||||||
|
//
|
||||||
|
// Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
|
||||||
|
Defs map[*syntax.Name]Object
|
||||||
|
|
||||||
|
// Uses maps identifiers to the objects they denote.
|
||||||
|
//
|
||||||
|
// For an embedded field, Uses returns the *TypeName it denotes.
|
||||||
|
//
|
||||||
|
// Invariant: Uses[id].Pos() != id.Pos()
|
||||||
|
Uses map[*syntax.Name]Object
|
||||||
|
|
||||||
|
// Implicits maps nodes to their implicitly declared objects, if any.
|
||||||
|
// The following node and object types may appear:
|
||||||
|
//
|
||||||
|
// node declared object
|
||||||
|
//
|
||||||
|
// *syntax.ImportDecl *PkgName for imports without renames
|
||||||
|
// *syntax.CaseClause type-specific *Var for each type switch case clause (incl. default)
|
||||||
|
// *syntax.Field anonymous parameter *Var (incl. unnamed results)
|
||||||
|
//
|
||||||
|
Implicits map[syntax.Node]Object
|
||||||
|
|
||||||
|
// Selections maps selector expressions (excluding qualified identifiers)
|
||||||
|
// to their corresponding selections.
|
||||||
|
Selections map[*syntax.SelectorExpr]*Selection
|
||||||
|
|
||||||
|
// Scopes maps syntax.Nodes to the scopes they define. Package scopes are not
|
||||||
|
// associated with a specific node but with all files belonging to a package.
|
||||||
|
// Thus, the package scope can be found in the type-checked Package object.
|
||||||
|
// Scopes nest, with the Universe scope being the outermost scope, enclosing
|
||||||
|
// the package scope, which contains (one or more) files scopes, which enclose
|
||||||
|
// function scopes which in turn enclose statement and function literal scopes.
|
||||||
|
// Note that even though package-level functions are declared in the package
|
||||||
|
// scope, the function scopes are embedded in the file scope of the file
|
||||||
|
// containing the function declaration.
|
||||||
|
//
|
||||||
|
// The following node types may appear in Scopes:
|
||||||
|
//
|
||||||
|
// *syntax.File
|
||||||
|
// *syntax.FuncType
|
||||||
|
// *syntax.BlockStmt
|
||||||
|
// *syntax.IfStmt
|
||||||
|
// *syntax.SwitchStmt
|
||||||
|
// *syntax.CaseClause
|
||||||
|
// *syntax.CommClause
|
||||||
|
// *syntax.ForStmt
|
||||||
|
//
|
||||||
|
Scopes map[syntax.Node]*Scope
|
||||||
|
|
||||||
|
// InitOrder is the list of package-level initializers in the order in which
|
||||||
|
// they must be executed. Initializers referring to variables related by an
|
||||||
|
// initialization dependency appear in topological order, the others appear
|
||||||
|
// in source order. Variables without an initialization expression do not
|
||||||
|
// appear in this list.
|
||||||
|
InitOrder []*Initializer
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeOf returns the type of expression e, or nil if not found.
|
||||||
|
// Precondition: the Types, Uses and Defs maps are populated.
|
||||||
|
//
|
||||||
|
func (info *Info) TypeOf(e syntax.Expr) Type {
|
||||||
|
if t, ok := info.Types[e]; ok {
|
||||||
|
return t.Type
|
||||||
|
}
|
||||||
|
if id, _ := e.(*syntax.Name); id != nil {
|
||||||
|
if obj := info.ObjectOf(id); obj != nil {
|
||||||
|
return obj.Type()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObjectOf returns the object denoted by the specified id,
|
||||||
|
// or nil if not found.
|
||||||
|
//
|
||||||
|
// If id is an embedded struct field, ObjectOf returns the field (*Var)
|
||||||
|
// it defines, not the type (*TypeName) it uses.
|
||||||
|
//
|
||||||
|
// Precondition: the Uses and Defs maps are populated.
|
||||||
|
//
|
||||||
|
func (info *Info) ObjectOf(id *syntax.Name) Object {
|
||||||
|
if obj := info.Defs[id]; obj != nil {
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
return info.Uses[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeAndValue reports the type and value (for constants)
|
||||||
|
// of the corresponding expression.
|
||||||
|
type TypeAndValue struct {
|
||||||
|
mode operandMode
|
||||||
|
Type Type
|
||||||
|
Value constant.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsVoid reports whether the corresponding expression
|
||||||
|
// is a function call without results.
|
||||||
|
func (tv TypeAndValue) IsVoid() bool {
|
||||||
|
return tv.mode == novalue
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsType reports whether the corresponding expression specifies a type.
|
||||||
|
func (tv TypeAndValue) IsType() bool {
|
||||||
|
return tv.mode == typexpr
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBuiltin reports whether the corresponding expression denotes
|
||||||
|
// a (possibly parenthesized) built-in function.
|
||||||
|
func (tv TypeAndValue) IsBuiltin() bool {
|
||||||
|
return tv.mode == builtin
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValue reports whether the corresponding expression is a value.
|
||||||
|
// Builtins are not considered values. Constant values have a non-
|
||||||
|
// nil Value.
|
||||||
|
func (tv TypeAndValue) IsValue() bool {
|
||||||
|
switch tv.mode {
|
||||||
|
case constant_, variable, mapindex, value, nilvalue, commaok, commaerr:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNil reports whether the corresponding expression denotes the
|
||||||
|
// predeclared value nil. Depending on context, it may have been
|
||||||
|
// given a type different from UntypedNil.
|
||||||
|
func (tv TypeAndValue) IsNil() bool {
|
||||||
|
return tv.mode == nilvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addressable reports whether the corresponding expression
|
||||||
|
// is addressable (https://golang.org/ref/spec#Address_operators).
|
||||||
|
func (tv TypeAndValue) Addressable() bool {
|
||||||
|
return tv.mode == variable
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assignable reports whether the corresponding expression
|
||||||
|
// is assignable to (provided a value of the right type).
|
||||||
|
func (tv TypeAndValue) Assignable() bool {
|
||||||
|
return tv.mode == variable || tv.mode == mapindex
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasOk reports whether the corresponding expression may be
|
||||||
|
// used on the rhs of a comma-ok assignment.
|
||||||
|
func (tv TypeAndValue) HasOk() bool {
|
||||||
|
return tv.mode == commaok || tv.mode == mapindex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inferred reports the inferred type arguments and signature
|
||||||
|
// for a parameterized function call that uses type inference.
|
||||||
|
type Inferred struct {
|
||||||
|
Targs []Type
|
||||||
|
Sig *Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Initializer describes a package-level variable, or a list of variables in case
|
||||||
|
// of a multi-valued initialization expression, and the corresponding initialization
|
||||||
|
// expression.
|
||||||
|
type Initializer struct {
|
||||||
|
Lhs []*Var // var Lhs = Rhs
|
||||||
|
Rhs syntax.Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (init *Initializer) String() string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i, lhs := range init.Lhs {
|
||||||
|
if i > 0 {
|
||||||
|
buf.WriteString(", ")
|
||||||
|
}
|
||||||
|
buf.WriteString(lhs.Name())
|
||||||
|
}
|
||||||
|
buf.WriteString(" = ")
|
||||||
|
syntax.Fprint(&buf, init.Rhs, syntax.ShortForm)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check type-checks a package and returns the resulting package object and
|
||||||
|
// the first error if any. Additionally, if info != nil, Check populates each
|
||||||
|
// of the non-nil maps in the Info struct.
|
||||||
|
//
|
||||||
|
// The package is marked as complete if no errors occurred, otherwise it is
|
||||||
|
// incomplete. See Config.Error for controlling behavior in the presence of
|
||||||
|
// errors.
|
||||||
|
//
|
||||||
|
// The package is specified by a list of *syntax.Files and corresponding
|
||||||
|
// file set, and the package path the package is identified with.
|
||||||
|
// The clean path must not be empty or dot (".").
|
||||||
|
func (conf *Config) Check(path string, files []*syntax.File, info *Info) (*Package, error) {
|
||||||
|
pkg := NewPackage(path, "")
|
||||||
|
return pkg, NewChecker(conf, pkg, info).Files(files)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertableTo reports whether a value of type V can be asserted to have type T.
|
||||||
|
func AssertableTo(V *Interface, T Type) bool {
|
||||||
|
m, _ := (*Checker)(nil).assertableTo(V, T, false)
|
||||||
|
return m == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssignableTo reports whether a value of type V is assignable to a variable of type T.
|
||||||
|
func AssignableTo(V, T Type) bool {
|
||||||
|
x := operand{mode: value, typ: V}
|
||||||
|
return x.assignableTo(nil, T, nil) // check not needed for non-constant x
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
|
||||||
|
func ConvertibleTo(V, T Type) bool {
|
||||||
|
x := operand{mode: value, typ: V}
|
||||||
|
return x.convertibleTo(nil, T) // check not needed for non-constant x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements reports whether type V implements interface T.
|
||||||
|
func Implements(V Type, T *Interface) bool {
|
||||||
|
f, _ := MissingMethod(V, T, true)
|
||||||
|
return f == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identical reports whether x and y are identical types.
|
||||||
|
// Receivers of Signature types are ignored.
|
||||||
|
func Identical(x, y Type) bool {
|
||||||
|
return (*Checker)(nil).identical(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IdenticalIgnoreTags reports whether x and y are identical types if tags are ignored.
|
||||||
|
// Receivers of Signature types are ignored.
|
||||||
|
func IdenticalIgnoreTags(x, y Type) bool {
|
||||||
|
return (*Checker)(nil).identicalIgnoreTags(x, y)
|
||||||
|
}
|
||||||
1791
src/cmd/compile/internal/types2/api_test.go
Normal file
1791
src/cmd/compile/internal/types2/api_test.go
Normal file
File diff suppressed because it is too large
Load diff
368
src/cmd/compile/internal/types2/assignments.go
Normal file
368
src/cmd/compile/internal/types2/assignments.go
Normal file
|
|
@ -0,0 +1,368 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements initialization and assignment checks.
|
||||||
|
|
||||||
|
package types2
|
||||||
|
|
||||||
|
import "cmd/compile/internal/syntax"
|
||||||
|
|
||||||
|
// assignment reports whether x can be assigned to a variable of type T,
|
||||||
|
// if necessary by attempting to convert untyped values to the appropriate
|
||||||
|
// type. context describes the context in which the assignment takes place.
|
||||||
|
// Use T == nil to indicate assignment to an untyped blank identifier.
|
||||||
|
// x.mode is set to invalid if the assignment failed.
|
||||||
|
func (check *Checker) assignment(x *operand, T Type, context string) {
|
||||||
|
check.singleValue(x)
|
||||||
|
|
||||||
|
switch x.mode {
|
||||||
|
case invalid:
|
||||||
|
return // error reported before
|
||||||
|
case constant_, variable, mapindex, value, nilvalue, commaok, commaerr:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
// we may get here because of other problems (issue #39634, crash 12)
|
||||||
|
check.errorf(x, "cannot assign %s to %s in %s", x, T, context)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if isUntyped(x.typ) {
|
||||||
|
target := T
|
||||||
|
// spec: "If an untyped constant is assigned to a variable of interface
|
||||||
|
// type or the blank identifier, the constant is first converted to type
|
||||||
|
// bool, rune, int, float64, complex128 or string respectively, depending
|
||||||
|
// on whether the value is a boolean, rune, integer, floating-point, complex,
|
||||||
|
// or string constant."
|
||||||
|
if x.isNil() {
|
||||||
|
if T == nil {
|
||||||
|
check.errorf(x, "use of untyped nil in %s", context)
|
||||||
|
x.mode = invalid
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if T == nil || IsInterface(T) {
|
||||||
|
target = Default(x.typ)
|
||||||
|
}
|
||||||
|
check.convertUntyped(x, target)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// x.typ is typed
|
||||||
|
|
||||||
|
// A generic (non-instantiated) function value cannot be assigned to a variable.
|
||||||
|
if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 {
|
||||||
|
check.errorf(x, "cannot use generic function %s without instantiation in %s", x, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec: "If a left-hand side is the blank identifier, any typed or
|
||||||
|
// non-constant value except for the predeclared identifier nil may
|
||||||
|
// be assigned to it."
|
||||||
|
if T == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if reason := ""; !x.assignableTo(check, T, &reason) {
|
||||||
|
if check.conf.CompilerErrorMessages {
|
||||||
|
check.errorf(x, "incompatible type: cannot use %s as %s value", x, T)
|
||||||
|
} else {
|
||||||
|
if reason != "" {
|
||||||
|
check.errorf(x, "cannot use %s as %s value in %s: %s", x, T, context, reason)
|
||||||
|
} else {
|
||||||
|
check.errorf(x, "cannot use %s as %s value in %s", x, T, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x.mode = invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) initConst(lhs *Const, x *operand) {
|
||||||
|
if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
|
||||||
|
if lhs.typ == nil {
|
||||||
|
lhs.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// rhs must be a constant
|
||||||
|
if x.mode != constant_ {
|
||||||
|
check.errorf(x, "%s is not constant", x)
|
||||||
|
if lhs.typ == nil {
|
||||||
|
lhs.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert(isConstType(x.typ))
|
||||||
|
|
||||||
|
// If the lhs doesn't have a type yet, use the type of x.
|
||||||
|
if lhs.typ == nil {
|
||||||
|
lhs.typ = x.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
check.assignment(x, lhs.typ, "constant declaration")
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lhs.val = x.val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
|
||||||
|
if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
|
||||||
|
if lhs.typ == nil {
|
||||||
|
lhs.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
lhs.used = true // avoid follow-on "declared but not used" errors
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the lhs doesn't have a type yet, use the type of x.
|
||||||
|
if lhs.typ == nil {
|
||||||
|
typ := x.typ
|
||||||
|
if isUntyped(typ) {
|
||||||
|
// convert untyped types to default types
|
||||||
|
if typ == Typ[UntypedNil] {
|
||||||
|
check.errorf(x, "use of untyped nil in %s", context)
|
||||||
|
lhs.typ = Typ[Invalid]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
typ = Default(typ)
|
||||||
|
}
|
||||||
|
lhs.typ = typ
|
||||||
|
}
|
||||||
|
|
||||||
|
check.assignment(x, lhs.typ, context)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type {
|
||||||
|
if x.mode == invalid || x.typ == Typ[Invalid] {
|
||||||
|
check.useLHS(lhs)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if the lhs is a (possibly parenthesized) identifier.
|
||||||
|
ident, _ := unparen(lhs).(*syntax.Name)
|
||||||
|
|
||||||
|
// Don't evaluate lhs if it is the blank identifier.
|
||||||
|
if ident != nil && ident.Value == "_" {
|
||||||
|
check.recordDef(ident, nil)
|
||||||
|
check.assignment(x, nil, "assignment to _ identifier")
|
||||||
|
if x.mode == invalid {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return x.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the lhs is an identifier denoting a variable v, this assignment
|
||||||
|
// is not a 'use' of v. Remember current value of v.used and restore
|
||||||
|
// after evaluating the lhs via check.expr.
|
||||||
|
var v *Var
|
||||||
|
var v_used bool
|
||||||
|
if ident != nil {
|
||||||
|
if obj := check.lookup(ident.Value); obj != nil {
|
||||||
|
// It's ok to mark non-local variables, but ignore variables
|
||||||
|
// from other packages to avoid potential race conditions with
|
||||||
|
// dot-imported variables.
|
||||||
|
if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg {
|
||||||
|
v = w
|
||||||
|
v_used = v.used
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var z operand
|
||||||
|
check.expr(&z, lhs)
|
||||||
|
if v != nil {
|
||||||
|
v.used = v_used // restore v.used
|
||||||
|
}
|
||||||
|
|
||||||
|
if z.mode == invalid || z.typ == Typ[Invalid] {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec: "Each left-hand side operand must be addressable, a map index
|
||||||
|
// expression, or the blank identifier. Operands may be parenthesized."
|
||||||
|
switch z.mode {
|
||||||
|
case invalid:
|
||||||
|
return nil
|
||||||
|
case variable, mapindex:
|
||||||
|
// ok
|
||||||
|
case nilvalue:
|
||||||
|
check.errorf(&z, "cannot assign to nil") // default would print "untyped nil"
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
if sel, ok := z.expr.(*syntax.SelectorExpr); ok {
|
||||||
|
var op operand
|
||||||
|
check.expr(&op, sel.X)
|
||||||
|
if op.mode == mapindex {
|
||||||
|
check.errorf(&z, "cannot assign to struct field %s in map", syntax.String(z.expr))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check.errorf(&z, "cannot assign to %s", &z)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
check.assignment(x, z.typ, "assignment")
|
||||||
|
if x.mode == invalid {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// If returnPos is valid, initVars is called to type-check the assignment of
|
||||||
|
// return expressions, and returnPos is the position of the return statement.
|
||||||
|
func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syntax.Pos) {
|
||||||
|
rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2 && !returnPos.IsKnown())
|
||||||
|
|
||||||
|
if len(lhs) != len(rhs) {
|
||||||
|
// invalidate lhs
|
||||||
|
for _, obj := range lhs {
|
||||||
|
if obj.typ == nil {
|
||||||
|
obj.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// don't report an error if we already reported one
|
||||||
|
for _, x := range rhs {
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if returnPos.IsKnown() {
|
||||||
|
check.errorf(returnPos, "wrong number of return values (want %d, got %d)", len(lhs), len(rhs))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check.errorf(rhs[0], "cannot initialize %d variables with %d values", len(lhs), len(rhs))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
context := "assignment"
|
||||||
|
if returnPos.IsKnown() {
|
||||||
|
context = "return statement"
|
||||||
|
}
|
||||||
|
|
||||||
|
if commaOk {
|
||||||
|
var a [2]Type
|
||||||
|
for i := range a {
|
||||||
|
a[i] = check.initVar(lhs[i], rhs[i], context)
|
||||||
|
}
|
||||||
|
check.recordCommaOkTypes(orig_rhs[0], a)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, lhs := range lhs {
|
||||||
|
check.initVar(lhs, rhs[i], context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) assignVars(lhs, orig_rhs []syntax.Expr) {
|
||||||
|
rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2)
|
||||||
|
|
||||||
|
if len(lhs) != len(rhs) {
|
||||||
|
check.useLHS(lhs...)
|
||||||
|
// don't report an error if we already reported one
|
||||||
|
for _, x := range rhs {
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check.errorf(rhs[0], "cannot assign %d values to %d variables", len(rhs), len(lhs))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if commaOk {
|
||||||
|
var a [2]Type
|
||||||
|
for i := range a {
|
||||||
|
a[i] = check.assignVar(lhs[i], rhs[i])
|
||||||
|
}
|
||||||
|
check.recordCommaOkTypes(orig_rhs[0], a)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, lhs := range lhs {
|
||||||
|
check.assignVar(lhs, rhs[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpack unpacks a *syntax.ListExpr into a list of syntax.Expr.
|
||||||
|
// Helper introduced for the go/types -> types2 port.
|
||||||
|
// TODO(gri) Should find a more efficient solution that doesn't
|
||||||
|
// require introduction of a new slice for simple
|
||||||
|
// expressions.
|
||||||
|
func unpackExpr(x syntax.Expr) []syntax.Expr {
|
||||||
|
if x, _ := x.(*syntax.ListExpr); x != nil {
|
||||||
|
return x.ElemList
|
||||||
|
}
|
||||||
|
if x != nil {
|
||||||
|
return []syntax.Expr{x}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) shortVarDecl(pos syntax.Pos, lhs, rhs []syntax.Expr) {
|
||||||
|
top := len(check.delayed)
|
||||||
|
scope := check.scope
|
||||||
|
|
||||||
|
// collect lhs variables
|
||||||
|
var newVars []*Var
|
||||||
|
var lhsVars = make([]*Var, len(lhs))
|
||||||
|
for i, lhs := range lhs {
|
||||||
|
var obj *Var
|
||||||
|
if ident, _ := lhs.(*syntax.Name); ident != nil {
|
||||||
|
// Use the correct obj if the ident is redeclared. The
|
||||||
|
// variable's scope starts after the declaration; so we
|
||||||
|
// must use Scope.Lookup here and call Scope.Insert
|
||||||
|
// (via check.declare) later.
|
||||||
|
name := ident.Value
|
||||||
|
if alt := scope.Lookup(name); alt != nil {
|
||||||
|
// redeclared object must be a variable
|
||||||
|
if alt, _ := alt.(*Var); alt != nil {
|
||||||
|
obj = alt
|
||||||
|
} else {
|
||||||
|
check.errorf(lhs, "cannot assign to %s", lhs)
|
||||||
|
}
|
||||||
|
check.recordUse(ident, alt)
|
||||||
|
} else {
|
||||||
|
// declare new variable, possibly a blank (_) variable
|
||||||
|
obj = NewVar(ident.Pos(), check.pkg, name, nil)
|
||||||
|
if name != "_" {
|
||||||
|
newVars = append(newVars, obj)
|
||||||
|
}
|
||||||
|
check.recordDef(ident, obj)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
check.useLHS(lhs)
|
||||||
|
check.errorf(lhs, "cannot declare %s", lhs)
|
||||||
|
}
|
||||||
|
if obj == nil {
|
||||||
|
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
|
||||||
|
}
|
||||||
|
lhsVars[i] = obj
|
||||||
|
}
|
||||||
|
|
||||||
|
check.initVars(lhsVars, rhs, nopos)
|
||||||
|
|
||||||
|
// process function literals in rhs expressions before scope changes
|
||||||
|
check.processDelayed(top)
|
||||||
|
|
||||||
|
// declare new variables
|
||||||
|
if len(newVars) > 0 {
|
||||||
|
// spec: "The scope of a constant or variable identifier declared inside
|
||||||
|
// a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
|
||||||
|
// for short variable declarations) and ends at the end of the innermost
|
||||||
|
// containing block."
|
||||||
|
scopePos := endPos(rhs[len(rhs)-1])
|
||||||
|
for _, obj := range newVars {
|
||||||
|
check.declare(scope, nil, obj, scopePos) // recordObject already called
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
check.softErrorf(pos, "no new variables on left side of :=")
|
||||||
|
}
|
||||||
|
}
|
||||||
776
src/cmd/compile/internal/types2/builtins.go
Normal file
776
src/cmd/compile/internal/types2/builtins.go
Normal file
|
|
@ -0,0 +1,776 @@
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements typechecking of builtin function calls.
|
||||||
|
|
||||||
|
package types2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"go/constant"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// builtin type-checks a call to the built-in specified by id and
|
||||||
|
// reports whether the call is valid, with *x holding the result;
|
||||||
|
// but x.expr is not set. If the call is invalid, the result is
|
||||||
|
// false, and *x is undefined.
|
||||||
|
//
|
||||||
|
func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (_ bool) {
|
||||||
|
// append is the only built-in that permits the use of ... for the last argument
|
||||||
|
bin := predeclaredFuncs[id]
|
||||||
|
if call.HasDots && id != _Append {
|
||||||
|
//check.invalidOpf(call.Ellipsis, "invalid use of ... with built-in %s", bin.name)
|
||||||
|
check.invalidOpf(call, "invalid use of ... with built-in %s", bin.name)
|
||||||
|
check.use(call.ArgList...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// For len(x) and cap(x) we need to know if x contains any function calls or
|
||||||
|
// receive operations. Save/restore current setting and set hasCallOrRecv to
|
||||||
|
// false for the evaluation of x so that we can check it afterwards.
|
||||||
|
// Note: We must do this _before_ calling exprList because exprList evaluates
|
||||||
|
// all arguments.
|
||||||
|
if id == _Len || id == _Cap {
|
||||||
|
defer func(b bool) {
|
||||||
|
check.hasCallOrRecv = b
|
||||||
|
}(check.hasCallOrRecv)
|
||||||
|
check.hasCallOrRecv = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine actual arguments
|
||||||
|
var arg func(*operand, int) // TODO(gri) remove use of arg getter in favor of using xlist directly
|
||||||
|
nargs := len(call.ArgList)
|
||||||
|
switch id {
|
||||||
|
default:
|
||||||
|
// make argument getter
|
||||||
|
xlist, _ := check.exprList(call.ArgList, false)
|
||||||
|
arg = func(x *operand, i int) { *x = *xlist[i]; x.typ = expand(x.typ) }
|
||||||
|
nargs = len(xlist)
|
||||||
|
// evaluate first argument, if present
|
||||||
|
if nargs > 0 {
|
||||||
|
arg(x, 0)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _Make, _New, _Offsetof, _Trace:
|
||||||
|
// arguments require special handling
|
||||||
|
}
|
||||||
|
|
||||||
|
// check argument count
|
||||||
|
{
|
||||||
|
msg := ""
|
||||||
|
if nargs < bin.nargs {
|
||||||
|
msg = "not enough"
|
||||||
|
} else if !bin.variadic && nargs > bin.nargs {
|
||||||
|
msg = "too many"
|
||||||
|
}
|
||||||
|
if msg != "" {
|
||||||
|
check.invalidOpf(call, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch id {
|
||||||
|
case _Append:
|
||||||
|
// append(s S, x ...T) S, where T is the element type of S
|
||||||
|
// spec: "The variadic function append appends zero or more values x to s of type
|
||||||
|
// S, which must be a slice type, and returns the resulting slice, also of type S.
|
||||||
|
// The values x are passed to a parameter of type ...T where T is the element type
|
||||||
|
// of S and the respective parameter passing rules apply."
|
||||||
|
S := x.typ
|
||||||
|
var T Type
|
||||||
|
if s := asSlice(S); s != nil {
|
||||||
|
T = s.elem
|
||||||
|
} else {
|
||||||
|
check.invalidArgf(x, "%s is not a slice", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember arguments that have been evaluated already
|
||||||
|
alist := []operand{*x}
|
||||||
|
|
||||||
|
// spec: "As a special case, append also accepts a first argument assignable
|
||||||
|
// to type []byte with a second argument of string type followed by ... .
|
||||||
|
// This form appends the bytes of the string.
|
||||||
|
if nargs == 2 && call.HasDots && x.assignableTo(check, NewSlice(universeByte), nil) {
|
||||||
|
arg(x, 1)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if isString(x.typ) {
|
||||||
|
if check.Types != nil {
|
||||||
|
sig := makeSig(S, S, x.typ)
|
||||||
|
sig.variadic = true
|
||||||
|
check.recordBuiltinType(call.Fun, sig)
|
||||||
|
}
|
||||||
|
x.mode = value
|
||||||
|
x.typ = S
|
||||||
|
break
|
||||||
|
}
|
||||||
|
alist = append(alist, *x)
|
||||||
|
// fallthrough
|
||||||
|
}
|
||||||
|
|
||||||
|
// check general case by creating custom signature
|
||||||
|
sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature
|
||||||
|
sig.variadic = true
|
||||||
|
var xlist []*operand
|
||||||
|
// convert []operand to []*operand
|
||||||
|
for i := range alist {
|
||||||
|
xlist = append(xlist, &alist[i])
|
||||||
|
}
|
||||||
|
for i := len(alist); i < nargs; i++ {
|
||||||
|
var x operand
|
||||||
|
arg(&x, i)
|
||||||
|
xlist = append(xlist, &x)
|
||||||
|
}
|
||||||
|
check.arguments(call, sig, xlist) // discard result (we know the result type)
|
||||||
|
// ok to continue even if check.arguments reported errors
|
||||||
|
|
||||||
|
x.mode = value
|
||||||
|
x.typ = S
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Cap, _Len:
|
||||||
|
// cap(x)
|
||||||
|
// len(x)
|
||||||
|
mode := invalid
|
||||||
|
var typ Type
|
||||||
|
var val constant.Value
|
||||||
|
switch typ = implicitArrayDeref(optype(x.typ)); t := typ.(type) {
|
||||||
|
case *Basic:
|
||||||
|
if isString(t) && id == _Len {
|
||||||
|
if x.mode == constant_ {
|
||||||
|
mode = constant_
|
||||||
|
val = constant.MakeInt64(int64(len(constant.StringVal(x.val))))
|
||||||
|
} else {
|
||||||
|
mode = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Array:
|
||||||
|
mode = value
|
||||||
|
// spec: "The expressions len(s) and cap(s) are constants
|
||||||
|
// if the type of s is an array or pointer to an array and
|
||||||
|
// the expression s does not contain channel receives or
|
||||||
|
// function calls; in this case s is not evaluated."
|
||||||
|
if !check.hasCallOrRecv {
|
||||||
|
mode = constant_
|
||||||
|
if t.len >= 0 {
|
||||||
|
val = constant.MakeInt64(t.len)
|
||||||
|
} else {
|
||||||
|
val = constant.MakeUnknown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Slice, *Chan:
|
||||||
|
mode = value
|
||||||
|
|
||||||
|
case *Map:
|
||||||
|
if id == _Len {
|
||||||
|
mode = value
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Sum:
|
||||||
|
if t.is(func(t Type) bool {
|
||||||
|
switch t := under(t).(type) {
|
||||||
|
case *Basic:
|
||||||
|
if isString(t) && id == _Len {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case *Array, *Slice, *Chan:
|
||||||
|
return true
|
||||||
|
case *Map:
|
||||||
|
if id == _Len {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}) {
|
||||||
|
mode = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mode == invalid && typ != Typ[Invalid] {
|
||||||
|
check.invalidArgf(x, "%s for %s", x, bin.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = mode
|
||||||
|
x.typ = Typ[Int]
|
||||||
|
x.val = val
|
||||||
|
if check.Types != nil && mode != constant_ {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ, typ))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Close:
|
||||||
|
// close(c)
|
||||||
|
c := asChan(x.typ)
|
||||||
|
if c == nil {
|
||||||
|
check.invalidArgf(x, "%s is not a channel", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.dir == RecvOnly {
|
||||||
|
check.invalidArgf(x, "%s must not be a receive-only channel", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = novalue
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(nil, c))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Complex:
|
||||||
|
// complex(x, y floatT) complexT
|
||||||
|
var y operand
|
||||||
|
arg(&y, 1)
|
||||||
|
if y.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert or check untyped arguments
|
||||||
|
d := 0
|
||||||
|
if isUntyped(x.typ) {
|
||||||
|
d |= 1
|
||||||
|
}
|
||||||
|
if isUntyped(y.typ) {
|
||||||
|
d |= 2
|
||||||
|
}
|
||||||
|
switch d {
|
||||||
|
case 0:
|
||||||
|
// x and y are typed => nothing to do
|
||||||
|
case 1:
|
||||||
|
// only x is untyped => convert to type of y
|
||||||
|
check.convertUntyped(x, y.typ)
|
||||||
|
case 2:
|
||||||
|
// only y is untyped => convert to type of x
|
||||||
|
check.convertUntyped(&y, x.typ)
|
||||||
|
case 3:
|
||||||
|
// x and y are untyped =>
|
||||||
|
// 1) if both are constants, convert them to untyped
|
||||||
|
// floating-point numbers if possible,
|
||||||
|
// 2) if one of them is not constant (possible because
|
||||||
|
// it contains a shift that is yet untyped), convert
|
||||||
|
// both of them to float64 since they must have the
|
||||||
|
// same type to succeed (this will result in an error
|
||||||
|
// because shifts of floats are not permitted)
|
||||||
|
if x.mode == constant_ && y.mode == constant_ {
|
||||||
|
toFloat := func(x *operand) {
|
||||||
|
if isNumeric(x.typ) && constant.Sign(constant.Imag(x.val)) == 0 {
|
||||||
|
x.typ = Typ[UntypedFloat]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toFloat(x)
|
||||||
|
toFloat(&y)
|
||||||
|
} else {
|
||||||
|
check.convertUntyped(x, Typ[Float64])
|
||||||
|
check.convertUntyped(&y, Typ[Float64])
|
||||||
|
// x and y should be invalid now, but be conservative
|
||||||
|
// and check below
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if x.mode == invalid || y.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// both argument types must be identical
|
||||||
|
if !check.identical(x.typ, y.typ) {
|
||||||
|
check.invalidOpf(x, "%s (mismatched types %s and %s)", call, x.typ, y.typ)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// the argument types must be of floating-point type
|
||||||
|
f := func(x Type) Type {
|
||||||
|
if t := asBasic(x); t != nil {
|
||||||
|
switch t.kind {
|
||||||
|
case Float32:
|
||||||
|
return Typ[Complex64]
|
||||||
|
case Float64:
|
||||||
|
return Typ[Complex128]
|
||||||
|
case UntypedFloat:
|
||||||
|
return Typ[UntypedComplex]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
resTyp := check.applyTypeFunc(f, x.typ)
|
||||||
|
if resTyp == nil {
|
||||||
|
check.invalidArgf(x, "arguments have type %s, expected floating-point", x.typ)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if both arguments are constants, the result is a constant
|
||||||
|
if x.mode == constant_ && y.mode == constant_ {
|
||||||
|
x.val = constant.BinaryOp(constant.ToFloat(x.val), token.ADD, constant.MakeImag(constant.ToFloat(y.val)))
|
||||||
|
} else {
|
||||||
|
x.mode = value
|
||||||
|
}
|
||||||
|
|
||||||
|
if check.Types != nil && x.mode != constant_ {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ, x.typ))
|
||||||
|
}
|
||||||
|
|
||||||
|
x.typ = resTyp
|
||||||
|
|
||||||
|
case _Copy:
|
||||||
|
// copy(x, y []T) int
|
||||||
|
var dst Type
|
||||||
|
if t := asSlice(x.typ); t != nil {
|
||||||
|
dst = t.elem
|
||||||
|
}
|
||||||
|
|
||||||
|
var y operand
|
||||||
|
arg(&y, 1)
|
||||||
|
if y.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var src Type
|
||||||
|
switch t := optype(y.typ).(type) {
|
||||||
|
case *Basic:
|
||||||
|
if isString(y.typ) {
|
||||||
|
src = universeByte
|
||||||
|
}
|
||||||
|
case *Slice:
|
||||||
|
src = t.elem
|
||||||
|
}
|
||||||
|
|
||||||
|
if dst == nil || src == nil {
|
||||||
|
check.invalidArgf(x, "copy expects slice arguments; found %s and %s", x, &y)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !check.identical(dst, src) {
|
||||||
|
check.invalidArgf(x, "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ, y.typ))
|
||||||
|
}
|
||||||
|
x.mode = value
|
||||||
|
x.typ = Typ[Int]
|
||||||
|
|
||||||
|
case _Delete:
|
||||||
|
// delete(m, k)
|
||||||
|
m := asMap(x.typ)
|
||||||
|
if m == nil {
|
||||||
|
check.invalidArgf(x, "%s is not a map", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
arg(x, 1) // k
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
check.assignment(x, m.key, "argument to delete")
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = novalue
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Imag, _Real:
|
||||||
|
// imag(complexT) floatT
|
||||||
|
// real(complexT) floatT
|
||||||
|
|
||||||
|
// convert or check untyped argument
|
||||||
|
if isUntyped(x.typ) {
|
||||||
|
if x.mode == constant_ {
|
||||||
|
// an untyped constant number can always be considered
|
||||||
|
// as a complex constant
|
||||||
|
if isNumeric(x.typ) {
|
||||||
|
x.typ = Typ[UntypedComplex]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// an untyped non-constant argument may appear if
|
||||||
|
// it contains a (yet untyped non-constant) shift
|
||||||
|
// expression: convert it to complex128 which will
|
||||||
|
// result in an error (shift of complex value)
|
||||||
|
check.convertUntyped(x, Typ[Complex128])
|
||||||
|
// x should be invalid now, but be conservative and check
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the argument must be of complex type
|
||||||
|
f := func(x Type) Type {
|
||||||
|
if t := asBasic(x); t != nil {
|
||||||
|
switch t.kind {
|
||||||
|
case Complex64:
|
||||||
|
return Typ[Float32]
|
||||||
|
case Complex128:
|
||||||
|
return Typ[Float64]
|
||||||
|
case UntypedComplex:
|
||||||
|
return Typ[UntypedFloat]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
resTyp := check.applyTypeFunc(f, x.typ)
|
||||||
|
if resTyp == nil {
|
||||||
|
check.invalidArgf(x, "argument has type %s, expected complex type", x.typ)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the argument is a constant, the result is a constant
|
||||||
|
if x.mode == constant_ {
|
||||||
|
if id == _Real {
|
||||||
|
x.val = constant.Real(x.val)
|
||||||
|
} else {
|
||||||
|
x.val = constant.Imag(x.val)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x.mode = value
|
||||||
|
}
|
||||||
|
|
||||||
|
if check.Types != nil && x.mode != constant_ {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ))
|
||||||
|
}
|
||||||
|
|
||||||
|
x.typ = resTyp
|
||||||
|
|
||||||
|
case _Make:
|
||||||
|
// make(T, n)
|
||||||
|
// make(T, n, m)
|
||||||
|
// (no argument evaluated yet)
|
||||||
|
arg0 := call.ArgList[0]
|
||||||
|
T := check.varType(arg0)
|
||||||
|
if T == Typ[Invalid] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
min, max := -1, 10
|
||||||
|
var valid func(t Type) bool
|
||||||
|
valid = func(t Type) bool {
|
||||||
|
var m int
|
||||||
|
switch t := optype(t).(type) {
|
||||||
|
case *Slice:
|
||||||
|
m = 2
|
||||||
|
case *Map, *Chan:
|
||||||
|
m = 1
|
||||||
|
case *Sum:
|
||||||
|
return t.is(valid)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if m > min {
|
||||||
|
min = m
|
||||||
|
}
|
||||||
|
if m+1 < max {
|
||||||
|
max = m + 1
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !valid(T) {
|
||||||
|
check.invalidArgf(arg0, "cannot make %s; type must be slice, map, or channel", arg0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if nargs < min || max < nargs {
|
||||||
|
if min == max {
|
||||||
|
check.errorf(call, "%v expects %d arguments; found %d", call, min, nargs)
|
||||||
|
} else {
|
||||||
|
check.errorf(call, "%v expects %d or %d arguments; found %d", call, min, max, nargs)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
types := []Type{T}
|
||||||
|
var sizes []int64 // constant integer arguments, if any
|
||||||
|
for _, arg := range call.ArgList[1:] {
|
||||||
|
typ, size := check.index(arg, -1) // ok to continue with typ == Typ[Invalid]
|
||||||
|
types = append(types, typ)
|
||||||
|
if size >= 0 {
|
||||||
|
sizes = append(sizes, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(sizes) == 2 && sizes[0] > sizes[1] {
|
||||||
|
check.invalidArgf(call.ArgList[1], "length and capacity swapped")
|
||||||
|
// safe to continue
|
||||||
|
}
|
||||||
|
x.mode = value
|
||||||
|
x.typ = T
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ, types...))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _New:
|
||||||
|
// new(T)
|
||||||
|
// (no argument evaluated yet)
|
||||||
|
T := check.varType(call.ArgList[0])
|
||||||
|
if T == Typ[Invalid] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = value
|
||||||
|
x.typ = &Pointer{base: T}
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ, T))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Panic:
|
||||||
|
// panic(x)
|
||||||
|
// record panic call if inside a function with result parameters
|
||||||
|
// (for use in Checker.isTerminating)
|
||||||
|
if check.sig != nil && check.sig.results.Len() > 0 {
|
||||||
|
// function has result parameters
|
||||||
|
p := check.isPanic
|
||||||
|
if p == nil {
|
||||||
|
// allocate lazily
|
||||||
|
p = make(map[*syntax.CallExpr]bool)
|
||||||
|
check.isPanic = p
|
||||||
|
}
|
||||||
|
p[call] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
check.assignment(x, &emptyInterface, "argument to panic")
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = novalue
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(nil, &emptyInterface))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Print, _Println:
|
||||||
|
// print(x, y, ...)
|
||||||
|
// println(x, y, ...)
|
||||||
|
var params []Type
|
||||||
|
if nargs > 0 {
|
||||||
|
params = make([]Type, nargs)
|
||||||
|
for i := 0; i < nargs; i++ {
|
||||||
|
if i > 0 {
|
||||||
|
arg(x, i) // first argument already evaluated
|
||||||
|
}
|
||||||
|
check.assignment(x, nil, "argument to "+predeclaredFuncs[id].name)
|
||||||
|
if x.mode == invalid {
|
||||||
|
// TODO(gri) "use" all arguments?
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params[i] = x.typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = novalue
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(nil, params...))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Recover:
|
||||||
|
// recover() interface{}
|
||||||
|
x.mode = value
|
||||||
|
x.typ = &emptyInterface
|
||||||
|
if check.Types != nil {
|
||||||
|
check.recordBuiltinType(call.Fun, makeSig(x.typ))
|
||||||
|
}
|
||||||
|
|
||||||
|
case _Alignof:
|
||||||
|
// unsafe.Alignof(x T) uintptr
|
||||||
|
if asTypeParam(x.typ) != nil {
|
||||||
|
check.invalidOpf(call, "unsafe.Alignof undefined for %s", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check.assignment(x, nil, "argument to unsafe.Alignof")
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = constant_
|
||||||
|
x.val = constant.MakeInt64(check.conf.alignof(x.typ))
|
||||||
|
x.typ = Typ[Uintptr]
|
||||||
|
// result is constant - no need to record signature
|
||||||
|
|
||||||
|
case _Offsetof:
|
||||||
|
// unsafe.Offsetof(x T) uintptr, where x must be a selector
|
||||||
|
// (no argument evaluated yet)
|
||||||
|
arg0 := call.ArgList[0]
|
||||||
|
selx, _ := unparen(arg0).(*syntax.SelectorExpr)
|
||||||
|
if selx == nil {
|
||||||
|
check.invalidArgf(arg0, "%s is not a selector expression", arg0)
|
||||||
|
check.use(arg0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
check.expr(x, selx.X)
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
base := derefStructPtr(x.typ)
|
||||||
|
sel := selx.Sel.Value
|
||||||
|
obj, index, indirect := check.lookupFieldOrMethod(base, false, check.pkg, sel)
|
||||||
|
switch obj.(type) {
|
||||||
|
case nil:
|
||||||
|
check.invalidArgf(x, "%s has no single field %s", base, sel)
|
||||||
|
return
|
||||||
|
case *Func:
|
||||||
|
// TODO(gri) Using derefStructPtr may result in methods being found
|
||||||
|
// that don't actually exist. An error either way, but the error
|
||||||
|
// message is confusing. See: https://play.golang.org/p/al75v23kUy ,
|
||||||
|
// but go/types reports: "invalid argument: x.m is a method value".
|
||||||
|
check.invalidArgf(arg0, "%s is a method value", arg0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if indirect {
|
||||||
|
check.invalidArgf(x, "field %s is embedded via a pointer in %s", sel, base)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)?
|
||||||
|
check.recordSelection(selx, FieldVal, base, obj, index, false)
|
||||||
|
|
||||||
|
offs := check.conf.offsetof(base, index)
|
||||||
|
x.mode = constant_
|
||||||
|
x.val = constant.MakeInt64(offs)
|
||||||
|
x.typ = Typ[Uintptr]
|
||||||
|
// result is constant - no need to record signature
|
||||||
|
|
||||||
|
case _Sizeof:
|
||||||
|
// unsafe.Sizeof(x T) uintptr
|
||||||
|
if asTypeParam(x.typ) != nil {
|
||||||
|
check.invalidOpf(call, "unsafe.Sizeof undefined for %s", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
check.assignment(x, nil, "argument to unsafe.Sizeof")
|
||||||
|
if x.mode == invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
x.mode = constant_
|
||||||
|
x.val = constant.MakeInt64(check.conf.sizeof(x.typ))
|
||||||
|
x.typ = Typ[Uintptr]
|
||||||
|
// result is constant - no need to record signature
|
||||||
|
|
||||||
|
case _Assert:
|
||||||
|
// assert(pred) causes a typechecker error if pred is false.
|
||||||
|
// The result of assert is the value of pred if there is no error.
|
||||||
|
// Note: assert is only available in self-test mode.
|
||||||
|
if x.mode != constant_ || !isBoolean(x.typ) {
|
||||||
|
check.invalidArgf(x, "%s is not a boolean constant", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if x.val.Kind() != constant.Bool {
|
||||||
|
check.errorf(x, "internal error: value of %s should be a boolean constant", x)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !constant.BoolVal(x.val) {
|
||||||
|
check.errorf(call, "%v failed", call)
|
||||||
|
// compile-time assertion failure - safe to continue
|
||||||
|
}
|
||||||
|
// result is constant - no need to record signature
|
||||||
|
|
||||||
|
case _Trace:
|
||||||
|
// trace(x, y, z, ...) dumps the positions, expressions, and
|
||||||
|
// values of its arguments. The result of trace is the value
|
||||||
|
// of the first argument.
|
||||||
|
// Note: trace is only available in self-test mode.
|
||||||
|
// (no argument evaluated yet)
|
||||||
|
if nargs == 0 {
|
||||||
|
check.dump("%v: trace() without arguments", posFor(call))
|
||||||
|
x.mode = novalue
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var t operand
|
||||||
|
x1 := x
|
||||||
|
for _, arg := range call.ArgList {
|
||||||
|
check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T))
|
||||||
|
check.dump("%v: %s", posFor(x1), x1)
|
||||||
|
x1 = &t // use incoming x only for first argument
|
||||||
|
}
|
||||||
|
// trace is only available in test mode - no need to record signature
|
||||||
|
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyTypeFunc applies f to x. If x is a type parameter,
|
||||||
|
// the result is a type parameter constrained by an new
|
||||||
|
// interface bound. The type bounds for that interface
|
||||||
|
// are computed by applying f to each of the type bounds
|
||||||
|
// of x. If any of these applications of f return nil,
|
||||||
|
// applyTypeFunc returns nil.
|
||||||
|
// If x is not a type parameter, the result is f(x).
|
||||||
|
func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
|
||||||
|
if tp := asTypeParam(x); tp != nil {
|
||||||
|
// Test if t satisfies the requirements for the argument
|
||||||
|
// type and collect possible result types at the same time.
|
||||||
|
var rtypes []Type
|
||||||
|
if !tp.Bound().is(func(x Type) bool {
|
||||||
|
if r := f(x); r != nil {
|
||||||
|
rtypes = append(rtypes, r)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) Would it be ok to return just the one type
|
||||||
|
// if len(rtypes) == 1? What about top-level
|
||||||
|
// uses of real() where the result is used to
|
||||||
|
// define type and initialize a variable?
|
||||||
|
|
||||||
|
// construct a suitable new type parameter
|
||||||
|
tpar := NewTypeName(nopos, nil /* = Universe pkg */, "<type parameter>", nil)
|
||||||
|
ptyp := check.NewTypeParam(tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect
|
||||||
|
tsum := NewSum(rtypes)
|
||||||
|
ptyp.bound = &Interface{types: tsum, allMethods: markComplete, allTypes: tsum}
|
||||||
|
|
||||||
|
return ptyp
|
||||||
|
}
|
||||||
|
|
||||||
|
return f(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeSig makes a signature for the given argument and result types.
|
||||||
|
// Default types are used for untyped arguments, and res may be nil.
|
||||||
|
func makeSig(res Type, args ...Type) *Signature {
|
||||||
|
list := make([]*Var, len(args))
|
||||||
|
for i, param := range args {
|
||||||
|
list[i] = NewVar(nopos, nil, "", Default(param))
|
||||||
|
}
|
||||||
|
params := NewTuple(list...)
|
||||||
|
var result *Tuple
|
||||||
|
if res != nil {
|
||||||
|
assert(!isUntyped(res))
|
||||||
|
result = NewTuple(NewVar(nopos, nil, "", res))
|
||||||
|
}
|
||||||
|
return &Signature{params: params, results: result}
|
||||||
|
}
|
||||||
|
|
||||||
|
// implicitArrayDeref returns A if typ is of the form *A and A is an array;
|
||||||
|
// otherwise it returns typ.
|
||||||
|
//
|
||||||
|
func implicitArrayDeref(typ Type) Type {
|
||||||
|
if p, ok := typ.(*Pointer); ok {
|
||||||
|
if a := asArray(p.base); a != nil {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typ
|
||||||
|
}
|
||||||
|
|
||||||
|
// unparen returns e with any enclosing parentheses stripped.
|
||||||
|
func unparen(e syntax.Expr) syntax.Expr {
|
||||||
|
for {
|
||||||
|
p, ok := e.(*syntax.ParenExpr)
|
||||||
|
if !ok {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
e = p.X
|
||||||
|
}
|
||||||
|
}
|
||||||
218
src/cmd/compile/internal/types2/builtins_test.go
Normal file
218
src/cmd/compile/internal/types2/builtins_test.go
Normal file
|
|
@ -0,0 +1,218 @@
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types2_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "cmd/compile/internal/types2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var builtinCalls = []struct {
|
||||||
|
name, src, sig string
|
||||||
|
}{
|
||||||
|
{"append", `var s []int; _ = append(s)`, `func([]int, ...int) []int`},
|
||||||
|
{"append", `var s []int; _ = append(s, 0)`, `func([]int, ...int) []int`},
|
||||||
|
{"append", `var s []int; _ = (append)(s, 0)`, `func([]int, ...int) []int`},
|
||||||
|
{"append", `var s []byte; _ = ((append))(s, 0)`, `func([]byte, ...byte) []byte`},
|
||||||
|
{"append", `var s []byte; _ = append(s, "foo"...)`, `func([]byte, string...) []byte`},
|
||||||
|
{"append", `type T []byte; var s T; var str string; _ = append(s, str...)`, `func(p.T, string...) p.T`},
|
||||||
|
{"append", `type T []byte; type U string; var s T; var str U; _ = append(s, str...)`, `func(p.T, p.U...) p.T`},
|
||||||
|
|
||||||
|
{"cap", `var s [10]int; _ = cap(s)`, `invalid type`}, // constant
|
||||||
|
{"cap", `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant
|
||||||
|
{"cap", `var s []int64; _ = cap(s)`, `func([]int64) int`},
|
||||||
|
{"cap", `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`},
|
||||||
|
|
||||||
|
{"len", `_ = len("foo")`, `invalid type`}, // constant
|
||||||
|
{"len", `var s string; _ = len(s)`, `func(string) int`},
|
||||||
|
{"len", `var s [10]int; _ = len(s)`, `invalid type`}, // constant
|
||||||
|
{"len", `var s [10]int; _ = len(&s)`, `invalid type`}, // constant
|
||||||
|
{"len", `var s []int64; _ = len(s)`, `func([]int64) int`},
|
||||||
|
{"len", `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`},
|
||||||
|
{"len", `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`},
|
||||||
|
|
||||||
|
{"close", `var c chan int; close(c)`, `func(chan int)`},
|
||||||
|
{"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`},
|
||||||
|
|
||||||
|
{"complex", `_ = complex(1, 0)`, `invalid type`}, // constant
|
||||||
|
{"complex", `var re float32; _ = complex(re, 1.0)`, `func(float32, float32) complex64`},
|
||||||
|
{"complex", `var im float64; _ = complex(1, im)`, `func(float64, float64) complex128`},
|
||||||
|
{"complex", `type F32 float32; var re, im F32; _ = complex(re, im)`, `func(p.F32, p.F32) complex64`},
|
||||||
|
{"complex", `type F64 float64; var re, im F64; _ = complex(re, im)`, `func(p.F64, p.F64) complex128`},
|
||||||
|
|
||||||
|
{"copy", `var src, dst []byte; copy(dst, src)`, `func([]byte, []byte) int`},
|
||||||
|
{"copy", `type T [][]int; var src, dst T; _ = copy(dst, src)`, `func(p.T, p.T) int`},
|
||||||
|
{"copy", `var src string; var dst []byte; copy(dst, src)`, `func([]byte, string) int`},
|
||||||
|
{"copy", `type T string; type U []byte; var src T; var dst U; copy(dst, src)`, `func(p.U, p.T) int`},
|
||||||
|
{"copy", `var dst []byte; copy(dst, "hello")`, `func([]byte, string) int`},
|
||||||
|
|
||||||
|
{"delete", `var m map[string]bool; delete(m, "foo")`, `func(map[string]bool, string)`},
|
||||||
|
{"delete", `type (K string; V int); var m map[K]V; delete(m, "foo")`, `func(map[p.K]p.V, p.K)`},
|
||||||
|
|
||||||
|
{"imag", `_ = imag(1i)`, `invalid type`}, // constant
|
||||||
|
{"imag", `var c complex64; _ = imag(c)`, `func(complex64) float32`},
|
||||||
|
{"imag", `var c complex128; _ = imag(c)`, `func(complex128) float64`},
|
||||||
|
{"imag", `type C64 complex64; var c C64; _ = imag(c)`, `func(p.C64) float32`},
|
||||||
|
{"imag", `type C128 complex128; var c C128; _ = imag(c)`, `func(p.C128) float64`},
|
||||||
|
|
||||||
|
{"real", `_ = real(1i)`, `invalid type`}, // constant
|
||||||
|
{"real", `var c complex64; _ = real(c)`, `func(complex64) float32`},
|
||||||
|
{"real", `var c complex128; _ = real(c)`, `func(complex128) float64`},
|
||||||
|
{"real", `type C64 complex64; var c C64; _ = real(c)`, `func(p.C64) float32`},
|
||||||
|
{"real", `type C128 complex128; var c C128; _ = real(c)`, `func(p.C128) float64`},
|
||||||
|
|
||||||
|
{"make", `_ = make([]int, 10)`, `func([]int, int) []int`},
|
||||||
|
{"make", `type T []byte; _ = make(T, 10, 20)`, `func(p.T, int, int) p.T`},
|
||||||
|
|
||||||
|
// issue #37349
|
||||||
|
{"make", ` _ = make([]int, 0 )`, `func([]int, int) []int`},
|
||||||
|
{"make", `var l int; _ = make([]int, l )`, `func([]int, int) []int`},
|
||||||
|
{"make", ` _ = make([]int, 0, 0)`, `func([]int, int, int) []int`},
|
||||||
|
{"make", `var l int; _ = make([]int, l, 0)`, `func([]int, int, int) []int`},
|
||||||
|
{"make", `var c int; _ = make([]int, 0, c)`, `func([]int, int, int) []int`},
|
||||||
|
{"make", `var l, c int; _ = make([]int, l, c)`, `func([]int, int, int) []int`},
|
||||||
|
|
||||||
|
// issue #37393
|
||||||
|
{"make", ` _ = make([]int , 0 )`, `func([]int, int) []int`},
|
||||||
|
{"make", `var l byte ; _ = make([]int8 , l )`, `func([]int8, byte) []int8`},
|
||||||
|
{"make", ` _ = make([]int16 , 0, 0)`, `func([]int16, int, int) []int16`},
|
||||||
|
{"make", `var l int16; _ = make([]string , l, 0)`, `func([]string, int16, int) []string`},
|
||||||
|
{"make", `var c int32; _ = make([]float64 , 0, c)`, `func([]float64, int, int32) []float64`},
|
||||||
|
{"make", `var l, c uint ; _ = make([]complex128, l, c)`, `func([]complex128, uint, uint) []complex128`},
|
||||||
|
|
||||||
|
{"new", `_ = new(int)`, `func(int) *int`},
|
||||||
|
{"new", `type T struct{}; _ = new(T)`, `func(p.T) *p.T`},
|
||||||
|
|
||||||
|
{"panic", `panic(0)`, `func(interface{})`},
|
||||||
|
{"panic", `panic("foo")`, `func(interface{})`},
|
||||||
|
|
||||||
|
{"print", `print()`, `func()`},
|
||||||
|
{"print", `print(0)`, `func(int)`},
|
||||||
|
{"print", `print(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
|
||||||
|
|
||||||
|
{"println", `println()`, `func()`},
|
||||||
|
{"println", `println(0)`, `func(int)`},
|
||||||
|
{"println", `println(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
|
||||||
|
|
||||||
|
{"recover", `recover()`, `func() interface{}`},
|
||||||
|
{"recover", `_ = recover()`, `func() interface{}`},
|
||||||
|
|
||||||
|
{"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant
|
||||||
|
{"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant
|
||||||
|
|
||||||
|
{"Offsetof", `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant
|
||||||
|
{"Offsetof", `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant
|
||||||
|
|
||||||
|
{"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant
|
||||||
|
{"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant
|
||||||
|
|
||||||
|
{"assert", `assert(true)`, `invalid type`}, // constant
|
||||||
|
{"assert", `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant
|
||||||
|
|
||||||
|
// no tests for trace since it produces output as a side-effect
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuiltinSignatures(t *testing.T) {
|
||||||
|
DefPredeclaredTestFuncs()
|
||||||
|
|
||||||
|
seen := map[string]bool{"trace": true} // no test for trace built-in; add it manually
|
||||||
|
for _, call := range builtinCalls {
|
||||||
|
testBuiltinSignature(t, call.name, call.src, call.sig)
|
||||||
|
seen[call.name] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we didn't miss one
|
||||||
|
for _, name := range Universe.Names() {
|
||||||
|
if _, ok := Universe.Lookup(name).(*Builtin); ok && !seen[name] {
|
||||||
|
t.Errorf("missing test for %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, name := range Unsafe.Scope().Names() {
|
||||||
|
if _, ok := Unsafe.Scope().Lookup(name).(*Builtin); ok && !seen[name] {
|
||||||
|
t.Errorf("missing test for unsafe.%s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBuiltinSignature(t *testing.T, name, src0, want string) {
|
||||||
|
src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0)
|
||||||
|
f, err := parseSrc("", src)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %s", src0, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
conf := Config{Importer: defaultImporter()}
|
||||||
|
uses := make(map[*syntax.Name]Object)
|
||||||
|
types := make(map[syntax.Expr]TypeAndValue)
|
||||||
|
_, err = conf.Check(f.PkgName.Value, []*syntax.File{f}, &Info{Uses: uses, Types: types})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %s", src0, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// find called function
|
||||||
|
n := 0
|
||||||
|
var fun syntax.Expr
|
||||||
|
for x := range types {
|
||||||
|
if call, _ := x.(*syntax.CallExpr); call != nil {
|
||||||
|
fun = call.Fun
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n != 1 {
|
||||||
|
t.Errorf("%s: got %d CallExprs; want 1", src0, n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check recorded types for fun and descendents (may be parenthesized)
|
||||||
|
for {
|
||||||
|
// the recorded type for the built-in must match the wanted signature
|
||||||
|
typ := types[fun].Type
|
||||||
|
if typ == nil {
|
||||||
|
t.Errorf("%s: no type recorded for %s", src0, syntax.String(fun))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got := typ.String(); got != want {
|
||||||
|
t.Errorf("%s: got type %s; want %s", src0, got, want)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// called function must be a (possibly parenthesized, qualified)
|
||||||
|
// identifier denoting the expected built-in
|
||||||
|
switch p := fun.(type) {
|
||||||
|
case *syntax.Name:
|
||||||
|
obj := uses[p]
|
||||||
|
if obj == nil {
|
||||||
|
t.Errorf("%s: no object found for %s", src0, p.Value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bin, _ := obj.(*Builtin)
|
||||||
|
if bin == nil {
|
||||||
|
t.Errorf("%s: %s does not denote a built-in", src0, p.Value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bin.Name() != name {
|
||||||
|
t.Errorf("%s: got built-in %s; want %s", src0, bin.Name(), name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return // we're done
|
||||||
|
|
||||||
|
case *syntax.ParenExpr:
|
||||||
|
fun = p.X // unpack
|
||||||
|
|
||||||
|
case *syntax.SelectorExpr:
|
||||||
|
// built-in from package unsafe - ignore details
|
||||||
|
return // we're done
|
||||||
|
|
||||||
|
default:
|
||||||
|
t.Errorf("%s: invalid function call", src0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
770
src/cmd/compile/internal/types2/call.go
Normal file
770
src/cmd/compile/internal/types2/call.go
Normal file
|
|
@ -0,0 +1,770 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements typechecking of call and selector expressions.
|
||||||
|
|
||||||
|
package types2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// funcInst type-checks a function instantiaton inst and returns the result in x.
|
||||||
|
// The operand x must be the evaluation of inst.X and its type must be a signature.
|
||||||
|
func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
|
||||||
|
args, ok := check.exprOrTypeList(unpackExpr(inst.Index))
|
||||||
|
if !ok {
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = inst
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(args) > 0 && args[0].mode != typexpr {
|
||||||
|
check.errorf(args[0], "%s is not a type", args[0])
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// check number of type arguments
|
||||||
|
n := len(args)
|
||||||
|
sig := x.typ.(*Signature)
|
||||||
|
if !check.conf.InferFromConstraints && n != len(sig.tparams) || n > len(sig.tparams) {
|
||||||
|
check.errorf(args[n-1], "got %d type arguments but want %d", n, len(sig.tparams))
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = inst
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect types
|
||||||
|
targs := make([]Type, n)
|
||||||
|
poslist := make([]syntax.Pos, n)
|
||||||
|
for i, a := range args {
|
||||||
|
if a.mode != typexpr {
|
||||||
|
// error was reported earlier
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = inst
|
||||||
|
return
|
||||||
|
}
|
||||||
|
targs[i] = a.typ
|
||||||
|
poslist[i] = a.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we don't have enough type arguments, use constraint type inference
|
||||||
|
var inferred bool
|
||||||
|
if n < len(sig.tparams) {
|
||||||
|
var failed int
|
||||||
|
targs, failed = check.inferB(sig.tparams, targs)
|
||||||
|
if targs == nil {
|
||||||
|
// error was already reported
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = inst
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if failed >= 0 {
|
||||||
|
// at least one type argument couldn't be inferred
|
||||||
|
assert(targs[failed] == nil)
|
||||||
|
tpar := sig.tparams[failed]
|
||||||
|
check.errorf(inst, "cannot infer %s (%s) (%s)", tpar.name, tpar.pos, targs)
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = inst
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// all type arguments were inferred sucessfully
|
||||||
|
if debug {
|
||||||
|
for _, targ := range targs {
|
||||||
|
assert(targ != nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n = len(targs)
|
||||||
|
inferred = true
|
||||||
|
}
|
||||||
|
assert(n == len(sig.tparams))
|
||||||
|
|
||||||
|
// instantiate function signature
|
||||||
|
for i, typ := range targs {
|
||||||
|
// some positions may be missing if types are inferred
|
||||||
|
var pos syntax.Pos
|
||||||
|
if i < len(poslist) {
|
||||||
|
pos = poslist[i]
|
||||||
|
}
|
||||||
|
check.ordinaryType(pos, typ)
|
||||||
|
}
|
||||||
|
res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
|
||||||
|
assert(res.tparams == nil) // signature is not generic anymore
|
||||||
|
if inferred {
|
||||||
|
check.recordInferred(inst, targs, res)
|
||||||
|
}
|
||||||
|
x.typ = res
|
||||||
|
x.mode = value
|
||||||
|
x.expr = inst
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) call(x *operand, call *syntax.CallExpr) exprKind {
|
||||||
|
check.exprOrType(x, call.Fun)
|
||||||
|
|
||||||
|
switch x.mode {
|
||||||
|
case invalid:
|
||||||
|
check.use(call.ArgList...)
|
||||||
|
x.expr = call
|
||||||
|
return statement
|
||||||
|
|
||||||
|
case typexpr:
|
||||||
|
// conversion
|
||||||
|
T := x.typ
|
||||||
|
x.mode = invalid
|
||||||
|
switch n := len(call.ArgList); n {
|
||||||
|
case 0:
|
||||||
|
check.errorf(call, "missing argument in conversion to %s", T)
|
||||||
|
case 1:
|
||||||
|
check.expr(x, call.ArgList[0])
|
||||||
|
if x.mode != invalid {
|
||||||
|
if t := asInterface(T); t != nil {
|
||||||
|
check.completeInterface(nopos, t)
|
||||||
|
if t.IsConstraint() {
|
||||||
|
check.errorf(call, "cannot use interface %s in conversion (contains type list or is comparable)", T)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if call.HasDots {
|
||||||
|
check.errorf(call.ArgList[0], "invalid use of ... in type conversion to %s", T)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
check.conversion(x, T)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
check.use(call.ArgList...)
|
||||||
|
check.errorf(call.ArgList[n-1], "too many arguments in conversion to %s", T)
|
||||||
|
}
|
||||||
|
x.expr = call
|
||||||
|
return conversion
|
||||||
|
|
||||||
|
case builtin:
|
||||||
|
id := x.id
|
||||||
|
if !check.builtin(x, call, id) {
|
||||||
|
x.mode = invalid
|
||||||
|
}
|
||||||
|
x.expr = call
|
||||||
|
// a non-constant result implies a function call
|
||||||
|
if x.mode != invalid && x.mode != constant_ {
|
||||||
|
check.hasCallOrRecv = true
|
||||||
|
}
|
||||||
|
return predeclaredFuncs[id].kind
|
||||||
|
|
||||||
|
default:
|
||||||
|
// function/method call
|
||||||
|
cgocall := x.mode == cgofunc
|
||||||
|
|
||||||
|
sig := asSignature(x.typ)
|
||||||
|
if sig == nil {
|
||||||
|
check.invalidOpf(x, "cannot call non-function %s", x)
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = call
|
||||||
|
return statement
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluate arguments
|
||||||
|
args, ok := check.exprOrTypeList(call.ArgList)
|
||||||
|
if !ok {
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = call
|
||||||
|
return expression
|
||||||
|
}
|
||||||
|
|
||||||
|
sig = check.arguments(call, sig, args)
|
||||||
|
|
||||||
|
// determine result
|
||||||
|
switch sig.results.Len() {
|
||||||
|
case 0:
|
||||||
|
x.mode = novalue
|
||||||
|
case 1:
|
||||||
|
if cgocall {
|
||||||
|
x.mode = commaerr
|
||||||
|
} else {
|
||||||
|
x.mode = value
|
||||||
|
}
|
||||||
|
x.typ = sig.results.vars[0].typ // unpack tuple
|
||||||
|
default:
|
||||||
|
x.mode = value
|
||||||
|
x.typ = sig.results
|
||||||
|
}
|
||||||
|
x.expr = call
|
||||||
|
check.hasCallOrRecv = true
|
||||||
|
|
||||||
|
// if type inference failed, a parametrized result must be invalidated
|
||||||
|
// (operands cannot have a parametrized type)
|
||||||
|
if x.mode == value && len(sig.tparams) > 0 && isParameterized(sig.tparams, x.typ) {
|
||||||
|
x.mode = invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
return statement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// exprOrTypeList returns a list of operands and reports an error if the
|
||||||
|
// list contains a mix of values and types (ignoring invalid operands).
|
||||||
|
// TODO(gri) Now we can split this into exprList and typeList.
|
||||||
|
func (check *Checker) exprOrTypeList(elist []syntax.Expr) (xlist []*operand, ok bool) {
|
||||||
|
ok = true
|
||||||
|
|
||||||
|
switch len(elist) {
|
||||||
|
case 0:
|
||||||
|
// nothing to do
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// single (possibly comma-ok) value or type, or function returning multiple values
|
||||||
|
e := elist[0]
|
||||||
|
var x operand
|
||||||
|
check.multiExprOrType(&x, e)
|
||||||
|
if t, ok := x.typ.(*Tuple); ok && x.mode != invalid && x.mode != typexpr {
|
||||||
|
// multiple values
|
||||||
|
xlist = make([]*operand, t.Len())
|
||||||
|
for i, v := range t.vars {
|
||||||
|
xlist[i] = &operand{mode: value, expr: e, typ: v.typ}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
check.instantiatedOperand(&x)
|
||||||
|
|
||||||
|
// exactly one (possibly invalid or comma-ok) value or type
|
||||||
|
xlist = []*operand{&x}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// multiple (possibly invalid) values or types
|
||||||
|
xlist = make([]*operand, len(elist))
|
||||||
|
ntypes := 0
|
||||||
|
for i, e := range elist {
|
||||||
|
var x operand
|
||||||
|
check.exprOrType(&x, e)
|
||||||
|
xlist[i] = &x
|
||||||
|
switch x.mode {
|
||||||
|
case invalid:
|
||||||
|
ntypes = len(xlist) // make 'if' condition fail below (no additional error in this case)
|
||||||
|
case typexpr:
|
||||||
|
ntypes++
|
||||||
|
check.instantiatedOperand(&x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if 0 < ntypes && ntypes < len(xlist) {
|
||||||
|
check.errorf(xlist[0], "mix of value and type expressions")
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) exprList(elist []syntax.Expr, allowCommaOk bool) (xlist []*operand, commaOk bool) {
|
||||||
|
switch len(elist) {
|
||||||
|
case 0:
|
||||||
|
// nothing to do
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// single (possibly comma-ok) value, or function returning multiple values
|
||||||
|
e := elist[0]
|
||||||
|
var x operand
|
||||||
|
check.multiExpr(&x, e)
|
||||||
|
if t, ok := x.typ.(*Tuple); ok && x.mode != invalid {
|
||||||
|
// multiple values
|
||||||
|
xlist = make([]*operand, t.Len())
|
||||||
|
for i, v := range t.vars {
|
||||||
|
xlist[i] = &operand{mode: value, expr: e, typ: v.typ}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// exactly one (possibly invalid or comma-ok) value
|
||||||
|
xlist = []*operand{&x}
|
||||||
|
if allowCommaOk && (x.mode == mapindex || x.mode == commaok || x.mode == commaerr) {
|
||||||
|
x.mode = value
|
||||||
|
xlist = append(xlist, &operand{mode: value, expr: e, typ: Typ[UntypedBool]})
|
||||||
|
commaOk = true
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// multiple (possibly invalid) values
|
||||||
|
xlist = make([]*operand, len(elist))
|
||||||
|
for i, e := range elist {
|
||||||
|
var x operand
|
||||||
|
check.expr(&x, e)
|
||||||
|
xlist[i] = &x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, args []*operand) (rsig *Signature) {
|
||||||
|
rsig = sig
|
||||||
|
|
||||||
|
// TODO(gri) try to eliminate this extra verification loop
|
||||||
|
for _, a := range args {
|
||||||
|
switch a.mode {
|
||||||
|
case typexpr:
|
||||||
|
check.errorf(a, "%s used as value", a)
|
||||||
|
return
|
||||||
|
case invalid:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function call argument/parameter count requirements
|
||||||
|
//
|
||||||
|
// | standard call | dotdotdot call |
|
||||||
|
// --------------+------------------+----------------+
|
||||||
|
// standard func | nargs == npars | invalid |
|
||||||
|
// --------------+------------------+----------------+
|
||||||
|
// variadic func | nargs >= npars-1 | nargs == npars |
|
||||||
|
// --------------+------------------+----------------+
|
||||||
|
|
||||||
|
nargs := len(args)
|
||||||
|
npars := sig.params.Len()
|
||||||
|
ddd := call.HasDots
|
||||||
|
|
||||||
|
// set up parameters
|
||||||
|
sig_params := sig.params // adjusted for variadic functions (may be nil for empty parameter lists!)
|
||||||
|
adjusted := false // indicates if sig_params is different from t.params
|
||||||
|
if sig.variadic {
|
||||||
|
if ddd {
|
||||||
|
// variadic_func(a, b, c...)
|
||||||
|
if len(call.ArgList) == 1 && nargs > 1 {
|
||||||
|
// f()... is not permitted if f() is multi-valued
|
||||||
|
//check.errorf(call.Ellipsis, "cannot use ... with %d-valued %s", nargs, call.ArgList[0])
|
||||||
|
check.errorf(call, "cannot use ... with %d-valued %s", nargs, call.ArgList[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// variadic_func(a, b, c)
|
||||||
|
if nargs >= npars-1 {
|
||||||
|
// Create custom parameters for arguments: keep
|
||||||
|
// the first npars-1 parameters and add one for
|
||||||
|
// each argument mapping to the ... parameter.
|
||||||
|
vars := make([]*Var, npars-1) // npars > 0 for variadic functions
|
||||||
|
copy(vars, sig.params.vars)
|
||||||
|
last := sig.params.vars[npars-1]
|
||||||
|
typ := last.typ.(*Slice).elem
|
||||||
|
for len(vars) < nargs {
|
||||||
|
vars = append(vars, NewParam(last.pos, last.pkg, last.name, typ))
|
||||||
|
}
|
||||||
|
sig_params = NewTuple(vars...) // possibly nil!
|
||||||
|
adjusted = true
|
||||||
|
npars = nargs
|
||||||
|
} else {
|
||||||
|
// nargs < npars-1
|
||||||
|
npars-- // for correct error message below
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ddd {
|
||||||
|
// standard_func(a, b, c...)
|
||||||
|
//check.errorf(call.Ellipsis, "cannot use ... in call to non-variadic %s", call.Fun)
|
||||||
|
check.errorf(call, "cannot use ... in call to non-variadic %s", call.Fun)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// standard_func(a, b, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check argument count
|
||||||
|
switch {
|
||||||
|
case nargs < npars:
|
||||||
|
check.errorf(call, "not enough arguments in call to %s", call.Fun)
|
||||||
|
return
|
||||||
|
case nargs > npars:
|
||||||
|
check.errorf(args[npars], "too many arguments in call to %s", call.Fun) // report at first extra argument
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// infer type arguments and instantiate signature if necessary
|
||||||
|
if len(sig.tparams) > 0 {
|
||||||
|
// TODO(gri) provide position information for targs so we can feed
|
||||||
|
// it to the instantiate call for better error reporting
|
||||||
|
targs, failed := check.infer(sig.tparams, sig_params, args)
|
||||||
|
if targs == nil {
|
||||||
|
return // error already reported
|
||||||
|
}
|
||||||
|
if failed >= 0 {
|
||||||
|
// Some type arguments couldn't be inferred. Use
|
||||||
|
// bounds type inference to try to make progress.
|
||||||
|
if check.conf.InferFromConstraints {
|
||||||
|
targs, failed = check.inferB(sig.tparams, targs)
|
||||||
|
if targs == nil {
|
||||||
|
return // error already reported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if failed >= 0 {
|
||||||
|
// at least one type argument couldn't be inferred
|
||||||
|
assert(targs[failed] == nil)
|
||||||
|
tpar := sig.tparams[failed]
|
||||||
|
// TODO(gri) here we'd like to use the position of the call's ')'
|
||||||
|
check.errorf(call.Pos(), "cannot infer %s (%s) (%s)", tpar.name, tpar.pos, targs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// all type arguments were inferred sucessfully
|
||||||
|
if debug {
|
||||||
|
for _, targ := range targs {
|
||||||
|
assert(targ != nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//check.dump("### inferred targs = %s", targs)
|
||||||
|
|
||||||
|
// compute result signature
|
||||||
|
rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature)
|
||||||
|
assert(rsig.tparams == nil) // signature is not generic anymore
|
||||||
|
check.recordInferred(call, targs, rsig)
|
||||||
|
|
||||||
|
// Optimization: Only if the parameter list was adjusted do we
|
||||||
|
// need to compute it from the adjusted list; otherwise we can
|
||||||
|
// simply use the result signature's parameter list.
|
||||||
|
if adjusted {
|
||||||
|
sig_params = check.subst(call.Pos(), sig_params, makeSubstMap(sig.tparams, targs)).(*Tuple)
|
||||||
|
} else {
|
||||||
|
sig_params = rsig.params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check arguments
|
||||||
|
for i, a := range args {
|
||||||
|
check.assignment(a, sig_params.vars[i].typ, "argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var cgoPrefixes = [...]string{
|
||||||
|
"_Ciconst_",
|
||||||
|
"_Cfconst_",
|
||||||
|
"_Csconst_",
|
||||||
|
"_Ctype_",
|
||||||
|
"_Cvar_", // actually a pointer to the var
|
||||||
|
"_Cfpvar_fp_",
|
||||||
|
"_Cfunc_",
|
||||||
|
"_Cmacro_", // function to evaluate the expanded expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
|
||||||
|
// these must be declared before the "goto Error" statements
|
||||||
|
var (
|
||||||
|
obj Object
|
||||||
|
index []int
|
||||||
|
indirect bool
|
||||||
|
)
|
||||||
|
|
||||||
|
sel := e.Sel.Value
|
||||||
|
// If the identifier refers to a package, handle everything here
|
||||||
|
// so we don't need a "package" mode for operands: package names
|
||||||
|
// can only appear in qualified identifiers which are mapped to
|
||||||
|
// selector expressions.
|
||||||
|
if ident, ok := e.X.(*syntax.Name); ok {
|
||||||
|
obj := check.lookup(ident.Value)
|
||||||
|
if pname, _ := obj.(*PkgName); pname != nil {
|
||||||
|
assert(pname.pkg == check.pkg)
|
||||||
|
check.recordUse(ident, pname)
|
||||||
|
pname.used = true
|
||||||
|
pkg := pname.imported
|
||||||
|
|
||||||
|
var exp Object
|
||||||
|
funcMode := value
|
||||||
|
if pkg.cgo {
|
||||||
|
// cgo special cases C.malloc: it's
|
||||||
|
// rewritten to _CMalloc and does not
|
||||||
|
// support two-result calls.
|
||||||
|
if sel == "malloc" {
|
||||||
|
sel = "_CMalloc"
|
||||||
|
} else {
|
||||||
|
funcMode = cgofunc
|
||||||
|
}
|
||||||
|
for _, prefix := range cgoPrefixes {
|
||||||
|
// cgo objects are part of the current package (in file
|
||||||
|
// _cgo_gotypes.go). Use regular lookup.
|
||||||
|
_, exp = check.scope.LookupParent(prefix+sel, check.pos)
|
||||||
|
if exp != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if exp == nil {
|
||||||
|
check.errorf(e.Sel, "%s not declared by package C", sel)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
check.objDecl(exp, nil)
|
||||||
|
} else {
|
||||||
|
exp = pkg.scope.Lookup(sel)
|
||||||
|
if exp == nil {
|
||||||
|
if !pkg.fake {
|
||||||
|
if check.conf.CompilerErrorMessages {
|
||||||
|
check.errorf(e.Sel, "undefined: %s.%s", pkg.name, sel)
|
||||||
|
} else {
|
||||||
|
check.errorf(e.Sel, "%s not declared by package %s", sel, pkg.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
if !exp.Exported() {
|
||||||
|
check.errorf(e.Sel, "%s not exported by package %s", sel, pkg.name)
|
||||||
|
// ok to continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check.recordUse(e.Sel, exp)
|
||||||
|
|
||||||
|
// Simplified version of the code for *syntax.Names:
|
||||||
|
// - imported objects are always fully initialized
|
||||||
|
switch exp := exp.(type) {
|
||||||
|
case *Const:
|
||||||
|
assert(exp.Val() != nil)
|
||||||
|
x.mode = constant_
|
||||||
|
x.typ = exp.typ
|
||||||
|
x.val = exp.val
|
||||||
|
case *TypeName:
|
||||||
|
x.mode = typexpr
|
||||||
|
x.typ = exp.typ
|
||||||
|
case *Var:
|
||||||
|
x.mode = variable
|
||||||
|
x.typ = exp.typ
|
||||||
|
if pkg.cgo && strings.HasPrefix(exp.name, "_Cvar_") {
|
||||||
|
x.typ = x.typ.(*Pointer).base
|
||||||
|
}
|
||||||
|
case *Func:
|
||||||
|
x.mode = funcMode
|
||||||
|
x.typ = exp.typ
|
||||||
|
if pkg.cgo && strings.HasPrefix(exp.name, "_Cmacro_") {
|
||||||
|
x.mode = value
|
||||||
|
x.typ = x.typ.(*Signature).results.vars[0].typ
|
||||||
|
}
|
||||||
|
case *Builtin:
|
||||||
|
x.mode = builtin
|
||||||
|
x.typ = exp.typ
|
||||||
|
x.id = exp.id
|
||||||
|
default:
|
||||||
|
check.dump("%v: unexpected object %v", posFor(e.Sel), exp)
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
x.expr = e
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check.exprOrType(x, e.X)
|
||||||
|
if x.mode == invalid {
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
check.instantiatedOperand(x)
|
||||||
|
|
||||||
|
obj, index, indirect = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
|
||||||
|
if obj == nil {
|
||||||
|
switch {
|
||||||
|
case index != nil:
|
||||||
|
// TODO(gri) should provide actual type where the conflict happens
|
||||||
|
check.errorf(e.Sel, "ambiguous selector %s.%s", x.expr, sel)
|
||||||
|
case indirect:
|
||||||
|
check.errorf(e.Sel, "cannot call pointer method %s on %s", sel, x.typ)
|
||||||
|
default:
|
||||||
|
var why string
|
||||||
|
if tpar := asTypeParam(x.typ); tpar != nil {
|
||||||
|
// Type parameter bounds don't specify fields, so don't mention "field".
|
||||||
|
switch obj := tpar.Bound().obj.(type) {
|
||||||
|
case nil:
|
||||||
|
why = check.sprintf("type bound for %s has no method %s", x.typ, sel)
|
||||||
|
case *TypeName:
|
||||||
|
why = check.sprintf("interface %s has no method %s", obj.name, sel)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
why = check.sprintf("type %s has no field or method %s", x.typ, sel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if capitalization of sel matters and provide better error message in that case.
|
||||||
|
if len(sel) > 0 {
|
||||||
|
var changeCase string
|
||||||
|
if r := rune(sel[0]); unicode.IsUpper(r) {
|
||||||
|
changeCase = string(unicode.ToLower(r)) + sel[1:]
|
||||||
|
} else {
|
||||||
|
changeCase = string(unicode.ToUpper(r)) + sel[1:]
|
||||||
|
}
|
||||||
|
if obj, _, _ = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil {
|
||||||
|
why += ", but does have " + changeCase
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check.errorf(e.Sel, "%s.%s undefined (%s)", x.expr, sel, why)
|
||||||
|
|
||||||
|
}
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// methods may not have a fully set up signature yet
|
||||||
|
if m, _ := obj.(*Func); m != nil {
|
||||||
|
// check.dump("### found method %s", m)
|
||||||
|
check.objDecl(m, nil)
|
||||||
|
// If m has a parameterized receiver type, infer the type parameter
|
||||||
|
// values from the actual receiver provided and then substitute the
|
||||||
|
// type parameters in the signature accordingly.
|
||||||
|
// TODO(gri) factor this code out
|
||||||
|
sig := m.typ.(*Signature)
|
||||||
|
if len(sig.rparams) > 0 {
|
||||||
|
//check.dump("### recv typ = %s", x.typ)
|
||||||
|
//check.dump("### method = %s rparams = %s tparams = %s", m, sig.rparams, sig.tparams)
|
||||||
|
// The method may have a pointer receiver, but the actually provided receiver
|
||||||
|
// may be a (hopefully addressable) non-pointer value, or vice versa. Here we
|
||||||
|
// only care about inferring receiver type parameters; to make the inference
|
||||||
|
// work, match up pointer-ness of receiver and argument.
|
||||||
|
arg := x
|
||||||
|
if ptrRecv := isPointer(sig.recv.typ); ptrRecv != isPointer(arg.typ) {
|
||||||
|
copy := *arg
|
||||||
|
if ptrRecv {
|
||||||
|
copy.typ = NewPointer(arg.typ)
|
||||||
|
} else {
|
||||||
|
copy.typ = arg.typ.(*Pointer).base
|
||||||
|
}
|
||||||
|
arg = ©
|
||||||
|
}
|
||||||
|
targs, failed := check.infer(sig.rparams, NewTuple(sig.recv), []*operand{arg})
|
||||||
|
//check.dump("### inferred targs = %s", targs)
|
||||||
|
if failed >= 0 {
|
||||||
|
// We may reach here if there were other errors (see issue #40056).
|
||||||
|
// check.infer will report a follow-up error.
|
||||||
|
// TODO(gri) avoid the follow-up error or provide better explanation.
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
// Don't modify m. Instead - for now - make a copy of m and use that instead.
|
||||||
|
// (If we modify m, some tests will fail; possibly because the m is in use.)
|
||||||
|
// TODO(gri) investigate and provide a correct explanation here
|
||||||
|
copy := *m
|
||||||
|
copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.rparams, targs))
|
||||||
|
obj = ©
|
||||||
|
}
|
||||||
|
// TODO(gri) we also need to do substitution for parameterized interface methods
|
||||||
|
// (this breaks code in testdata/linalg.go2 at the moment)
|
||||||
|
// 12/20/2019: Is this TODO still correct?
|
||||||
|
}
|
||||||
|
|
||||||
|
if x.mode == typexpr {
|
||||||
|
// method expression
|
||||||
|
m, _ := obj.(*Func)
|
||||||
|
if m == nil {
|
||||||
|
// TODO(gri) should check if capitalization of sel matters and provide better error message in that case
|
||||||
|
check.errorf(e.Sel, "%s.%s undefined (type %s has no method %s)", x.expr, sel, x.typ, sel)
|
||||||
|
goto Error
|
||||||
|
}
|
||||||
|
|
||||||
|
check.recordSelection(e, MethodExpr, x.typ, m, index, indirect)
|
||||||
|
|
||||||
|
// the receiver type becomes the type of the first function
|
||||||
|
// argument of the method expression's function type
|
||||||
|
var params []*Var
|
||||||
|
sig := m.typ.(*Signature)
|
||||||
|
if sig.params != nil {
|
||||||
|
params = sig.params.vars
|
||||||
|
}
|
||||||
|
x.mode = value
|
||||||
|
x.typ = &Signature{
|
||||||
|
tparams: sig.tparams,
|
||||||
|
params: NewTuple(append([]*Var{NewVar(nopos, check.pkg, "_", x.typ)}, params...)...),
|
||||||
|
results: sig.results,
|
||||||
|
variadic: sig.variadic,
|
||||||
|
}
|
||||||
|
|
||||||
|
check.addDeclDep(m)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// regular selector
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *Var:
|
||||||
|
check.recordSelection(e, FieldVal, x.typ, obj, index, indirect)
|
||||||
|
if x.mode == variable || indirect {
|
||||||
|
x.mode = variable
|
||||||
|
} else {
|
||||||
|
x.mode = value
|
||||||
|
}
|
||||||
|
x.typ = obj.typ
|
||||||
|
|
||||||
|
case *Func:
|
||||||
|
// TODO(gri) If we needed to take into account the receiver's
|
||||||
|
// addressability, should we report the type &(x.typ) instead?
|
||||||
|
check.recordSelection(e, MethodVal, x.typ, obj, index, indirect)
|
||||||
|
|
||||||
|
x.mode = value
|
||||||
|
|
||||||
|
// remove receiver
|
||||||
|
sig := *obj.typ.(*Signature)
|
||||||
|
sig.recv = nil
|
||||||
|
x.typ = &sig
|
||||||
|
|
||||||
|
check.addDeclDep(obj)
|
||||||
|
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// everything went well
|
||||||
|
x.expr = e
|
||||||
|
return
|
||||||
|
|
||||||
|
Error:
|
||||||
|
x.mode = invalid
|
||||||
|
x.expr = e
|
||||||
|
}
|
||||||
|
|
||||||
|
// use type-checks each argument.
|
||||||
|
// Useful to make sure expressions are evaluated
|
||||||
|
// (and variables are "used") in the presence of other errors.
|
||||||
|
// The arguments may be nil.
|
||||||
|
// TODO(gri) make this accept a []syntax.Expr and use an unpack function when we have a ListExpr?
|
||||||
|
func (check *Checker) use(arg ...syntax.Expr) {
|
||||||
|
var x operand
|
||||||
|
for _, e := range arg {
|
||||||
|
// Certain AST fields may legally be nil (e.g., the ast.SliceExpr.High field).
|
||||||
|
if e == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if l, _ := e.(*syntax.ListExpr); l != nil {
|
||||||
|
check.use(l.ElemList...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
check.rawExpr(&x, e, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// useLHS is like use, but doesn't "use" top-level identifiers.
|
||||||
|
// It should be called instead of use if the arguments are
|
||||||
|
// expressions on the lhs of an assignment.
|
||||||
|
// The arguments must not be nil.
|
||||||
|
func (check *Checker) useLHS(arg ...syntax.Expr) {
|
||||||
|
var x operand
|
||||||
|
for _, e := range arg {
|
||||||
|
// If the lhs is an identifier denoting a variable v, this assignment
|
||||||
|
// is not a 'use' of v. Remember current value of v.used and restore
|
||||||
|
// after evaluating the lhs via check.rawExpr.
|
||||||
|
var v *Var
|
||||||
|
var v_used bool
|
||||||
|
if ident, _ := unparen(e).(*syntax.Name); ident != nil {
|
||||||
|
// never type-check the blank name on the lhs
|
||||||
|
if ident.Value == "_" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, obj := check.scope.LookupParent(ident.Value, nopos); obj != nil {
|
||||||
|
// It's ok to mark non-local variables, but ignore variables
|
||||||
|
// from other packages to avoid potential race conditions with
|
||||||
|
// dot-imported variables.
|
||||||
|
if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg {
|
||||||
|
v = w
|
||||||
|
v_used = v.used
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check.rawExpr(&x, e, nil)
|
||||||
|
if v != nil {
|
||||||
|
v.used = v_used // restore v.used
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// instantiatedOperand reports an error of x is an uninstantiated (generic) type and sets x.typ to Typ[Invalid].
|
||||||
|
func (check *Checker) instantiatedOperand(x *operand) {
|
||||||
|
if x.mode == typexpr && isGeneric(x.typ) {
|
||||||
|
check.errorf(x, "cannot use generic type %s without instantiation", x.typ)
|
||||||
|
x.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
}
|
||||||
455
src/cmd/compile/internal/types2/check.go
Normal file
455
src/cmd/compile/internal/types2/check.go
Normal file
|
|
@ -0,0 +1,455 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements the Check function, which drives type-checking.
|
||||||
|
|
||||||
|
package types2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go/constant"
|
||||||
|
)
|
||||||
|
|
||||||
|
var nopos syntax.Pos
|
||||||
|
|
||||||
|
// debugging/development support
|
||||||
|
const debug = true // leave on during development
|
||||||
|
|
||||||
|
// If forceStrict is set, the type-checker enforces additional
|
||||||
|
// rules not specified by the Go 1 spec, but which will
|
||||||
|
// catch guaranteed run-time errors if the respective
|
||||||
|
// code is executed. In other words, programs passing in
|
||||||
|
// strict mode are Go 1 compliant, but not all Go 1 programs
|
||||||
|
// will pass in strict mode. The additional rules are:
|
||||||
|
//
|
||||||
|
// - A type assertion x.(T) where T is an interface type
|
||||||
|
// is invalid if any (statically known) method that exists
|
||||||
|
// for both x and T have different signatures.
|
||||||
|
//
|
||||||
|
const forceStrict = false
|
||||||
|
|
||||||
|
// If methodTypeParamsOk is set, type parameters are
|
||||||
|
// permitted in method declarations (in interfaces, too).
|
||||||
|
// Generalization and experimental feature.
|
||||||
|
const methodTypeParamsOk = true
|
||||||
|
|
||||||
|
// exprInfo stores information about an untyped expression.
|
||||||
|
type exprInfo struct {
|
||||||
|
isLhs bool // expression is lhs operand of a shift with delayed type-check
|
||||||
|
mode operandMode
|
||||||
|
typ *Basic
|
||||||
|
val constant.Value // constant value; or nil (if not a constant)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A context represents the context within which an object is type-checked.
|
||||||
|
type context struct {
|
||||||
|
decl *declInfo // package-level declaration whose init expression/function body is checked
|
||||||
|
scope *Scope // top-most scope for lookups
|
||||||
|
pos syntax.Pos // if valid, identifiers are looked up as if at position pos (used by Eval)
|
||||||
|
iota constant.Value // value of iota in a constant declaration; nil otherwise
|
||||||
|
errpos syntax.Pos // if valid, identifier position of a constant with inherited initializer
|
||||||
|
sig *Signature // function signature if inside a function; nil otherwise
|
||||||
|
isPanic map[*syntax.CallExpr]bool // set of panic call expressions (used for termination check)
|
||||||
|
hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions
|
||||||
|
hasCallOrRecv bool // set if an expression contains a function call or channel receive operation
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookup looks up name in the current context and returns the matching object, or nil.
|
||||||
|
func (ctxt *context) lookup(name string) Object {
|
||||||
|
_, obj := ctxt.scope.LookupParent(name, ctxt.pos)
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
// An importKey identifies an imported package by import path and source directory
|
||||||
|
// (directory containing the file containing the import). In practice, the directory
|
||||||
|
// may always be the same, or may not matter. Given an (import path, directory), an
|
||||||
|
// importer must always return the same package (but given two different import paths,
|
||||||
|
// an importer may still return the same package by mapping them to the same package
|
||||||
|
// paths).
|
||||||
|
type importKey struct {
|
||||||
|
path, dir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// A dotImportKey describes a dot-imported object in the given scope.
|
||||||
|
type dotImportKey struct {
|
||||||
|
scope *Scope
|
||||||
|
obj Object
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Checker maintains the state of the type checker.
|
||||||
|
// It must be created with NewChecker.
|
||||||
|
type Checker struct {
|
||||||
|
// package information
|
||||||
|
// (initialized by NewChecker, valid for the life-time of checker)
|
||||||
|
conf *Config
|
||||||
|
pkg *Package
|
||||||
|
*Info
|
||||||
|
version version // accepted language version
|
||||||
|
nextId uint64 // unique Id for type parameters (first valid Id is 1)
|
||||||
|
objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
|
||||||
|
impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
|
||||||
|
posMap map[*Interface][]syntax.Pos // maps interface types to lists of embedded interface positions
|
||||||
|
typMap map[string]*Named // maps an instantiated named type hash to a *Named type
|
||||||
|
pkgCnt map[string]int // counts number of imported packages with a given name (for better error messages)
|
||||||
|
|
||||||
|
// information collected during type-checking of a set of package files
|
||||||
|
// (initialized by Files, valid only for the duration of check.Files;
|
||||||
|
// maps and lists are allocated on demand)
|
||||||
|
files []*syntax.File // list of package files
|
||||||
|
imports []*PkgName // list of imported packages
|
||||||
|
dotImportMap map[dotImportKey]*PkgName // maps dot-imported objects to the package they were dot-imported through
|
||||||
|
|
||||||
|
firstErr error // first error encountered
|
||||||
|
methods map[*TypeName][]*Func // maps package scope type names to associated non-blank (non-interface) methods
|
||||||
|
untyped map[syntax.Expr]exprInfo // map of expressions without final type
|
||||||
|
delayed []func() // stack of delayed action segments; segments are processed in FIFO order
|
||||||
|
finals []func() // list of final actions; processed at the end of type-checking the current set of files
|
||||||
|
objPath []Object // path of object dependencies during type inference (for cycle reporting)
|
||||||
|
|
||||||
|
// context within which the current object is type-checked
|
||||||
|
// (valid only for the duration of type-checking a specific object)
|
||||||
|
context
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
indent int // indentation for tracing
|
||||||
|
}
|
||||||
|
|
||||||
|
// addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists
|
||||||
|
func (check *Checker) addDeclDep(to Object) {
|
||||||
|
from := check.decl
|
||||||
|
if from == nil {
|
||||||
|
return // not in a package-level init expression
|
||||||
|
}
|
||||||
|
if _, found := check.objMap[to]; !found {
|
||||||
|
return // to is not a package-level object
|
||||||
|
}
|
||||||
|
from.addDep(to)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) rememberUntyped(e syntax.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
|
||||||
|
m := check.untyped
|
||||||
|
if m == nil {
|
||||||
|
m = make(map[syntax.Expr]exprInfo)
|
||||||
|
check.untyped = m
|
||||||
|
}
|
||||||
|
m[e] = exprInfo{lhs, mode, typ, val}
|
||||||
|
}
|
||||||
|
|
||||||
|
// later pushes f on to the stack of actions that will be processed later;
|
||||||
|
// either at the end of the current statement, or in case of a local constant
|
||||||
|
// or variable declaration, before the constant or variable is in scope
|
||||||
|
// (so that f still sees the scope before any new declarations).
|
||||||
|
func (check *Checker) later(f func()) {
|
||||||
|
check.delayed = append(check.delayed, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// atEnd adds f to the list of actions processed at the end
|
||||||
|
// of type-checking, before initialization order computation.
|
||||||
|
// Actions added by atEnd are processed after any actions
|
||||||
|
// added by later.
|
||||||
|
func (check *Checker) atEnd(f func()) {
|
||||||
|
check.finals = append(check.finals, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// push pushes obj onto the object path and returns its index in the path.
|
||||||
|
func (check *Checker) push(obj Object) int {
|
||||||
|
check.objPath = append(check.objPath, obj)
|
||||||
|
return len(check.objPath) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// pop pops and returns the topmost object from the object path.
|
||||||
|
func (check *Checker) pop() Object {
|
||||||
|
i := len(check.objPath) - 1
|
||||||
|
obj := check.objPath[i]
|
||||||
|
check.objPath[i] = nil
|
||||||
|
check.objPath = check.objPath[:i]
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChecker returns a new Checker instance for a given package.
|
||||||
|
// Package files may be added incrementally via checker.Files.
|
||||||
|
func NewChecker(conf *Config, pkg *Package, info *Info) *Checker {
|
||||||
|
// make sure we have a configuration
|
||||||
|
if conf == nil {
|
||||||
|
conf = new(Config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we have an info struct
|
||||||
|
if info == nil {
|
||||||
|
info = new(Info)
|
||||||
|
}
|
||||||
|
|
||||||
|
version, err := parseGoVersion(conf.GoVersion)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("invalid Go version %q (%v)", conf.GoVersion, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Checker{
|
||||||
|
conf: conf,
|
||||||
|
pkg: pkg,
|
||||||
|
Info: info,
|
||||||
|
version: version,
|
||||||
|
nextId: 1,
|
||||||
|
objMap: make(map[Object]*declInfo),
|
||||||
|
impMap: make(map[importKey]*Package),
|
||||||
|
posMap: make(map[*Interface][]syntax.Pos),
|
||||||
|
typMap: make(map[string]*Named),
|
||||||
|
pkgCnt: make(map[string]int),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// initFiles initializes the files-specific portion of checker.
|
||||||
|
// The provided files must all belong to the same package.
|
||||||
|
func (check *Checker) initFiles(files []*syntax.File) {
|
||||||
|
// start with a clean slate (check.Files may be called multiple times)
|
||||||
|
check.files = nil
|
||||||
|
check.imports = nil
|
||||||
|
check.dotImportMap = nil
|
||||||
|
|
||||||
|
check.firstErr = nil
|
||||||
|
check.methods = nil
|
||||||
|
check.untyped = nil
|
||||||
|
check.delayed = nil
|
||||||
|
check.finals = nil
|
||||||
|
|
||||||
|
// determine package name and collect valid files
|
||||||
|
pkg := check.pkg
|
||||||
|
for _, file := range files {
|
||||||
|
switch name := file.PkgName.Value; pkg.name {
|
||||||
|
case "":
|
||||||
|
if name != "_" {
|
||||||
|
pkg.name = name
|
||||||
|
} else {
|
||||||
|
check.errorf(file.PkgName, "invalid package name _")
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case name:
|
||||||
|
check.files = append(check.files, file)
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.errorf(file, "package %s; expected %s", name, pkg.name)
|
||||||
|
// ignore this file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A bailout panic is used for early termination.
|
||||||
|
type bailout struct{}
|
||||||
|
|
||||||
|
func (check *Checker) handleBailout(err *error) {
|
||||||
|
switch p := recover().(type) {
|
||||||
|
case nil, bailout:
|
||||||
|
// normal return or early exit
|
||||||
|
*err = check.firstErr
|
||||||
|
default:
|
||||||
|
// re-panic
|
||||||
|
panic(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Files checks the provided files as part of the checker's package.
|
||||||
|
func (check *Checker) Files(files []*syntax.File) error { return check.checkFiles(files) }
|
||||||
|
|
||||||
|
var errBadCgo = errors.New("cannot use FakeImportC and go115UsesCgo together")
|
||||||
|
|
||||||
|
func (check *Checker) checkFiles(files []*syntax.File) (err error) {
|
||||||
|
if check.conf.FakeImportC && check.conf.go115UsesCgo {
|
||||||
|
return errBadCgo
|
||||||
|
}
|
||||||
|
|
||||||
|
defer check.handleBailout(&err)
|
||||||
|
|
||||||
|
print := func(msg string) {
|
||||||
|
if check.conf.Trace {
|
||||||
|
fmt.Println(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("== initFiles ==")
|
||||||
|
check.initFiles(files)
|
||||||
|
|
||||||
|
print("== collectObjects ==")
|
||||||
|
check.collectObjects()
|
||||||
|
|
||||||
|
print("== packageObjects ==")
|
||||||
|
check.packageObjects()
|
||||||
|
|
||||||
|
print("== processDelayed ==")
|
||||||
|
check.processDelayed(0) // incl. all functions
|
||||||
|
check.processFinals()
|
||||||
|
|
||||||
|
print("== initOrder ==")
|
||||||
|
check.initOrder()
|
||||||
|
|
||||||
|
if !check.conf.DisableUnusedImportCheck {
|
||||||
|
print("== unusedImports ==")
|
||||||
|
check.unusedImports()
|
||||||
|
}
|
||||||
|
// no longer needed - release memory
|
||||||
|
check.imports = nil
|
||||||
|
check.dotImportMap = nil
|
||||||
|
|
||||||
|
print("== recordUntyped ==")
|
||||||
|
check.recordUntyped()
|
||||||
|
|
||||||
|
if check.Info != nil {
|
||||||
|
print("== sanitizeInfo ==")
|
||||||
|
sanitizeInfo(check.Info)
|
||||||
|
}
|
||||||
|
|
||||||
|
check.pkg.complete = true
|
||||||
|
|
||||||
|
// TODO(gri) There's more memory we should release at this point.
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// processDelayed processes all delayed actions pushed after top.
|
||||||
|
func (check *Checker) processDelayed(top int) {
|
||||||
|
// If each delayed action pushes a new action, the
|
||||||
|
// stack will continue to grow during this loop.
|
||||||
|
// However, it is only processing functions (which
|
||||||
|
// are processed in a delayed fashion) that may
|
||||||
|
// add more actions (such as nested functions), so
|
||||||
|
// this is a sufficiently bounded process.
|
||||||
|
for i := top; i < len(check.delayed); i++ {
|
||||||
|
check.delayed[i]() // may append to check.delayed
|
||||||
|
}
|
||||||
|
assert(top <= len(check.delayed)) // stack must not have shrunk
|
||||||
|
check.delayed = check.delayed[:top]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) processFinals() {
|
||||||
|
n := len(check.finals)
|
||||||
|
for _, f := range check.finals {
|
||||||
|
f() // must not append to check.finals
|
||||||
|
}
|
||||||
|
if len(check.finals) != n {
|
||||||
|
panic("internal error: final action list grew")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordUntyped() {
|
||||||
|
if !debug && check.Types == nil {
|
||||||
|
return // nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
for x, info := range check.untyped {
|
||||||
|
if debug && isTyped(info.typ) {
|
||||||
|
check.dump("%v: %s (type %s) is typed", posFor(x), x, info.typ)
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
check.recordTypeAndValue(x, info.mode, info.typ, info.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Type, val constant.Value) {
|
||||||
|
assert(x != nil)
|
||||||
|
assert(typ != nil)
|
||||||
|
if mode == invalid {
|
||||||
|
return // omit
|
||||||
|
}
|
||||||
|
if mode == constant_ {
|
||||||
|
assert(val != nil)
|
||||||
|
assert(typ == Typ[Invalid] || isConstType(typ))
|
||||||
|
}
|
||||||
|
if m := check.Types; m != nil {
|
||||||
|
m[x] = TypeAndValue{mode, typ, val}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordBuiltinType(f syntax.Expr, sig *Signature) {
|
||||||
|
// f must be a (possibly parenthesized) identifier denoting a built-in
|
||||||
|
// (built-ins in package unsafe always produce a constant result and
|
||||||
|
// we don't record their signatures, so we don't see qualified idents
|
||||||
|
// here): record the signature for f and possible children.
|
||||||
|
for {
|
||||||
|
check.recordTypeAndValue(f, builtin, sig, nil)
|
||||||
|
switch p := f.(type) {
|
||||||
|
case *syntax.Name:
|
||||||
|
return // we're done
|
||||||
|
case *syntax.ParenExpr:
|
||||||
|
f = p.X
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordCommaOkTypes(x syntax.Expr, a [2]Type) {
|
||||||
|
assert(x != nil)
|
||||||
|
if a[0] == nil || a[1] == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert(isTyped(a[0]) && isTyped(a[1]) && (isBoolean(a[1]) || a[1] == universeError))
|
||||||
|
if m := check.Types; m != nil {
|
||||||
|
for {
|
||||||
|
tv := m[x]
|
||||||
|
assert(tv.Type != nil) // should have been recorded already
|
||||||
|
pos := x.Pos()
|
||||||
|
tv.Type = NewTuple(
|
||||||
|
NewVar(pos, check.pkg, "", a[0]),
|
||||||
|
NewVar(pos, check.pkg, "", a[1]),
|
||||||
|
)
|
||||||
|
m[x] = tv
|
||||||
|
// if x is a parenthesized expression (p.X), update p.X
|
||||||
|
p, _ := x.(*syntax.ParenExpr)
|
||||||
|
if p == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
x = p.X
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordInferred(call syntax.Expr, targs []Type, sig *Signature) {
|
||||||
|
assert(call != nil)
|
||||||
|
assert(sig != nil)
|
||||||
|
if m := check.Inferred; m != nil {
|
||||||
|
m[call] = Inferred{targs, sig}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordDef(id *syntax.Name, obj Object) {
|
||||||
|
assert(id != nil)
|
||||||
|
if m := check.Defs; m != nil {
|
||||||
|
m[id] = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordUse(id *syntax.Name, obj Object) {
|
||||||
|
assert(id != nil)
|
||||||
|
assert(obj != nil)
|
||||||
|
if m := check.Uses; m != nil {
|
||||||
|
m[id] = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordImplicit(node syntax.Node, obj Object) {
|
||||||
|
assert(node != nil)
|
||||||
|
assert(obj != nil)
|
||||||
|
if m := check.Implicits; m != nil {
|
||||||
|
m[node] = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordSelection(x *syntax.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
|
||||||
|
assert(obj != nil && (recv == nil || len(index) > 0))
|
||||||
|
check.recordUse(x.Sel, obj)
|
||||||
|
if m := check.Selections; m != nil {
|
||||||
|
m[x] = &Selection{kind, recv, obj, index, indirect}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) recordScope(node syntax.Node, scope *Scope) {
|
||||||
|
assert(node != nil)
|
||||||
|
assert(scope != nil)
|
||||||
|
if m := check.Scopes; m != nil {
|
||||||
|
m[node] = scope
|
||||||
|
}
|
||||||
|
}
|
||||||
298
src/cmd/compile/internal/types2/check_test.go
Normal file
298
src/cmd/compile/internal/types2/check_test.go
Normal file
|
|
@ -0,0 +1,298 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements a typechecker test harness. The packages specified
|
||||||
|
// in tests are typechecked. Error messages reported by the typechecker are
|
||||||
|
// compared against the error messages expected in the test files.
|
||||||
|
//
|
||||||
|
// Expected errors are indicated in the test files by putting a comment
|
||||||
|
// of the form /* ERROR "rx" */ immediately following an offending token.
|
||||||
|
// The harness will verify that an error matching the regular expression
|
||||||
|
// rx is reported at that source position. Consecutive comments may be
|
||||||
|
// used to indicate multiple errors for the same token position.
|
||||||
|
//
|
||||||
|
// For instance, the following test file indicates that a "not declared"
|
||||||
|
// error should be reported for the undeclared variable x:
|
||||||
|
//
|
||||||
|
// package p
|
||||||
|
// func f() {
|
||||||
|
// _ = x /* ERROR "not declared" */ + 1
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TODO(gri) Also collect strict mode errors of the form /* STRICT ... */
|
||||||
|
// and test against strict mode.
|
||||||
|
|
||||||
|
package types2_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"internal/testenv"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "cmd/compile/internal/types2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
haltOnError = flag.Bool("halt", false, "halt on error")
|
||||||
|
listErrors = flag.Bool("errlist", false, "list errors")
|
||||||
|
testFiles = flag.String("files", "", "comma-separated list of test files")
|
||||||
|
goVersion = flag.String("lang", "", "Go language version (e.g. \"go1.12\"")
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseFiles(t *testing.T, filenames []string, mode syntax.Mode) ([]*syntax.File, []error) {
|
||||||
|
var files []*syntax.File
|
||||||
|
var errlist []error
|
||||||
|
errh := func(err error) { errlist = append(errlist, err) }
|
||||||
|
for _, filename := range filenames {
|
||||||
|
file, err := syntax.ParseFile(filename, errh, nil, mode)
|
||||||
|
if file == nil {
|
||||||
|
t.Fatalf("%s: %s", filename, err)
|
||||||
|
}
|
||||||
|
files = append(files, file)
|
||||||
|
}
|
||||||
|
return files, errlist
|
||||||
|
}
|
||||||
|
|
||||||
|
func unpackError(err error) syntax.Error {
|
||||||
|
switch err := err.(type) {
|
||||||
|
case syntax.Error:
|
||||||
|
return err
|
||||||
|
case Error:
|
||||||
|
return syntax.Error{Pos: err.Pos, Msg: err.Msg}
|
||||||
|
default:
|
||||||
|
return syntax.Error{Msg: err.Error()}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func delta(x, y uint) uint {
|
||||||
|
switch {
|
||||||
|
case x < y:
|
||||||
|
return y - x
|
||||||
|
case x > y:
|
||||||
|
return x - y
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// goVersionRx matches a Go version string using '_', e.g. "go1_12".
|
||||||
|
var goVersionRx = regexp.MustCompile(`^go[1-9][0-9]*_(0|[1-9][0-9]*)$`)
|
||||||
|
|
||||||
|
// asGoVersion returns a regular Go language version string
|
||||||
|
// if s is a Go version string using '_' rather than '.' to
|
||||||
|
// separate the major and minor version numbers (e.g. "go1_12").
|
||||||
|
// Otherwise it returns the empty string.
|
||||||
|
func asGoVersion(s string) string {
|
||||||
|
if goVersionRx.MatchString(s) {
|
||||||
|
return strings.Replace(s, "_", ".", 1)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkFiles(t *testing.T, sources []string, goVersion string, colDelta uint, trace bool) {
|
||||||
|
if len(sources) == 0 {
|
||||||
|
t.Fatal("no source files")
|
||||||
|
}
|
||||||
|
|
||||||
|
var mode syntax.Mode
|
||||||
|
if strings.HasSuffix(sources[0], ".go2") {
|
||||||
|
mode |= syntax.AllowGenerics
|
||||||
|
}
|
||||||
|
// parse files and collect parser errors
|
||||||
|
files, errlist := parseFiles(t, sources, mode)
|
||||||
|
|
||||||
|
pkgName := "<no package>"
|
||||||
|
if len(files) > 0 {
|
||||||
|
pkgName = files[0].PkgName.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no Go version is given, consider the package name
|
||||||
|
if goVersion == "" {
|
||||||
|
goVersion = asGoVersion(pkgName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *listErrors && len(errlist) > 0 {
|
||||||
|
t.Errorf("--- %s:", pkgName)
|
||||||
|
for _, err := range errlist {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// typecheck and collect typechecker errors
|
||||||
|
var conf Config
|
||||||
|
conf.GoVersion = goVersion
|
||||||
|
conf.AcceptMethodTypeParams = true
|
||||||
|
conf.InferFromConstraints = true
|
||||||
|
// special case for importC.src
|
||||||
|
if len(sources) == 1 && strings.HasSuffix(sources[0], "importC.src") {
|
||||||
|
conf.FakeImportC = true
|
||||||
|
}
|
||||||
|
conf.Trace = trace
|
||||||
|
conf.Importer = defaultImporter()
|
||||||
|
conf.Error = func(err error) {
|
||||||
|
if *haltOnError {
|
||||||
|
defer panic(err)
|
||||||
|
}
|
||||||
|
if *listErrors {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Ignore secondary error messages starting with "\t";
|
||||||
|
// they are clarifying messages for a primary error.
|
||||||
|
if !strings.Contains(err.Error(), ": \t") {
|
||||||
|
errlist = append(errlist, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conf.Check(pkgName, files, nil)
|
||||||
|
|
||||||
|
if *listErrors {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect expected errors
|
||||||
|
errmap := make(map[string]map[uint][]syntax.Error)
|
||||||
|
for _, filename := range sources {
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if m := syntax.ErrorMap(f); len(m) > 0 {
|
||||||
|
errmap[filename] = m
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// match against found errors
|
||||||
|
for _, err := range errlist {
|
||||||
|
got := unpackError(err)
|
||||||
|
|
||||||
|
// find list of errors for the respective error line
|
||||||
|
filename := got.Pos.Base().Filename()
|
||||||
|
filemap := errmap[filename]
|
||||||
|
var line uint
|
||||||
|
var list []syntax.Error
|
||||||
|
if filemap != nil {
|
||||||
|
line = got.Pos.Line()
|
||||||
|
list = filemap[line]
|
||||||
|
}
|
||||||
|
// list may be nil
|
||||||
|
|
||||||
|
// one of errors in list should match the current error
|
||||||
|
index := -1 // list index of matching message, if any
|
||||||
|
for i, want := range list {
|
||||||
|
rx, err := regexp.Compile(want.Msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s:%d:%d: %v", filename, line, want.Pos.Col(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rx.MatchString(got.Msg) {
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if index < 0 {
|
||||||
|
t.Errorf("%s: no error expected: %q", got.Pos, got.Msg)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// column position must be within expected colDelta
|
||||||
|
want := list[index]
|
||||||
|
if delta(got.Pos.Col(), want.Pos.Col()) > colDelta {
|
||||||
|
t.Errorf("%s: got col = %d; want %d", got.Pos, got.Pos.Col(), want.Pos.Col())
|
||||||
|
}
|
||||||
|
|
||||||
|
// eliminate from list
|
||||||
|
if n := len(list) - 1; n > 0 {
|
||||||
|
// not the last entry - swap in last element and shorten list by 1
|
||||||
|
list[index] = list[n]
|
||||||
|
filemap[line] = list[:n]
|
||||||
|
} else {
|
||||||
|
// last entry - remove list from filemap
|
||||||
|
delete(filemap, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if filemap is empty, eliminate from errmap
|
||||||
|
if len(filemap) == 0 {
|
||||||
|
delete(errmap, filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// there should be no expected errors left
|
||||||
|
if len(errmap) > 0 {
|
||||||
|
t.Errorf("--- %s: unreported errors:", pkgName)
|
||||||
|
for filename, filemap := range errmap {
|
||||||
|
for line, list := range filemap {
|
||||||
|
for _, err := range list {
|
||||||
|
t.Errorf("%s:%d:%d: %s", filename, line, err.Pos.Col(), err.Msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCheck is for manual testing of selected input files, provided with -files.
|
||||||
|
// The accepted Go language version can be controlled with the -lang flag.
|
||||||
|
func TestCheck(t *testing.T) {
|
||||||
|
if *testFiles == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
testenv.MustHaveGoBuild(t)
|
||||||
|
DefPredeclaredTestFuncs()
|
||||||
|
checkFiles(t, strings.Split(*testFiles, ","), *goVersion, 0, testing.Verbose())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTestdata(t *testing.T) { DefPredeclaredTestFuncs(); testDir(t, 75, "testdata") } // TODO(gri) narrow column tolerance
|
||||||
|
func TestExamples(t *testing.T) { testDir(t, 0, "examples") }
|
||||||
|
func TestFixedbugs(t *testing.T) { testDir(t, 0, "fixedbugs") }
|
||||||
|
|
||||||
|
func testDir(t *testing.T, colDelta uint, dir string) {
|
||||||
|
testenv.MustHaveGoBuild(t)
|
||||||
|
|
||||||
|
fis, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for count, fi := range fis {
|
||||||
|
path := filepath.Join(dir, fi.Name())
|
||||||
|
|
||||||
|
// if fi is a directory, its files make up a single package
|
||||||
|
if fi.IsDir() {
|
||||||
|
if testing.Verbose() {
|
||||||
|
fmt.Printf("%3d %s\n", count, path)
|
||||||
|
}
|
||||||
|
fis, err := ioutil.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
files := make([]string, len(fis))
|
||||||
|
for i, fi := range fis {
|
||||||
|
// if fi is a directory, checkFiles below will complain
|
||||||
|
files[i] = filepath.Join(path, fi.Name())
|
||||||
|
if testing.Verbose() {
|
||||||
|
fmt.Printf("\t%s\n", files[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkFiles(t, files, "", colDelta, false)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, fi is a stand-alone file
|
||||||
|
if testing.Verbose() {
|
||||||
|
fmt.Printf("%3d %s\n", count, path)
|
||||||
|
}
|
||||||
|
checkFiles(t, []string{path}, "", colDelta, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
166
src/cmd/compile/internal/types2/conversions.go
Normal file
166
src/cmd/compile/internal/types2/conversions.go
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements typechecking of conversions.
|
||||||
|
|
||||||
|
package types2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/constant"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Conversion type-checks the conversion T(x).
|
||||||
|
// The result is in x.
|
||||||
|
func (check *Checker) conversion(x *operand, T Type) {
|
||||||
|
constArg := x.mode == constant_
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
switch {
|
||||||
|
case constArg && isConstType(T):
|
||||||
|
// constant conversion
|
||||||
|
switch t := asBasic(T); {
|
||||||
|
case representableConst(x.val, check, t, &x.val):
|
||||||
|
ok = true
|
||||||
|
case isInteger(x.typ) && isString(t):
|
||||||
|
codepoint := unicode.ReplacementChar
|
||||||
|
if i, ok := constant.Uint64Val(x.val); ok && i <= unicode.MaxRune {
|
||||||
|
codepoint = rune(i)
|
||||||
|
}
|
||||||
|
x.val = constant.MakeString(string(codepoint))
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
case x.convertibleTo(check, T):
|
||||||
|
// non-constant conversion
|
||||||
|
x.mode = value
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
check.errorf(x, "cannot convert %s to %s", x, T)
|
||||||
|
x.mode = invalid
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// The conversion argument types are final. For untyped values the
|
||||||
|
// conversion provides the type, per the spec: "A constant may be
|
||||||
|
// given a type explicitly by a constant declaration or conversion,...".
|
||||||
|
if isUntyped(x.typ) {
|
||||||
|
final := T
|
||||||
|
// - For conversions to interfaces, except for untyped nil arguments,
|
||||||
|
// use the argument's default type.
|
||||||
|
// - For conversions of untyped constants to non-constant types, also
|
||||||
|
// use the default type (e.g., []byte("foo") should report string
|
||||||
|
// not []byte as type for the constant "foo").
|
||||||
|
// - For integer to string conversions, keep the argument type.
|
||||||
|
// (See also the TODO below.)
|
||||||
|
if x.typ == Typ[UntypedNil] {
|
||||||
|
// ok
|
||||||
|
} else if IsInterface(T) || constArg && !isConstType(T) {
|
||||||
|
final = Default(x.typ)
|
||||||
|
} else if isInteger(x.typ) && isString(T) {
|
||||||
|
final = x.typ
|
||||||
|
}
|
||||||
|
check.updateExprType(x.expr, final, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
x.typ = T
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) convertibleTo checks if T(x) is valid. It assumes that the type
|
||||||
|
// of x is fully known, but that's not the case for say string(1<<s + 1.0):
|
||||||
|
// Here, the type of 1<<s + 1.0 will be UntypedFloat which will lead to the
|
||||||
|
// (correct!) refusal of the conversion. But the reported error is essentially
|
||||||
|
// "cannot convert untyped float value to string", yet the correct error (per
|
||||||
|
// the spec) is that we cannot shift a floating-point value: 1 in 1<<s should
|
||||||
|
// be converted to UntypedFloat because of the addition of 1.0. Fixing this
|
||||||
|
// is tricky because we'd have to run updateExprType on the argument first.
|
||||||
|
// (Issue #21982.)
|
||||||
|
|
||||||
|
// convertibleTo reports whether T(x) is valid.
|
||||||
|
// The check parameter may be nil if convertibleTo is invoked through an
|
||||||
|
// exported API call, i.e., when all methods have been type-checked.
|
||||||
|
func (x *operand) convertibleTo(check *Checker, T Type) bool {
|
||||||
|
// "x is assignable to T"
|
||||||
|
if x.assignableTo(check, T, nil) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x's type and T have identical underlying types if tags are ignored"
|
||||||
|
V := x.typ
|
||||||
|
Vu := under(V)
|
||||||
|
Tu := under(T)
|
||||||
|
if check.identicalIgnoreTags(Vu, Tu) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x's type and T are unnamed pointer types and their pointer base types
|
||||||
|
// have identical underlying types if tags are ignored"
|
||||||
|
if V, ok := V.(*Pointer); ok {
|
||||||
|
if T, ok := T.(*Pointer); ok {
|
||||||
|
if check.identicalIgnoreTags(under(V.base), under(T.base)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x's type and T are both integer or floating point types"
|
||||||
|
if isIntegerOrFloat(V) && isIntegerOrFloat(T) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x's type and T are both complex types"
|
||||||
|
if isComplex(V) && isComplex(T) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x is an integer or a slice of bytes or runes and T is a string type"
|
||||||
|
if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// "x is a string and T is a slice of bytes or runes"
|
||||||
|
if isString(V) && isBytesOrRunes(Tu) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// package unsafe:
|
||||||
|
// "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
|
||||||
|
if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// "and vice versa"
|
||||||
|
if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUintptr(typ Type) bool {
|
||||||
|
t := asBasic(typ)
|
||||||
|
return t != nil && t.kind == Uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUnsafePointer(typ Type) bool {
|
||||||
|
// TODO(gri): Is this asBasic(typ) instead of typ.(*Basic) correct?
|
||||||
|
// (The former calls under(), while the latter doesn't.)
|
||||||
|
// The spec does not say so, but gc claims it is. See also
|
||||||
|
// issue 6326.
|
||||||
|
t := asBasic(typ)
|
||||||
|
return t != nil && t.kind == UnsafePointer
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPointer(typ Type) bool {
|
||||||
|
return asPointer(typ) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBytesOrRunes(typ Type) bool {
|
||||||
|
if s := asSlice(typ); s != nil {
|
||||||
|
t := asBasic(s.elem)
|
||||||
|
return t != nil && (t.kind == Byte || t.kind == Rune)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
978
src/cmd/compile/internal/types2/decl.go
Normal file
978
src/cmd/compile/internal/types2/decl.go
Normal file
|
|
@ -0,0 +1,978 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"fmt"
|
||||||
|
"go/constant"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (check *Checker) reportAltDecl(obj Object) {
|
||||||
|
if pos := obj.Pos(); pos.IsKnown() {
|
||||||
|
// We use "other" rather than "previous" here because
|
||||||
|
// the first declaration seen may not be textually
|
||||||
|
// earlier in the source.
|
||||||
|
check.errorf(pos, "\tother declaration of %s", obj.Name()) // secondary error, \t indented
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) declare(scope *Scope, id *syntax.Name, obj Object, pos syntax.Pos) {
|
||||||
|
// spec: "The blank identifier, represented by the underscore
|
||||||
|
// character _, may be used in a declaration like any other
|
||||||
|
// identifier but the declaration does not introduce a new
|
||||||
|
// binding."
|
||||||
|
if obj.Name() != "_" {
|
||||||
|
if alt := scope.Insert(obj); alt != nil {
|
||||||
|
check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
|
||||||
|
check.reportAltDecl(alt)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
obj.setScopePos(pos)
|
||||||
|
}
|
||||||
|
if id != nil {
|
||||||
|
check.recordDef(id, obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pathString returns a string of the form a->b-> ... ->g for a path [a, b, ... g].
|
||||||
|
func pathString(path []Object) string {
|
||||||
|
var s string
|
||||||
|
for i, p := range path {
|
||||||
|
if i > 0 {
|
||||||
|
s += "->"
|
||||||
|
}
|
||||||
|
s += p.Name()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// objDecl type-checks the declaration of obj in its respective (file) context.
|
||||||
|
// For the meaning of def, see Checker.definedType, in typexpr.go.
|
||||||
|
func (check *Checker) objDecl(obj Object, def *Named) {
|
||||||
|
if check.conf.Trace && obj.Type() == nil {
|
||||||
|
if check.indent == 0 {
|
||||||
|
fmt.Println() // empty line between top-level objects for readability
|
||||||
|
}
|
||||||
|
check.trace(obj.Pos(), "-- checking %s (%s, objPath = %s)", obj, obj.color(), pathString(check.objPath))
|
||||||
|
check.indent++
|
||||||
|
defer func() {
|
||||||
|
check.indent--
|
||||||
|
check.trace(obj.Pos(), "=> %s (%s)", obj, obj.color())
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking the declaration of obj means inferring its type
|
||||||
|
// (and possibly its value, for constants).
|
||||||
|
// An object's type (and thus the object) may be in one of
|
||||||
|
// three states which are expressed by colors:
|
||||||
|
//
|
||||||
|
// - an object whose type is not yet known is painted white (initial color)
|
||||||
|
// - an object whose type is in the process of being inferred is painted grey
|
||||||
|
// - an object whose type is fully inferred is painted black
|
||||||
|
//
|
||||||
|
// During type inference, an object's color changes from white to grey
|
||||||
|
// to black (pre-declared objects are painted black from the start).
|
||||||
|
// A black object (i.e., its type) can only depend on (refer to) other black
|
||||||
|
// ones. White and grey objects may depend on white and black objects.
|
||||||
|
// A dependency on a grey object indicates a cycle which may or may not be
|
||||||
|
// valid.
|
||||||
|
//
|
||||||
|
// When objects turn grey, they are pushed on the object path (a stack);
|
||||||
|
// they are popped again when they turn black. Thus, if a grey object (a
|
||||||
|
// cycle) is encountered, it is on the object path, and all the objects
|
||||||
|
// it depends on are the remaining objects on that path. Color encoding
|
||||||
|
// is such that the color value of a grey object indicates the index of
|
||||||
|
// that object in the object path.
|
||||||
|
|
||||||
|
// During type-checking, white objects may be assigned a type without
|
||||||
|
// traversing through objDecl; e.g., when initializing constants and
|
||||||
|
// variables. Update the colors of those objects here (rather than
|
||||||
|
// everywhere where we set the type) to satisfy the color invariants.
|
||||||
|
if obj.color() == white && obj.Type() != nil {
|
||||||
|
obj.setColor(black)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch obj.color() {
|
||||||
|
case white:
|
||||||
|
assert(obj.Type() == nil)
|
||||||
|
// All color values other than white and black are considered grey.
|
||||||
|
// Because black and white are < grey, all values >= grey are grey.
|
||||||
|
// Use those values to encode the object's index into the object path.
|
||||||
|
obj.setColor(grey + color(check.push(obj)))
|
||||||
|
defer func() {
|
||||||
|
check.pop().setColor(black)
|
||||||
|
}()
|
||||||
|
|
||||||
|
case black:
|
||||||
|
assert(obj.Type() != nil)
|
||||||
|
return
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Color values other than white or black are considered grey.
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
case grey:
|
||||||
|
// We have a cycle.
|
||||||
|
// In the existing code, this is marked by a non-nil type
|
||||||
|
// for the object except for constants and variables whose
|
||||||
|
// type may be non-nil (known), or nil if it depends on the
|
||||||
|
// not-yet known initialization value.
|
||||||
|
// In the former case, set the type to Typ[Invalid] because
|
||||||
|
// we have an initialization cycle. The cycle error will be
|
||||||
|
// reported later, when determining initialization order.
|
||||||
|
// TODO(gri) Report cycle here and simplify initialization
|
||||||
|
// order code.
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *Const:
|
||||||
|
if check.cycle(obj) || obj.typ == nil {
|
||||||
|
obj.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Var:
|
||||||
|
if check.cycle(obj) || obj.typ == nil {
|
||||||
|
obj.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
|
||||||
|
case *TypeName:
|
||||||
|
if check.cycle(obj) {
|
||||||
|
// break cycle
|
||||||
|
// (without this, calling underlying()
|
||||||
|
// below may lead to an endless loop
|
||||||
|
// if we have a cycle for a defined
|
||||||
|
// (*Named) type)
|
||||||
|
obj.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Func:
|
||||||
|
if check.cycle(obj) {
|
||||||
|
// Don't set obj.typ to Typ[Invalid] here
|
||||||
|
// because plenty of code type-asserts that
|
||||||
|
// functions have a *Signature type. Grey
|
||||||
|
// functions have their type set to an empty
|
||||||
|
// signature which makes it impossible to
|
||||||
|
// initialize a variable with the function.
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
assert(obj.Type() != nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d := check.objMap[obj]
|
||||||
|
if d == nil {
|
||||||
|
check.dump("%v: %s should have been declared", obj.Pos(), obj)
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
// save/restore current context and setup object context
|
||||||
|
defer func(ctxt context) {
|
||||||
|
check.context = ctxt
|
||||||
|
}(check.context)
|
||||||
|
check.context = context{
|
||||||
|
scope: d.file,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Const and var declarations must not have initialization
|
||||||
|
// cycles. We track them by remembering the current declaration
|
||||||
|
// in check.decl. Initialization expressions depending on other
|
||||||
|
// consts, vars, or functions, add dependencies to the current
|
||||||
|
// check.decl.
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *Const:
|
||||||
|
check.decl = d // new package-level const decl
|
||||||
|
check.constDecl(obj, d.vtyp, d.init, d.inherited)
|
||||||
|
case *Var:
|
||||||
|
check.decl = d // new package-level var decl
|
||||||
|
check.varDecl(obj, d.lhs, d.vtyp, d.init)
|
||||||
|
case *TypeName:
|
||||||
|
// invalid recursive types are detected via path
|
||||||
|
check.typeDecl(obj, d.tdecl, def)
|
||||||
|
check.collectMethods(obj) // methods can only be added to top-level types
|
||||||
|
case *Func:
|
||||||
|
// functions may be recursive - no need to track dependencies
|
||||||
|
check.funcDecl(obj, d)
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cycle checks if the cycle starting with obj is valid and
|
||||||
|
// reports an error if it is not.
|
||||||
|
func (check *Checker) cycle(obj Object) (isCycle bool) {
|
||||||
|
// The object map contains the package scope objects and the non-interface methods.
|
||||||
|
if debug {
|
||||||
|
info := check.objMap[obj]
|
||||||
|
inObjMap := info != nil && (info.fdecl == nil || info.fdecl.Recv == nil) // exclude methods
|
||||||
|
isPkgObj := obj.Parent() == check.pkg.scope
|
||||||
|
if isPkgObj != inObjMap {
|
||||||
|
check.dump("%v: inconsistent object map for %s (isPkgObj = %v, inObjMap = %v)", obj.Pos(), obj, isPkgObj, inObjMap)
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count cycle objects.
|
||||||
|
assert(obj.color() >= grey)
|
||||||
|
start := obj.color() - grey // index of obj in objPath
|
||||||
|
cycle := check.objPath[start:]
|
||||||
|
nval := 0 // number of (constant or variable) values in the cycle
|
||||||
|
ndef := 0 // number of type definitions in the cycle
|
||||||
|
for _, obj := range cycle {
|
||||||
|
switch obj := obj.(type) {
|
||||||
|
case *Const, *Var:
|
||||||
|
nval++
|
||||||
|
case *TypeName:
|
||||||
|
// Determine if the type name is an alias or not. For
|
||||||
|
// package-level objects, use the object map which
|
||||||
|
// provides syntactic information (which doesn't rely
|
||||||
|
// on the order in which the objects are set up). For
|
||||||
|
// local objects, we can rely on the order, so use
|
||||||
|
// the object's predicate.
|
||||||
|
// TODO(gri) It would be less fragile to always access
|
||||||
|
// the syntactic information. We should consider storing
|
||||||
|
// this information explicitly in the object.
|
||||||
|
var alias bool
|
||||||
|
if d := check.objMap[obj]; d != nil {
|
||||||
|
alias = d.tdecl.Alias // package-level object
|
||||||
|
} else {
|
||||||
|
alias = obj.IsAlias() // function local object
|
||||||
|
}
|
||||||
|
if !alias {
|
||||||
|
ndef++
|
||||||
|
}
|
||||||
|
case *Func:
|
||||||
|
// ignored for now
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if check.conf.Trace {
|
||||||
|
check.trace(obj.Pos(), "## cycle detected: objPath = %s->%s (len = %d)", pathString(cycle), obj.Name(), len(cycle))
|
||||||
|
check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef)
|
||||||
|
defer func() {
|
||||||
|
if isCycle {
|
||||||
|
check.trace(obj.Pos(), "=> error: cycle is invalid")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// A cycle involving only constants and variables is invalid but we
|
||||||
|
// ignore them here because they are reported via the initialization
|
||||||
|
// cycle check.
|
||||||
|
if nval == len(cycle) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// A cycle involving only types (and possibly functions) must have at least
|
||||||
|
// one type definition to be permitted: If there is no type definition, we
|
||||||
|
// have a sequence of alias type names which will expand ad infinitum.
|
||||||
|
if nval == 0 && ndef > 0 {
|
||||||
|
return false // cycle is permitted
|
||||||
|
}
|
||||||
|
|
||||||
|
check.cycleError(cycle)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type typeInfo uint
|
||||||
|
|
||||||
|
// validType verifies that the given type does not "expand" infinitely
|
||||||
|
// producing a cycle in the type graph. Cycles are detected by marking
|
||||||
|
// defined types.
|
||||||
|
// (Cycles involving alias types, as in "type A = [10]A" are detected
|
||||||
|
// earlier, via the objDecl cycle detection mechanism.)
|
||||||
|
func (check *Checker) validType(typ Type, path []Object) typeInfo {
|
||||||
|
const (
|
||||||
|
unknown typeInfo = iota
|
||||||
|
marked
|
||||||
|
valid
|
||||||
|
invalid
|
||||||
|
)
|
||||||
|
|
||||||
|
switch t := typ.(type) {
|
||||||
|
case *Array:
|
||||||
|
return check.validType(t.elem, path)
|
||||||
|
|
||||||
|
case *Struct:
|
||||||
|
for _, f := range t.fields {
|
||||||
|
if check.validType(f.typ, path) == invalid {
|
||||||
|
return invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Interface:
|
||||||
|
for _, etyp := range t.embeddeds {
|
||||||
|
if check.validType(etyp, path) == invalid {
|
||||||
|
return invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case *Named:
|
||||||
|
// don't touch the type if it is from a different package or the Universe scope
|
||||||
|
// (doing so would lead to a race condition - was issue #35049)
|
||||||
|
if t.obj.pkg != check.pkg {
|
||||||
|
return valid
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't report a 2nd error if we already know the type is invalid
|
||||||
|
// (e.g., if a cycle was detected earlier, via Checker.underlying).
|
||||||
|
if t.underlying == Typ[Invalid] {
|
||||||
|
t.info = invalid
|
||||||
|
return invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t.info {
|
||||||
|
case unknown:
|
||||||
|
t.info = marked
|
||||||
|
t.info = check.validType(t.orig, append(path, t.obj)) // only types of current package added to path
|
||||||
|
case marked:
|
||||||
|
// cycle detected
|
||||||
|
for i, tn := range path {
|
||||||
|
if t.obj.pkg != check.pkg {
|
||||||
|
panic("internal error: type cycle via package-external type")
|
||||||
|
}
|
||||||
|
if tn == t.obj {
|
||||||
|
check.cycleError(path[i:])
|
||||||
|
t.info = invalid
|
||||||
|
return t.info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic("internal error: cycle start not found")
|
||||||
|
}
|
||||||
|
return t.info
|
||||||
|
|
||||||
|
case *instance:
|
||||||
|
return check.validType(t.expand(), path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid
|
||||||
|
}
|
||||||
|
|
||||||
|
// cycleError reports a declaration cycle starting with
|
||||||
|
// the object in cycle that is "first" in the source.
|
||||||
|
func (check *Checker) cycleError(cycle []Object) {
|
||||||
|
// TODO(gri) Should we start with the last (rather than the first) object in the cycle
|
||||||
|
// since that is the earliest point in the source where we start seeing the
|
||||||
|
// cycle? That would be more consistent with other error messages.
|
||||||
|
i := firstInSrc(cycle)
|
||||||
|
obj := cycle[i]
|
||||||
|
if check.conf.CompilerErrorMessages {
|
||||||
|
check.errorf(obj.Pos(), "invalid recursive type %s", obj.Name())
|
||||||
|
} else {
|
||||||
|
check.errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name())
|
||||||
|
}
|
||||||
|
for range cycle {
|
||||||
|
check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
|
||||||
|
i++
|
||||||
|
if i >= len(cycle) {
|
||||||
|
i = 0
|
||||||
|
}
|
||||||
|
obj = cycle[i]
|
||||||
|
}
|
||||||
|
check.errorf(obj.Pos(), "\t%s", obj.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(gri) This functionality should probably be with the Pos implementation.
|
||||||
|
func cmpPos(p, q syntax.Pos) int {
|
||||||
|
// TODO(gri) is RelFilename correct here?
|
||||||
|
pname := p.RelFilename()
|
||||||
|
qname := q.RelFilename()
|
||||||
|
switch {
|
||||||
|
case pname < qname:
|
||||||
|
return -1
|
||||||
|
case pname > qname:
|
||||||
|
return +1
|
||||||
|
}
|
||||||
|
|
||||||
|
pline := p.Line()
|
||||||
|
qline := q.Line()
|
||||||
|
switch {
|
||||||
|
case pline < qline:
|
||||||
|
return -1
|
||||||
|
case pline > qline:
|
||||||
|
return +1
|
||||||
|
}
|
||||||
|
|
||||||
|
pcol := p.Col()
|
||||||
|
qcol := q.Col()
|
||||||
|
switch {
|
||||||
|
case pcol < qcol:
|
||||||
|
return -1
|
||||||
|
case pcol > qcol:
|
||||||
|
return +1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// firstInSrc reports the index of the object with the "smallest"
|
||||||
|
// source position in path. path must not be empty.
|
||||||
|
func firstInSrc(path []Object) int {
|
||||||
|
fst, pos := 0, path[0].Pos()
|
||||||
|
for i, t := range path[1:] {
|
||||||
|
if cmpPos(t.Pos(), pos) < 0 {
|
||||||
|
fst, pos = i+1, t.Pos()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fst
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) constDecl(obj *Const, typ, init syntax.Expr, inherited bool) {
|
||||||
|
assert(obj.typ == nil)
|
||||||
|
|
||||||
|
// use the correct value of iota and errpos
|
||||||
|
defer func(iota constant.Value, errpos syntax.Pos) {
|
||||||
|
check.iota = iota
|
||||||
|
check.errpos = errpos
|
||||||
|
}(check.iota, check.errpos)
|
||||||
|
check.iota = obj.val
|
||||||
|
check.errpos = nopos
|
||||||
|
|
||||||
|
// provide valid constant value under all circumstances
|
||||||
|
obj.val = constant.MakeUnknown()
|
||||||
|
|
||||||
|
// determine type, if any
|
||||||
|
if typ != nil {
|
||||||
|
t := check.typ(typ)
|
||||||
|
if !isConstType(t) {
|
||||||
|
// don't report an error if the type is an invalid C (defined) type
|
||||||
|
// (issue #22090)
|
||||||
|
if under(t) != Typ[Invalid] {
|
||||||
|
check.errorf(typ, "invalid constant type %s", t)
|
||||||
|
}
|
||||||
|
obj.typ = Typ[Invalid]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
obj.typ = t
|
||||||
|
}
|
||||||
|
|
||||||
|
// check initialization
|
||||||
|
var x operand
|
||||||
|
if init != nil {
|
||||||
|
if inherited {
|
||||||
|
// The initialization expression is inherited from a previous
|
||||||
|
// constant declaration, and (error) positions refer to that
|
||||||
|
// expression and not the current constant declaration. Use
|
||||||
|
// the constant identifier position for any errors during
|
||||||
|
// init expression evaluation since that is all we have
|
||||||
|
// (see issues #42991, #42992).
|
||||||
|
check.errpos = obj.pos
|
||||||
|
}
|
||||||
|
check.expr(&x, init)
|
||||||
|
}
|
||||||
|
check.initConst(obj, &x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init syntax.Expr) {
|
||||||
|
assert(obj.typ == nil)
|
||||||
|
|
||||||
|
// If we have undefined variable types due to errors,
|
||||||
|
// mark variables as used to avoid follow-on errors.
|
||||||
|
// Matches compiler behavior.
|
||||||
|
defer func() {
|
||||||
|
if obj.typ == Typ[Invalid] {
|
||||||
|
obj.used = true
|
||||||
|
}
|
||||||
|
for _, lhs := range lhs {
|
||||||
|
if lhs.typ == Typ[Invalid] {
|
||||||
|
lhs.used = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// determine type, if any
|
||||||
|
if typ != nil {
|
||||||
|
obj.typ = check.varType(typ)
|
||||||
|
// We cannot spread the type to all lhs variables if there
|
||||||
|
// are more than one since that would mark them as checked
|
||||||
|
// (see Checker.objDecl) and the assignment of init exprs,
|
||||||
|
// if any, would not be checked.
|
||||||
|
//
|
||||||
|
// TODO(gri) If we have no init expr, we should distribute
|
||||||
|
// a given type otherwise we need to re-evalate the type
|
||||||
|
// expr for each lhs variable, leading to duplicate work.
|
||||||
|
}
|
||||||
|
|
||||||
|
// check initialization
|
||||||
|
if init == nil {
|
||||||
|
if typ == nil {
|
||||||
|
// error reported before by arityMatch
|
||||||
|
obj.typ = Typ[Invalid]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if lhs == nil || len(lhs) == 1 {
|
||||||
|
assert(lhs == nil || lhs[0] == obj)
|
||||||
|
var x operand
|
||||||
|
check.expr(&x, init)
|
||||||
|
check.initVar(obj, &x, "variable declaration")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
// obj must be one of lhs
|
||||||
|
found := false
|
||||||
|
for _, lhs := range lhs {
|
||||||
|
if obj == lhs {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
panic("inconsistent lhs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have multiple variables on the lhs and one init expr.
|
||||||
|
// Make sure all variables have been given the same type if
|
||||||
|
// one was specified, otherwise they assume the type of the
|
||||||
|
// init expression values (was issue #15755).
|
||||||
|
if typ != nil {
|
||||||
|
for _, lhs := range lhs {
|
||||||
|
lhs.typ = obj.typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check.initVars(lhs, []syntax.Expr{init}, nopos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// under returns the expanded underlying type of n0; possibly by following
|
||||||
|
// forward chains of named types. If an underlying type is found, resolve
|
||||||
|
// the chain by setting the underlying type for each defined type in the
|
||||||
|
// chain before returning it. If no underlying type is found or a cycle
|
||||||
|
// is detected, the result is Typ[Invalid]. If a cycle is detected and
|
||||||
|
// n0.check != nil, the cycle is reported.
|
||||||
|
func (n0 *Named) under() Type {
|
||||||
|
u := n0.underlying
|
||||||
|
if u == nil {
|
||||||
|
return Typ[Invalid]
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the underlying type of a defined type is not a defined
|
||||||
|
// type, then that is the desired underlying type.
|
||||||
|
n := asNamed(u)
|
||||||
|
if n == nil {
|
||||||
|
return u // common case
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, follow the forward chain.
|
||||||
|
seen := map[*Named]int{n0: 0}
|
||||||
|
path := []Object{n0.obj}
|
||||||
|
for {
|
||||||
|
u = n.underlying
|
||||||
|
if u == nil {
|
||||||
|
u = Typ[Invalid]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
n1 := asNamed(u)
|
||||||
|
if n1 == nil {
|
||||||
|
break // end of chain
|
||||||
|
}
|
||||||
|
|
||||||
|
seen[n] = len(seen)
|
||||||
|
path = append(path, n.obj)
|
||||||
|
n = n1
|
||||||
|
|
||||||
|
if i, ok := seen[n]; ok {
|
||||||
|
// cycle
|
||||||
|
// TODO(gri) revert this to a method on Checker. Having a possibly
|
||||||
|
// nil Checker on Named and TypeParam is too subtle.
|
||||||
|
if n0.check != nil {
|
||||||
|
n0.check.cycleError(path[i:])
|
||||||
|
}
|
||||||
|
u = Typ[Invalid]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for n := range seen {
|
||||||
|
// We should never have to update the underlying type of an imported type;
|
||||||
|
// those underlying types should have been resolved during the import.
|
||||||
|
// Also, doing so would lead to a race condition (was issue #31749).
|
||||||
|
// Do this check always, not just in debug more (it's cheap).
|
||||||
|
if n0.check != nil && n.obj.pkg != n0.check.pkg {
|
||||||
|
panic("internal error: imported type with unresolved underlying type")
|
||||||
|
}
|
||||||
|
n.underlying = u
|
||||||
|
}
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Named) setUnderlying(typ Type) {
|
||||||
|
if n != nil {
|
||||||
|
n.underlying = typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named) {
|
||||||
|
assert(obj.typ == nil)
|
||||||
|
|
||||||
|
check.later(func() {
|
||||||
|
check.validType(obj.typ, nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
alias := tdecl.Alias
|
||||||
|
if alias && tdecl.TParamList != nil {
|
||||||
|
// The parser will ensure this but we may still get an invalid AST.
|
||||||
|
// Complain and continue as regular type definition.
|
||||||
|
check.errorf(tdecl, "generic type cannot be alias")
|
||||||
|
alias = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if alias {
|
||||||
|
// type alias declaration
|
||||||
|
if !check.allowVersion(obj.pkg, 1, 9) {
|
||||||
|
check.errorf(tdecl, "type aliases requires go1.9 or later")
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.typ = Typ[Invalid]
|
||||||
|
obj.typ = check.anyType(tdecl.Type)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// defined type declaration
|
||||||
|
|
||||||
|
named := &Named{check: check, obj: obj}
|
||||||
|
def.setUnderlying(named)
|
||||||
|
obj.typ = named // make sure recursive type declarations terminate
|
||||||
|
|
||||||
|
if tdecl.TParamList != nil {
|
||||||
|
check.openScope(tdecl, "type parameters")
|
||||||
|
defer check.closeScope()
|
||||||
|
named.tparams = check.collectTypeParams(tdecl.TParamList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine underlying type of named
|
||||||
|
named.orig = check.definedType(tdecl.Type, named)
|
||||||
|
|
||||||
|
// The underlying type of named may be itself a named type that is
|
||||||
|
// incomplete:
|
||||||
|
//
|
||||||
|
// type (
|
||||||
|
// A B
|
||||||
|
// B *C
|
||||||
|
// C A
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// The type of C is the (named) type of A which is incomplete,
|
||||||
|
// and which has as its underlying type the named type B.
|
||||||
|
// Determine the (final, unnamed) underlying type by resolving
|
||||||
|
// any forward chain.
|
||||||
|
// TODO(gri) Investigate if we can just use named.origin here
|
||||||
|
// and rely on lazy computation of the underlying type.
|
||||||
|
named.underlying = under(named)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) collectTypeParams(list []*syntax.Field) (tparams []*TypeName) {
|
||||||
|
// Type parameter lists should not be empty. The parser will
|
||||||
|
// complain but we still may get an incorrect AST: ignore it.
|
||||||
|
if len(list) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare type parameters up-front, with empty interface as type bound.
|
||||||
|
// The scope of type parameters starts at the beginning of the type parameter
|
||||||
|
// list (so we can have mutually recursive parameterized interfaces).
|
||||||
|
for _, f := range list {
|
||||||
|
tparams = check.declareTypeParam(tparams, f.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bound Type
|
||||||
|
for i, j := 0, 0; i < len(list); i = j {
|
||||||
|
f := list[i]
|
||||||
|
|
||||||
|
// determine the range of type parameters list[i:j] with identical type bound
|
||||||
|
// (declared as in (type a, b, c B))
|
||||||
|
j = i + 1
|
||||||
|
for j < len(list) && list[j].Type == f.Type {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
|
||||||
|
// this should never be the case, but be careful
|
||||||
|
if f.Type == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// The predeclared identifier "any" is visible only as a constraint
|
||||||
|
// in a type parameter list. Look for it before general constraint
|
||||||
|
// resolution.
|
||||||
|
if tident, _ := f.Type.(*syntax.Name); tident != nil && tident.Value == "any" && check.lookup("any") == nil {
|
||||||
|
bound = universeAny
|
||||||
|
} else {
|
||||||
|
bound = check.typ(f.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// type bound must be an interface
|
||||||
|
// TODO(gri) We should delay the interface check because
|
||||||
|
// we may not have a complete interface yet:
|
||||||
|
// type C(type T C) interface {}
|
||||||
|
// (issue #39724).
|
||||||
|
if _, ok := under(bound).(*Interface); ok {
|
||||||
|
// set the type bounds
|
||||||
|
for i < j {
|
||||||
|
tparams[i].typ.(*TypeParam).bound = bound
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
} else if bound != Typ[Invalid] {
|
||||||
|
check.errorf(f.Type, "%s is not an interface", bound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) declareTypeParam(tparams []*TypeName, name *syntax.Name) []*TypeName {
|
||||||
|
tpar := NewTypeName(name.Pos(), check.pkg, name.Value, nil)
|
||||||
|
check.NewTypeParam(tpar, len(tparams), &emptyInterface) // assigns type to tpar as a side-effect
|
||||||
|
check.declare(check.scope, name, tpar, check.scope.pos) // TODO(gri) check scope position
|
||||||
|
tparams = append(tparams, tpar)
|
||||||
|
|
||||||
|
if check.conf.Trace {
|
||||||
|
check.trace(name.Pos(), "type param = %v", tparams[len(tparams)-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return tparams
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) collectMethods(obj *TypeName) {
|
||||||
|
// get associated methods
|
||||||
|
// (Checker.collectObjects only collects methods with non-blank names;
|
||||||
|
// Checker.resolveBaseTypeName ensures that obj is not an alias name
|
||||||
|
// if it has attached methods.)
|
||||||
|
methods := check.methods[obj]
|
||||||
|
if methods == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delete(check.methods, obj)
|
||||||
|
assert(!check.objMap[obj].tdecl.Alias) // don't use TypeName.IsAlias (requires fully set up object)
|
||||||
|
|
||||||
|
// use an objset to check for name conflicts
|
||||||
|
var mset objset
|
||||||
|
|
||||||
|
// spec: "If the base type is a struct type, the non-blank method
|
||||||
|
// and field names must be distinct."
|
||||||
|
base := asNamed(obj.typ) // shouldn't fail but be conservative
|
||||||
|
if base != nil {
|
||||||
|
if t, _ := base.underlying.(*Struct); t != nil {
|
||||||
|
for _, fld := range t.fields {
|
||||||
|
if fld.name != "_" {
|
||||||
|
assert(mset.insert(fld) == nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checker.Files may be called multiple times; additional package files
|
||||||
|
// may add methods to already type-checked types. Add pre-existing methods
|
||||||
|
// so that we can detect redeclarations.
|
||||||
|
for _, m := range base.methods {
|
||||||
|
assert(m.name != "_")
|
||||||
|
assert(mset.insert(m) == nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add valid methods
|
||||||
|
for _, m := range methods {
|
||||||
|
// spec: "For a base type, the non-blank names of methods bound
|
||||||
|
// to it must be unique."
|
||||||
|
assert(m.name != "_")
|
||||||
|
if alt := mset.insert(m); alt != nil {
|
||||||
|
switch alt.(type) {
|
||||||
|
case *Var:
|
||||||
|
check.errorf(m.pos, "field and method with the same name %s", m.name)
|
||||||
|
case *Func:
|
||||||
|
if check.conf.CompilerErrorMessages {
|
||||||
|
check.errorf(m.pos, "%s.%s redeclared in this block", obj.Name(), m.name)
|
||||||
|
} else {
|
||||||
|
check.errorf(m.pos, "method %s already declared for %s", m.name, obj)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
check.reportAltDecl(alt)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if base != nil {
|
||||||
|
base.methods = append(base.methods, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
|
||||||
|
assert(obj.typ == nil)
|
||||||
|
|
||||||
|
// func declarations cannot use iota
|
||||||
|
assert(check.iota == nil)
|
||||||
|
|
||||||
|
sig := new(Signature)
|
||||||
|
obj.typ = sig // guard against cycles
|
||||||
|
|
||||||
|
// Avoid cycle error when referring to method while type-checking the signature.
|
||||||
|
// This avoids a nuisance in the best case (non-parameterized receiver type) and
|
||||||
|
// since the method is not a type, we get an error. If we have a parameterized
|
||||||
|
// receiver type, instantiating the receiver type leads to the instantiation of
|
||||||
|
// its methods, and we don't want a cycle error in that case.
|
||||||
|
// TODO(gri) review if this is correct and/or whether we still need this?
|
||||||
|
saved := obj.color_
|
||||||
|
obj.color_ = black
|
||||||
|
fdecl := decl.fdecl
|
||||||
|
check.funcType(sig, fdecl.Recv, fdecl.TParamList, fdecl.Type)
|
||||||
|
obj.color_ = saved
|
||||||
|
|
||||||
|
// function body must be type-checked after global declarations
|
||||||
|
// (functions implemented elsewhere have no body)
|
||||||
|
if !check.conf.IgnoreFuncBodies && fdecl.Body != nil {
|
||||||
|
check.later(func() {
|
||||||
|
check.funcBody(decl, obj.name, sig, fdecl.Body, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) declStmt(list []syntax.Decl) {
|
||||||
|
pkg := check.pkg
|
||||||
|
|
||||||
|
first := -1 // index of first ConstDecl in the current group, or -1
|
||||||
|
var last *syntax.ConstDecl // last ConstDecl with init expressions, or nil
|
||||||
|
for index, decl := range list {
|
||||||
|
if _, ok := decl.(*syntax.ConstDecl); !ok {
|
||||||
|
first = -1 // we're not in a constant declaration
|
||||||
|
}
|
||||||
|
|
||||||
|
switch s := decl.(type) {
|
||||||
|
case *syntax.ConstDecl:
|
||||||
|
top := len(check.delayed)
|
||||||
|
|
||||||
|
// iota is the index of the current constDecl within the group
|
||||||
|
if first < 0 || list[index-1].(*syntax.ConstDecl).Group != s.Group {
|
||||||
|
first = index
|
||||||
|
last = nil
|
||||||
|
}
|
||||||
|
iota := constant.MakeInt64(int64(index - first))
|
||||||
|
|
||||||
|
// determine which initialization expressions to use
|
||||||
|
inherited := true
|
||||||
|
switch {
|
||||||
|
case s.Type != nil || s.Values != nil:
|
||||||
|
last = s
|
||||||
|
inherited = false
|
||||||
|
case last == nil:
|
||||||
|
last = new(syntax.ConstDecl) // make sure last exists
|
||||||
|
inherited = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// declare all constants
|
||||||
|
lhs := make([]*Const, len(s.NameList))
|
||||||
|
values := unpackExpr(last.Values)
|
||||||
|
for i, name := range s.NameList {
|
||||||
|
obj := NewConst(name.Pos(), pkg, name.Value, nil, iota)
|
||||||
|
lhs[i] = obj
|
||||||
|
|
||||||
|
var init syntax.Expr
|
||||||
|
if i < len(values) {
|
||||||
|
init = values[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
check.constDecl(obj, last.Type, init, inherited)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constants must always have init values.
|
||||||
|
check.arity(s.Pos(), s.NameList, values, true, inherited)
|
||||||
|
|
||||||
|
// process function literals in init expressions before scope changes
|
||||||
|
check.processDelayed(top)
|
||||||
|
|
||||||
|
// spec: "The scope of a constant or variable identifier declared
|
||||||
|
// inside a function begins at the end of the ConstSpec or VarSpec
|
||||||
|
// (ShortVarDecl for short variable declarations) and ends at the
|
||||||
|
// end of the innermost containing block."
|
||||||
|
scopePos := endPos(s)
|
||||||
|
for i, name := range s.NameList {
|
||||||
|
check.declare(check.scope, name, lhs[i], scopePos)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *syntax.VarDecl:
|
||||||
|
top := len(check.delayed)
|
||||||
|
|
||||||
|
lhs0 := make([]*Var, len(s.NameList))
|
||||||
|
for i, name := range s.NameList {
|
||||||
|
lhs0[i] = NewVar(name.Pos(), pkg, name.Value, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize all variables
|
||||||
|
values := unpackExpr(s.Values)
|
||||||
|
for i, obj := range lhs0 {
|
||||||
|
var lhs []*Var
|
||||||
|
var init syntax.Expr
|
||||||
|
switch len(values) {
|
||||||
|
case len(s.NameList):
|
||||||
|
// lhs and rhs match
|
||||||
|
init = values[i]
|
||||||
|
case 1:
|
||||||
|
// rhs is expected to be a multi-valued expression
|
||||||
|
lhs = lhs0
|
||||||
|
init = values[0]
|
||||||
|
default:
|
||||||
|
if i < len(values) {
|
||||||
|
init = values[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check.varDecl(obj, lhs, s.Type, init)
|
||||||
|
if len(values) == 1 {
|
||||||
|
// If we have a single lhs variable we are done either way.
|
||||||
|
// If we have a single rhs expression, it must be a multi-
|
||||||
|
// valued expression, in which case handling the first lhs
|
||||||
|
// variable will cause all lhs variables to have a type
|
||||||
|
// assigned, and we are done as well.
|
||||||
|
if debug {
|
||||||
|
for _, obj := range lhs0 {
|
||||||
|
assert(obj.typ != nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have no type, we must have values.
|
||||||
|
if s.Type == nil || values != nil {
|
||||||
|
check.arity(s.Pos(), s.NameList, values, false, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// process function literals in init expressions before scope changes
|
||||||
|
check.processDelayed(top)
|
||||||
|
|
||||||
|
// declare all variables
|
||||||
|
// (only at this point are the variable scopes (parents) set)
|
||||||
|
scopePos := endPos(s) // see constant declarations
|
||||||
|
for i, name := range s.NameList {
|
||||||
|
// see constant declarations
|
||||||
|
check.declare(check.scope, name, lhs0[i], scopePos)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *syntax.TypeDecl:
|
||||||
|
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Value, nil)
|
||||||
|
// spec: "The scope of a type identifier declared inside a function
|
||||||
|
// begins at the identifier in the TypeSpec and ends at the end of
|
||||||
|
// the innermost containing block."
|
||||||
|
scopePos := s.Name.Pos()
|
||||||
|
check.declare(check.scope, s.Name, obj, scopePos)
|
||||||
|
// mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
|
||||||
|
obj.setColor(grey + color(check.push(obj)))
|
||||||
|
check.typeDecl(obj, s, nil)
|
||||||
|
check.pop().setColor(black)
|
||||||
|
|
||||||
|
default:
|
||||||
|
check.invalidASTf(s, "unknown syntax.Decl node %T", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
172
src/cmd/compile/internal/types2/errors.go
Normal file
172
src/cmd/compile/internal/types2/errors.go
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2012 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file implements various error reporters.
|
||||||
|
|
||||||
|
package types2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func unimplemented() {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func assert(p bool) {
|
||||||
|
if !p {
|
||||||
|
panic("assertion failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unreachable() {
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) qualifier(pkg *Package) string {
|
||||||
|
// Qualify the package unless it's the package being type-checked.
|
||||||
|
if pkg != check.pkg {
|
||||||
|
// If the same package name was used by multiple packages, display the full path.
|
||||||
|
if check.pkgCnt[pkg.name] > 1 {
|
||||||
|
return strconv.Quote(pkg.path)
|
||||||
|
}
|
||||||
|
return pkg.name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) sprintf(format string, args ...interface{}) string {
|
||||||
|
for i, arg := range args {
|
||||||
|
switch a := arg.(type) {
|
||||||
|
case nil:
|
||||||
|
arg = "<nil>"
|
||||||
|
case operand:
|
||||||
|
panic("internal error: should always pass *operand")
|
||||||
|
case *operand:
|
||||||
|
arg = operandString(a, check.qualifier)
|
||||||
|
case syntax.Pos:
|
||||||
|
arg = a.String()
|
||||||
|
case syntax.Expr:
|
||||||
|
arg = syntax.String(a)
|
||||||
|
case Object:
|
||||||
|
arg = ObjectString(a, check.qualifier)
|
||||||
|
case Type:
|
||||||
|
arg = TypeString(a, check.qualifier)
|
||||||
|
}
|
||||||
|
args[i] = arg
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) trace(pos syntax.Pos, format string, args ...interface{}) {
|
||||||
|
fmt.Printf("%s:\t%s%s\n",
|
||||||
|
pos,
|
||||||
|
strings.Repeat(". ", check.indent),
|
||||||
|
check.sprintf(format, args...),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// dump is only needed for debugging
|
||||||
|
func (check *Checker) dump(format string, args ...interface{}) {
|
||||||
|
fmt.Println(check.sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) err(pos syntax.Pos, msg string, soft bool) {
|
||||||
|
// Cheap trick: Don't report errors with messages containing
|
||||||
|
// "invalid operand" or "invalid type" as those tend to be
|
||||||
|
// follow-on errors which don't add useful information. Only
|
||||||
|
// exclude them if these strings are not at the beginning,
|
||||||
|
// and only if we have at least one error already reported.
|
||||||
|
if check.firstErr != nil && (strings.Index(msg, "invalid operand") > 0 || strings.Index(msg, "invalid type") > 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are encountering an error while evaluating an inherited
|
||||||
|
// constant initialization expression, pos is the position of in
|
||||||
|
// the original expression, and not of the currently declared
|
||||||
|
// constant identifier. Use the provided errpos instead.
|
||||||
|
// TODO(gri) We may also want to augment the error message and
|
||||||
|
// refer to the position (pos) in the original expression.
|
||||||
|
if check.errpos.IsKnown() {
|
||||||
|
assert(check.iota != nil)
|
||||||
|
pos = check.errpos
|
||||||
|
}
|
||||||
|
|
||||||
|
err := Error{pos, stripAnnotations(msg), msg, soft}
|
||||||
|
if check.firstErr == nil {
|
||||||
|
check.firstErr = err
|
||||||
|
}
|
||||||
|
|
||||||
|
if check.conf.Trace {
|
||||||
|
check.trace(pos, "ERROR: %s", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
f := check.conf.Error
|
||||||
|
if f == nil {
|
||||||
|
panic(bailout{}) // report only first error
|
||||||
|
}
|
||||||
|
f(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type poser interface {
|
||||||
|
Pos() syntax.Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) error(at poser, msg string) {
|
||||||
|
check.err(posFor(at), msg, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) errorf(at poser, format string, args ...interface{}) {
|
||||||
|
check.err(posFor(at), check.sprintf(format, args...), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) softErrorf(at poser, format string, args ...interface{}) {
|
||||||
|
check.err(posFor(at), check.sprintf(format, args...), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) invalidASTf(at poser, format string, args ...interface{}) {
|
||||||
|
check.errorf(at, "invalid AST: "+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) invalidArgf(at poser, format string, args ...interface{}) {
|
||||||
|
check.errorf(at, "invalid argument: "+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (check *Checker) invalidOpf(at poser, format string, args ...interface{}) {
|
||||||
|
check.errorf(at, "invalid operation: "+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// posFor reports the left (= start) position of at.
|
||||||
|
func posFor(at poser) syntax.Pos {
|
||||||
|
switch x := at.(type) {
|
||||||
|
case *operand:
|
||||||
|
if x.expr != nil {
|
||||||
|
return startPos(x.expr)
|
||||||
|
}
|
||||||
|
case syntax.Node:
|
||||||
|
return startPos(x)
|
||||||
|
}
|
||||||
|
return at.Pos()
|
||||||
|
}
|
||||||
|
|
||||||
|
// stripAnnotations removes internal (type) annotations from s.
|
||||||
|
func stripAnnotations(s string) string {
|
||||||
|
// Would like to use strings.Builder but it's not available in Go 1.4.
|
||||||
|
var b bytes.Buffer
|
||||||
|
for _, r := range s {
|
||||||
|
// strip #'s and subscript digits
|
||||||
|
if r != instanceMarker && !('₀' <= r && r < '₀'+10) { // '₀' == U+2080
|
||||||
|
b.WriteRune(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b.Len() < len(s) {
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
25
src/cmd/compile/internal/types2/errors_test.go
Normal file
25
src/cmd/compile/internal/types2/errors_test.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types2
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestStripAnnotations(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
in, want string
|
||||||
|
}{
|
||||||
|
{"", ""},
|
||||||
|
{" ", " "},
|
||||||
|
{"foo", "foo"},
|
||||||
|
{"foo₀", "foo"},
|
||||||
|
{"foo(T₀)", "foo(T)"},
|
||||||
|
{"#foo(T₀)", "foo(T)"},
|
||||||
|
} {
|
||||||
|
got := stripAnnotations(test.in)
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("%q: got %q; want %q", test.in, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
269
src/cmd/compile/internal/types2/example_test.go
Normal file
269
src/cmd/compile/internal/types2/example_test.go
Normal file
|
|
@ -0,0 +1,269 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Only run where builders (build.golang.org) have
|
||||||
|
// access to compiled packages for import.
|
||||||
|
//
|
||||||
|
// +build !arm,!arm64
|
||||||
|
|
||||||
|
package types2_test
|
||||||
|
|
||||||
|
// This file shows examples of basic usage of the go/types API.
|
||||||
|
//
|
||||||
|
// To locate a Go package, use (*go/build.Context).Import.
|
||||||
|
// To load, parse, and type-check a complete Go program
|
||||||
|
// from source, use golang.org/x/tools/go/loader.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cmd/compile/internal/syntax"
|
||||||
|
"cmd/compile/internal/types2"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExampleScope prints the tree of Scopes of a package created from a
|
||||||
|
// set of parsed files.
|
||||||
|
func ExampleScope() {
|
||||||
|
// Parse the source files for a package.
|
||||||
|
var files []*syntax.File
|
||||||
|
for _, file := range []struct{ name, input string }{
|
||||||
|
{"main.go", `
|
||||||
|
package main
|
||||||
|
import "fmt"
|
||||||
|
func main() {
|
||||||
|
freezing := FToC(-18)
|
||||||
|
fmt.Println(freezing, Boiling) }
|
||||||
|
`},
|
||||||
|
{"celsius.go", `
|
||||||
|
package main
|
||||||
|
import "fmt"
|
||||||
|
type Celsius float64
|
||||||
|
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
|
||||||
|
func FToC(f float64) Celsius { return Celsius(f - 32 / 9 * 5) }
|
||||||
|
const Boiling Celsius = 100
|
||||||
|
func Unused() { {}; {{ var x int; _ = x }} } // make sure empty block scopes get printed
|
||||||
|
`},
|
||||||
|
} {
|
||||||
|
f, err := parseSrc(file.name, file.input)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
files = append(files, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type-check a package consisting of these files.
|
||||||
|
// Type information for the imported "fmt" package
|
||||||
|
// comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a.
|
||||||
|
conf := types2.Config{Importer: defaultImporter()}
|
||||||
|
pkg, err := conf.Check("temperature", files, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the tree of scopes.
|
||||||
|
// For determinism, we redact addresses.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
pkg.Scope().WriteTo(&buf, 0, true)
|
||||||
|
rx := regexp.MustCompile(` 0x[a-fA-F0-9]*`)
|
||||||
|
fmt.Println(rx.ReplaceAllString(buf.String(), ""))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// package "temperature" scope {
|
||||||
|
// . const temperature.Boiling temperature.Celsius
|
||||||
|
// . type temperature.Celsius float64
|
||||||
|
// . func temperature.FToC(f float64) temperature.Celsius
|
||||||
|
// . func temperature.Unused()
|
||||||
|
// . func temperature.main()
|
||||||
|
// . main.go scope {
|
||||||
|
// . . package fmt
|
||||||
|
// . . function scope {
|
||||||
|
// . . . var freezing temperature.Celsius
|
||||||
|
// . . }
|
||||||
|
// . }
|
||||||
|
// . celsius.go scope {
|
||||||
|
// . . package fmt
|
||||||
|
// . . function scope {
|
||||||
|
// . . . var c temperature.Celsius
|
||||||
|
// . . }
|
||||||
|
// . . function scope {
|
||||||
|
// . . . var f float64
|
||||||
|
// . . }
|
||||||
|
// . . function scope {
|
||||||
|
// . . . block scope {
|
||||||
|
// . . . }
|
||||||
|
// . . . block scope {
|
||||||
|
// . . . . block scope {
|
||||||
|
// . . . . . var x int
|
||||||
|
// . . . . }
|
||||||
|
// . . . }
|
||||||
|
// . . }
|
||||||
|
// . }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExampleInfo prints various facts recorded by the type checker in a
|
||||||
|
// types2.Info struct: definitions of and references to each named object,
|
||||||
|
// and the type, value, and mode of every expression in the package.
|
||||||
|
func ExampleInfo() {
|
||||||
|
// Parse a single source file.
|
||||||
|
const input = `
|
||||||
|
package fib
|
||||||
|
|
||||||
|
type S string
|
||||||
|
|
||||||
|
var a, b, c = len(b), S(c), "hello"
|
||||||
|
|
||||||
|
func fib(x int) int {
|
||||||
|
if x < 2 {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
return fib(x-1) - fib(x-2)
|
||||||
|
}`
|
||||||
|
f, err := parseSrc("fib.go", input)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type-check the package.
|
||||||
|
// We create an empty map for each kind of input
|
||||||
|
// we're interested in, and Check populates them.
|
||||||
|
info := types2.Info{
|
||||||
|
Types: make(map[syntax.Expr]types2.TypeAndValue),
|
||||||
|
Defs: make(map[*syntax.Name]types2.Object),
|
||||||
|
Uses: make(map[*syntax.Name]types2.Object),
|
||||||
|
}
|
||||||
|
var conf types2.Config
|
||||||
|
pkg, err := conf.Check("fib", []*syntax.File{f}, &info)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print package-level variables in initialization order.
|
||||||
|
fmt.Printf("InitOrder: %v\n\n", info.InitOrder)
|
||||||
|
|
||||||
|
// For each named object, print the line and
|
||||||
|
// column of its definition and each of its uses.
|
||||||
|
fmt.Println("Defs and Uses of each named object:")
|
||||||
|
usesByObj := make(map[types2.Object][]string)
|
||||||
|
for id, obj := range info.Uses {
|
||||||
|
posn := id.Pos()
|
||||||
|
lineCol := fmt.Sprintf("%d:%d", posn.Line(), posn.Col())
|
||||||
|
usesByObj[obj] = append(usesByObj[obj], lineCol)
|
||||||
|
}
|
||||||
|
var items []string
|
||||||
|
for obj, uses := range usesByObj {
|
||||||
|
sort.Strings(uses)
|
||||||
|
item := fmt.Sprintf("%s:\n defined at %s\n used at %s",
|
||||||
|
types2.ObjectString(obj, types2.RelativeTo(pkg)),
|
||||||
|
obj.Pos(),
|
||||||
|
strings.Join(uses, ", "))
|
||||||
|
items = append(items, item)
|
||||||
|
}
|
||||||
|
sort.Strings(items) // sort by line:col, in effect
|
||||||
|
fmt.Println(strings.Join(items, "\n"))
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
// TODO(gri) Enable once positions are updated/verified
|
||||||
|
// fmt.Println("Types and Values of each expression:")
|
||||||
|
// items = nil
|
||||||
|
// for expr, tv := range info.Types {
|
||||||
|
// var buf bytes.Buffer
|
||||||
|
// posn := expr.Pos()
|
||||||
|
// tvstr := tv.Type.String()
|
||||||
|
// if tv.Value != nil {
|
||||||
|
// tvstr += " = " + tv.Value.String()
|
||||||
|
// }
|
||||||
|
// // line:col | expr | mode : type = value
|
||||||
|
// fmt.Fprintf(&buf, "%2d:%2d | %-19s | %-7s : %s",
|
||||||
|
// posn.Line(), posn.Col(), types2.ExprString(expr),
|
||||||
|
// mode(tv), tvstr)
|
||||||
|
// items = append(items, buf.String())
|
||||||
|
// }
|
||||||
|
// sort.Strings(items)
|
||||||
|
// fmt.Println(strings.Join(items, "\n"))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// InitOrder: [c = "hello" b = S(c) a = len(b)]
|
||||||
|
//
|
||||||
|
// Defs and Uses of each named object:
|
||||||
|
// builtin len:
|
||||||
|
// defined at <unknown position>
|
||||||
|
// used at 6:15
|
||||||
|
// func fib(x int) int:
|
||||||
|
// defined at fib.go:8:6
|
||||||
|
// used at 12:20, 12:9
|
||||||
|
// type S string:
|
||||||
|
// defined at fib.go:4:6
|
||||||
|
// used at 6:23
|
||||||
|
// type int:
|
||||||
|
// defined at <unknown position>
|
||||||
|
// used at 8:12, 8:17
|
||||||
|
// type string:
|
||||||
|
// defined at <unknown position>
|
||||||
|
// used at 4:8
|
||||||
|
// var b S:
|
||||||
|
// defined at fib.go:6:8
|
||||||
|
// used at 6:19
|
||||||
|
// var c string:
|
||||||
|
// defined at fib.go:6:11
|
||||||
|
// used at 6:25
|
||||||
|
// var x int:
|
||||||
|
// defined at fib.go:8:10
|
||||||
|
// used at 10:10, 12:13, 12:24, 9:5
|
||||||
|
|
||||||
|
// TODO(gri) Enable once positions are updated/verified
|
||||||
|
// Types and Values of each expression:
|
||||||
|
// 4: 8 | string | type : string
|
||||||
|
// 6:15 | len | builtin : func(string) int
|
||||||
|
// 6:15 | len(b) | value : int
|
||||||
|
// 6:19 | b | var : fib.S
|
||||||
|
// 6:23 | S | type : fib.S
|
||||||
|
// 6:23 | S(c) | value : fib.S
|
||||||
|
// 6:25 | c | var : string
|
||||||
|
// 6:29 | "hello" | value : string = "hello"
|
||||||
|
// 8:12 | int | type : int
|
||||||
|
// 8:17 | int | type : int
|
||||||
|
// 9: 5 | x | var : int
|
||||||
|
// 9: 5 | x < 2 | value : untyped bool
|
||||||
|
// 9: 9 | 2 | value : int = 2
|
||||||
|
// 10:10 | x | var : int
|
||||||
|
// 12: 9 | fib | value : func(x int) int
|
||||||
|
// 12: 9 | fib(x - 1) | value : int
|
||||||
|
// 12: 9 | fib(x - 1) - fib(x - 2) | value : int
|
||||||
|
// 12:13 | x | var : int
|
||||||
|
// 12:13 | x - 1 | value : int
|
||||||
|
// 12:15 | 1 | value : int = 1
|
||||||
|
// 12:20 | fib | value : func(x int) int
|
||||||
|
// 12:20 | fib(x - 2) | value : int
|
||||||
|
// 12:24 | x | var : int
|
||||||
|
// 12:24 | x - 2 | value : int
|
||||||
|
// 12:26 | 2 | value : int = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func mode(tv types2.TypeAndValue) string {
|
||||||
|
switch {
|
||||||
|
case tv.IsVoid():
|
||||||
|
return "void"
|
||||||
|
case tv.IsType():
|
||||||
|
return "type"
|
||||||
|
case tv.IsBuiltin():
|
||||||
|
return "builtin"
|
||||||
|
case tv.IsNil():
|
||||||
|
return "nil"
|
||||||
|
case tv.Assignable():
|
||||||
|
if tv.Addressable() {
|
||||||
|
return "var"
|
||||||
|
}
|
||||||
|
return "mapindex"
|
||||||
|
case tv.IsValue():
|
||||||
|
return "value"
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
216
src/cmd/compile/internal/types2/examples/functions.go2
Normal file
216
src/cmd/compile/internal/types2/examples/functions.go2
Normal file
|
|
@ -0,0 +1,216 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file shows some examples of type-parameterized functions.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
// Reverse is a generic function that takes a []T argument and
|
||||||
|
// reverses that slice in place.
|
||||||
|
func Reverse[T any](list []T) {
|
||||||
|
i := 0
|
||||||
|
j := len(list)-1
|
||||||
|
for i < j {
|
||||||
|
list[i], list[j] = list[j], list[i]
|
||||||
|
i++
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// Reverse can be called with an explicit type argument.
|
||||||
|
Reverse[int](nil)
|
||||||
|
Reverse[string]([]string{"foo", "bar"})
|
||||||
|
Reverse[struct{x, y int}]([]struct{x, y int}{{1, 2}, {2, 3}, {3, 4}})
|
||||||
|
|
||||||
|
// Since the type parameter is used for an incoming argument,
|
||||||
|
// it can be inferred from the provided argument's type.
|
||||||
|
Reverse([]string{"foo", "bar"})
|
||||||
|
Reverse([]struct{x, y int}{{1, 2}, {2, 3}, {3, 4}})
|
||||||
|
|
||||||
|
// But the incoming argument must have a type, even if it's a
|
||||||
|
// default type. An untyped nil won't work.
|
||||||
|
// Reverse(nil) // this won't type-check
|
||||||
|
|
||||||
|
// A typed nil will work, though.
|
||||||
|
Reverse([]int(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Certain functions, such as the built-in `new` could be written using
|
||||||
|
// type parameters.
|
||||||
|
func new[T any]() *T {
|
||||||
|
var x T
|
||||||
|
return &x
|
||||||
|
}
|
||||||
|
|
||||||
|
// When calling our own `new`, we need to pass the type parameter
|
||||||
|
// explicitly since there is no (value) argument from which the
|
||||||
|
// result type could be inferred. We don't try to infer the
|
||||||
|
// result type from the assignment to keep things simple and
|
||||||
|
// easy to understand.
|
||||||
|
var _ = new[int]()
|
||||||
|
var _ *float64 = new[float64]() // the result type is indeed *float64
|
||||||
|
|
||||||
|
// A function may have multiple type parameters, of course.
|
||||||
|
func foo[A, B, C any](a A, b []B, c *C) B {
|
||||||
|
// do something here
|
||||||
|
return b[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// As before, we can pass type parameters explicitly.
|
||||||
|
var s = foo[int, string, float64](1, []string{"first"}, new[float64]())
|
||||||
|
|
||||||
|
// Or we can use type inference.
|
||||||
|
var _ float64 = foo(42, []float64{1.0}, &s)
|
||||||
|
|
||||||
|
// Type inference works in a straight-forward manner even
|
||||||
|
// for variadic functions.
|
||||||
|
func variadic[A, B any](A, B, ...B) int
|
||||||
|
|
||||||
|
// var _ = variadic(1) // ERROR not enough arguments
|
||||||
|
var _ = variadic(1, 2.3)
|
||||||
|
var _ = variadic(1, 2.3, 3.4, 4.5)
|
||||||
|
var _ = variadic[int, float64](1, 2.3, 3.4, 4)
|
||||||
|
|
||||||
|
// Type inference also works in recursive function calls where
|
||||||
|
// the inferred type is the type parameter of the caller.
|
||||||
|
func f1[T any](x T) {
|
||||||
|
f1(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func f2a[T any](x, y T) {
|
||||||
|
f2a(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func f2b[T any](x, y T) {
|
||||||
|
f2b(y, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func g2a[P, Q any](x P, y Q) {
|
||||||
|
g2a(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func g2b[P, Q any](x P, y Q) {
|
||||||
|
g2b(y, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here's an example of a recursive function call with variadic
|
||||||
|
// arguments and type inference inferring the type parameter of
|
||||||
|
// the caller (i.e., itself).
|
||||||
|
func max[T interface{ type int }](x ...T) T {
|
||||||
|
var x0 T
|
||||||
|
if len(x) > 0 {
|
||||||
|
x0 = x[0]
|
||||||
|
}
|
||||||
|
if len(x) > 1 {
|
||||||
|
x1 := max(x[1:]...)
|
||||||
|
if x1 > x0 {
|
||||||
|
return x1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x0
|
||||||
|
}
|
||||||
|
|
||||||
|
// When inferring channel types, the channel direction is ignored
|
||||||
|
// for the purpose of type inference. Once the type has been in-
|
||||||
|
// fered, the usual parameter passing rules are applied.
|
||||||
|
// Thus even if a type can be inferred successfully, the function
|
||||||
|
// call may not be valid.
|
||||||
|
|
||||||
|
func fboth[T any](chan T)
|
||||||
|
func frecv[T any](<-chan T)
|
||||||
|
func fsend[T any](chan<- T)
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var both chan int
|
||||||
|
var recv <-chan int
|
||||||
|
var send chan<-int
|
||||||
|
|
||||||
|
fboth(both)
|
||||||
|
fboth(recv /* ERROR cannot use */ )
|
||||||
|
fboth(send /* ERROR cannot use */ )
|
||||||
|
|
||||||
|
frecv(both)
|
||||||
|
frecv(recv)
|
||||||
|
frecv(send /* ERROR cannot use */ )
|
||||||
|
|
||||||
|
fsend(both)
|
||||||
|
fsend(recv /* ERROR cannot use */)
|
||||||
|
fsend(send)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ffboth[T any](func(chan T))
|
||||||
|
func ffrecv[T any](func(<-chan T))
|
||||||
|
func ffsend[T any](func(chan<- T))
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var both func(chan int)
|
||||||
|
var recv func(<-chan int)
|
||||||
|
var send func(chan<- int)
|
||||||
|
|
||||||
|
ffboth(both)
|
||||||
|
ffboth(recv /* ERROR cannot use */ )
|
||||||
|
ffboth(send /* ERROR cannot use */ )
|
||||||
|
|
||||||
|
ffrecv(both /* ERROR cannot use */ )
|
||||||
|
ffrecv(recv)
|
||||||
|
ffrecv(send /* ERROR cannot use */ )
|
||||||
|
|
||||||
|
ffsend(both /* ERROR cannot use */ )
|
||||||
|
ffsend(recv /* ERROR cannot use */ )
|
||||||
|
ffsend(send)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When inferring elements of unnamed composite parameter types,
|
||||||
|
// if the arguments are defined types, use their underlying types.
|
||||||
|
// Even though the matching types are not exactly structurally the
|
||||||
|
// same (one is a type literal, the other a named type), because
|
||||||
|
// assignment is permitted, parameter passing is permitted as well,
|
||||||
|
// so type inference should be able to handle these cases well.
|
||||||
|
|
||||||
|
func g1[T any]([]T)
|
||||||
|
func g2[T any]([]T, T)
|
||||||
|
func g3[T any](*T, ...T)
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
type intSlize []int
|
||||||
|
g1([]int{})
|
||||||
|
g1(intSlize{})
|
||||||
|
g2(nil, 0)
|
||||||
|
|
||||||
|
type myString string
|
||||||
|
var s1 string
|
||||||
|
g3(nil, "1", myString("2"), "3")
|
||||||
|
g3(&s1, "1", myString /* ERROR does not match */ ("2"), "3")
|
||||||
|
_ = s1
|
||||||
|
|
||||||
|
type myStruct struct{x int}
|
||||||
|
var s2 myStruct
|
||||||
|
g3(nil, struct{x int}{}, myStruct{})
|
||||||
|
g3(&s2, struct{x int}{}, myStruct{})
|
||||||
|
g3(nil, myStruct{}, struct{x int}{})
|
||||||
|
g3(&s2, myStruct{}, struct{x int}{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here's a realistic example.
|
||||||
|
|
||||||
|
func append[T any](s []T, t ...T) []T
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var f func()
|
||||||
|
type Funcs []func()
|
||||||
|
var funcs Funcs
|
||||||
|
_ = append(funcs, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic type declarations cannot have empty type parameter lists
|
||||||
|
// (that would indicate a slice type). Thus, generic functions cannot
|
||||||
|
// have empty type parameter lists, either. This is a syntax error.
|
||||||
|
|
||||||
|
func h[] /* ERROR empty type parameter list */ ()
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
h[] /* ERROR operand */ ()
|
||||||
|
}
|
||||||
97
src/cmd/compile/internal/types2/examples/methods.go2
Normal file
97
src/cmd/compile/internal/types2/examples/methods.go2
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file shows some examples of methods on type-parameterized types.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
// Parameterized types may have methods.
|
||||||
|
type T1[A any] struct{ a A }
|
||||||
|
|
||||||
|
// When declaring a method for a parameterized type, the "instantiated"
|
||||||
|
// receiver type acts as an implicit declaration of the type parameters
|
||||||
|
// for the receiver type. In the example below, method m1 on type T1 has
|
||||||
|
// the receiver type T1[A] which declares the type parameter A for use
|
||||||
|
// with this method. That is, within the method m1, A stands for the
|
||||||
|
// actual type argument provided to an instantiated T1.
|
||||||
|
func (t T1[A]) m1() A { return t.a }
|
||||||
|
|
||||||
|
// For instance, if T1 is instantiated with the type int, the type
|
||||||
|
// parameter A in m1 assumes that type (int) as well and we can write
|
||||||
|
// code like this:
|
||||||
|
var x T1[int]
|
||||||
|
var _ int = x.m1()
|
||||||
|
|
||||||
|
// Because the type parameter provided to a parameterized receiver type
|
||||||
|
// is declared through that receiver declaration, it must be an identifier.
|
||||||
|
// It cannot possibly be some other type because the receiver type is not
|
||||||
|
// instantiated with concrete types, it is standing for the parameterized
|
||||||
|
// receiver type.
|
||||||
|
func (t T1[[ /* ERROR must be an identifier */ ]int]) m2() {}
|
||||||
|
|
||||||
|
// Note that using what looks like a predeclared identifier, say int,
|
||||||
|
// as type parameter in this situation is deceptive and considered bad
|
||||||
|
// style. In m3 below, int is the name of the local receiver type parameter
|
||||||
|
// and it shadows the predeclared identifier int which then cannot be used
|
||||||
|
// anymore as expected.
|
||||||
|
// This is no different from locally redelaring a predeclared identifier
|
||||||
|
// and usually should be avoided. There are some notable exceptions; e.g.,
|
||||||
|
// sometimes it makes sense to use the identifier "copy" which happens to
|
||||||
|
// also be the name of a predeclared built-in function.
|
||||||
|
func (t T1[int]) m3() { var _ int = 42 /* ERROR cannot convert 42 .* to int */ }
|
||||||
|
|
||||||
|
// The names of the type parameters used in a parameterized receiver
|
||||||
|
// type don't have to match the type parameter names in the the declaration
|
||||||
|
// of the type used for the receiver. In our example, even though T1 is
|
||||||
|
// declared with type parameter named A, methods using that receiver type
|
||||||
|
// are free to use their own name for that type parameter. That is, the
|
||||||
|
// name of type parameters is always local to the declaration where they
|
||||||
|
// are introduced. In our example we can write a method m2 and use the
|
||||||
|
// name X instead of A for the type parameter w/o any difference.
|
||||||
|
func (t T1[X]) m4() X { return t.a }
|
||||||
|
|
||||||
|
// If the receiver type is parameterized, type parameters must always be
|
||||||
|
// provided: this simply follows from the general rule that a parameterized
|
||||||
|
// type must be instantiated before it can be used. A method receiver
|
||||||
|
// declaration using a parameterized receiver type is no exception. It is
|
||||||
|
// simply that such receiver type expressions perform two tasks simultaneously:
|
||||||
|
// they declare the (local) type parameters and then use them to instantiate
|
||||||
|
// the receiver type. Forgetting to provide a type parameter leads to an error.
|
||||||
|
func (t T1 /* ERROR generic type .* without instantiation */ ) m5() {}
|
||||||
|
|
||||||
|
// However, sometimes we don't need the type parameter, and thus it is
|
||||||
|
// inconvenient to have to choose a name. Since the receiver type expression
|
||||||
|
// serves as a declaration for its type parameters, we are free to choose the
|
||||||
|
// blank identifier:
|
||||||
|
func (t T1[_]) m6() {}
|
||||||
|
|
||||||
|
// Naturally, these rules apply to any number of type parameters on the receiver
|
||||||
|
// type. Here are some more complex examples.
|
||||||
|
type T2[A, B, C any] struct {
|
||||||
|
a A
|
||||||
|
b B
|
||||||
|
c C
|
||||||
|
}
|
||||||
|
|
||||||
|
// Naming of the type parameters is local and has no semantic impact:
|
||||||
|
func (t T2[A, B, C]) m1() (A, B, C) { return t.a, t.b, t.c }
|
||||||
|
func (t T2[C, B, A]) m2() (C, B, A) { return t.a, t.b, t.c }
|
||||||
|
func (t T2[X, Y, Z]) m3() (X, Y, Z) { return t.a, t.b, t.c }
|
||||||
|
|
||||||
|
// Type parameters may be left blank if they are not needed:
|
||||||
|
func (t T2[A, _, C]) m4() (A, C) { return t.a, t.c }
|
||||||
|
func (t T2[_, _, X]) m5() X { return t.c }
|
||||||
|
func (t T2[_, _, _]) m6() {}
|
||||||
|
|
||||||
|
// As usual, blank names may be used for any object which we don't care about
|
||||||
|
// using later. For instance, we may write an unnamed method with a receiver
|
||||||
|
// that cannot be accessed:
|
||||||
|
func (_ T2[_, _, _]) _() int { return 42 }
|
||||||
|
|
||||||
|
// Because a receiver parameter list is simply a parameter list, we can
|
||||||
|
// leave the receiver argument away for receiver types.
|
||||||
|
type T0 struct{}
|
||||||
|
func (T0) _() {}
|
||||||
|
func (T1[A]) _() {}
|
||||||
280
src/cmd/compile/internal/types2/examples/types.go2
Normal file
280
src/cmd/compile/internal/types2/examples/types.go2
Normal file
|
|
@ -0,0 +1,280 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file shows some examples of generic types.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
// List is just what it says - a slice of E elements.
|
||||||
|
type List[E any] []E
|
||||||
|
|
||||||
|
// A generic (parameterized) type must always be instantiated
|
||||||
|
// before it can be used to designate the type of a variable
|
||||||
|
// (including a struct field, or function parameter); though
|
||||||
|
// for the latter cases, the provided type may be another type
|
||||||
|
// parameter. So:
|
||||||
|
var _ List[byte] = []byte{}
|
||||||
|
|
||||||
|
// A generic binary tree might be declared as follows.
|
||||||
|
type Tree[E any] struct {
|
||||||
|
left, right *Tree[E]
|
||||||
|
payload E
|
||||||
|
}
|
||||||
|
|
||||||
|
// A simple instantiation of Tree:
|
||||||
|
var root1 Tree[int]
|
||||||
|
|
||||||
|
// The actual type parameter provided may be a generic type itself:
|
||||||
|
var root2 Tree[List[int]]
|
||||||
|
|
||||||
|
// A couple of more complex examples.
|
||||||
|
// We don't need extra parentheses around the element type of the slices on
|
||||||
|
// the right (unlike when we use ()'s rather than []'s for type parameters).
|
||||||
|
var _ List[List[int]] = []List[int]{}
|
||||||
|
var _ List[List[List[Tree[int]]]] = []List[List[Tree[int]]]{}
|
||||||
|
|
||||||
|
// Type parameters act like type aliases when used in generic types
|
||||||
|
// in the sense that we can "emulate" a specific type instantiation
|
||||||
|
// with type aliases.
|
||||||
|
type T1[P any] struct {
|
||||||
|
f P
|
||||||
|
}
|
||||||
|
|
||||||
|
type T2[P any] struct {
|
||||||
|
f struct {
|
||||||
|
g P
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var x1 T1[struct{ g int }]
|
||||||
|
var x2 T2[int]
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// This assignment is invalid because the types of x1, x2 are T1(...)
|
||||||
|
// and T2(...) respectively, which are two different defined types.
|
||||||
|
x1 = x2 // ERROR assignment
|
||||||
|
|
||||||
|
// This assignment is valid because the types of x1.f and x2.f are
|
||||||
|
// both struct { g int }; the type parameters act like type aliases
|
||||||
|
// and their actual names don't come into play here.
|
||||||
|
x1.f = x2.f
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can verify this behavior using type aliases instead:
|
||||||
|
type T1a struct {
|
||||||
|
f A1
|
||||||
|
}
|
||||||
|
type A1 = struct { g int }
|
||||||
|
|
||||||
|
type T2a struct {
|
||||||
|
f struct {
|
||||||
|
g A2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type A2 = int
|
||||||
|
|
||||||
|
var x1a T1a
|
||||||
|
var x2a T2a
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
x1a = x2a // ERROR assignment
|
||||||
|
x1a.f = x2a.f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Another interesting corner case are generic types that don't use
|
||||||
|
// their type arguments. For instance:
|
||||||
|
type T[P any] struct{}
|
||||||
|
|
||||||
|
var xint T[int]
|
||||||
|
var xbool T[bool]
|
||||||
|
|
||||||
|
// Are these two variables of the same type? After all, their underlying
|
||||||
|
// types are identical. We consider them to be different because each type
|
||||||
|
// instantiation creates a new named type, in this case T<int> and T<bool>
|
||||||
|
// even if their underlying types are identical. This is sensible because
|
||||||
|
// we might still have methods that have different signatures or behave
|
||||||
|
// differently depending on the type arguments, and thus we can't possibly
|
||||||
|
// consider such types identical. Consequently:
|
||||||
|
func _() {
|
||||||
|
xint = xbool // ERROR assignment
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic types cannot be used without instantiation.
|
||||||
|
var _ T // ERROR cannot use generic type T
|
||||||
|
|
||||||
|
// In type context, generic (parameterized) types cannot be parenthesized before
|
||||||
|
// being instantiated. See also NOTES entry from 12/4/2019.
|
||||||
|
var _ (T /* ERROR cannot use generic type T */ )[ /* ERROR unexpected \[ */ int]
|
||||||
|
|
||||||
|
// All types may be parameterized, including interfaces.
|
||||||
|
type I1[T any] interface{
|
||||||
|
m1(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is no such thing as a variadic generic type.
|
||||||
|
type _[T ... /* ERROR invalid use of ... */ interface{}] struct{}
|
||||||
|
|
||||||
|
// Generic interfaces may be embedded as one would expect.
|
||||||
|
type I2 interface {
|
||||||
|
I1(int) // method!
|
||||||
|
I1[string] // embedded I1
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var x I2
|
||||||
|
x.I1(0)
|
||||||
|
x.m1("foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
type I0 interface {
|
||||||
|
m0()
|
||||||
|
}
|
||||||
|
|
||||||
|
type I3 interface {
|
||||||
|
I0
|
||||||
|
I1[bool]
|
||||||
|
m(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var x I3
|
||||||
|
x.m0()
|
||||||
|
x.m1(true)
|
||||||
|
x.m("foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
type _ struct {
|
||||||
|
( /* ERROR cannot parenthesize */ int8)
|
||||||
|
( /* ERROR cannot parenthesize */ *int16)
|
||||||
|
*( /* ERROR cannot parenthesize */ int32)
|
||||||
|
List[int]
|
||||||
|
|
||||||
|
int8 /* ERROR int8 redeclared */
|
||||||
|
* /* ERROR int16 redeclared */ int16
|
||||||
|
List /* ERROR List redeclared */ [int]
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's possible to declare local types whose underlying types
|
||||||
|
// are type parameters. As with ordinary type definitions, the
|
||||||
|
// types underlying properties are "inherited" but the methods
|
||||||
|
// are not.
|
||||||
|
func _[T interface{ m(); type int }]() {
|
||||||
|
type L T
|
||||||
|
var x L
|
||||||
|
|
||||||
|
// m is not defined on L (it is not "inherited" from
|
||||||
|
// its underlying type).
|
||||||
|
x.m /* ERROR x.m undefined */ ()
|
||||||
|
|
||||||
|
// But the properties of T, such that as that it supports
|
||||||
|
// the operations of the types given by its type bound,
|
||||||
|
// are also the properties of L.
|
||||||
|
x++
|
||||||
|
_ = x - x
|
||||||
|
|
||||||
|
// On the other hand, if we define a local alias for T,
|
||||||
|
// that alias stands for T as expected.
|
||||||
|
type A = T
|
||||||
|
var y A
|
||||||
|
y.m()
|
||||||
|
_ = y < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// As a special case, an explicit type argument may be omitted
|
||||||
|
// from a type parameter bound if the type bound expects exactly
|
||||||
|
// one type argument. In that case, the type argument is the
|
||||||
|
// respective type parameter to which the type bound applies.
|
||||||
|
// Note: We may not permit this syntactic sugar at first.
|
||||||
|
// Note: This is now disabled. All examples below are adjusted.
|
||||||
|
type Adder[T any] interface {
|
||||||
|
Add(T) T
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't need to explicitly instantiate the Adder bound
|
||||||
|
// if we have exactly one type parameter.
|
||||||
|
func Sum[T Adder[T]](list []T) T {
|
||||||
|
var sum T
|
||||||
|
for _, x := range list {
|
||||||
|
sum = sum.Add(x)
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid and invalid variations.
|
||||||
|
type B0 interface {}
|
||||||
|
type B1[_ any] interface{}
|
||||||
|
type B2[_, _ any] interface{}
|
||||||
|
|
||||||
|
func _[T1 B0]()
|
||||||
|
func _[T1 B1[T1]]()
|
||||||
|
func _[T1 B2 /* ERROR cannot use generic type .* without instantiation */ ]()
|
||||||
|
|
||||||
|
func _[T1, T2 B0]()
|
||||||
|
func _[T1 B1[T1], T2 B1[T2]]()
|
||||||
|
func _[T1, T2 B2 /* ERROR cannot use generic type .* without instantiation */ ]()
|
||||||
|
|
||||||
|
func _[T1 B0, T2 B1[T2]]() // here B1 applies to T2
|
||||||
|
|
||||||
|
// When the type argument is left away, the type bound is
|
||||||
|
// instantiated for each type parameter with that type
|
||||||
|
// parameter.
|
||||||
|
// Note: We may not permit this syntactic sugar at first.
|
||||||
|
func _[A Adder[A], B Adder[B], C Adder[A]]() {
|
||||||
|
var a A // A's type bound is Adder[A]
|
||||||
|
a = a.Add(a)
|
||||||
|
var b B // B's type bound is Adder[B]
|
||||||
|
b = b.Add(b)
|
||||||
|
var c C // C's type bound is Adder[A]
|
||||||
|
a = c.Add(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The type of variables (incl. parameters and return values) cannot
|
||||||
|
// be an interface with type constraints or be/embed comparable.
|
||||||
|
type I interface {
|
||||||
|
type int
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ interface /* ERROR contains type constraints */ {type int}
|
||||||
|
_ I /* ERROR contains type constraints */
|
||||||
|
)
|
||||||
|
|
||||||
|
func _(I /* ERROR contains type constraints */ )
|
||||||
|
func _(x, y, z I /* ERROR contains type constraints */ )
|
||||||
|
func _() I /* ERROR contains type constraints */
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var _ I /* ERROR contains type constraints */
|
||||||
|
}
|
||||||
|
|
||||||
|
type C interface {
|
||||||
|
comparable
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ comparable /* ERROR comparable */
|
||||||
|
var _ C /* ERROR comparable */
|
||||||
|
|
||||||
|
func _(_ comparable /* ERROR comparable */ , _ C /* ERROR comparable */ )
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var _ comparable /* ERROR comparable */
|
||||||
|
var _ C /* ERROR comparable */
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type parameters are never const types, i.e., it's
|
||||||
|
// not possible to declare a constant of type parameter type.
|
||||||
|
// (If a type list contains just a single const type, we could
|
||||||
|
// allow it, but such type lists don't make much sense in the
|
||||||
|
// first place.)
|
||||||
|
func _[T interface { type int, float64 }]() {
|
||||||
|
// not valid
|
||||||
|
const _ = T /* ERROR not constant */ (0)
|
||||||
|
const _ T /* ERROR invalid constant type T */ = 1
|
||||||
|
|
||||||
|
// valid
|
||||||
|
var _ = T(0)
|
||||||
|
var _ T = 1
|
||||||
|
_ = T(0)
|
||||||
|
}
|
||||||
2020
src/cmd/compile/internal/types2/expr.go
Normal file
2020
src/cmd/compile/internal/types2/expr.go
Normal file
File diff suppressed because it is too large
Load diff
12
src/cmd/compile/internal/types2/fixedbugs/issue20583.src
Normal file
12
src/cmd/compile/internal/types2/fixedbugs/issue20583.src
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package issue20583
|
||||||
|
const (
|
||||||
|
_ = 6e886451608 /* ERROR malformed constant */ /2
|
||||||
|
_ = 6e886451608i /* ERROR malformed constant */ /2
|
||||||
|
_ = 0 * 1e+1000000000 // ERROR malformed constant
|
||||||
|
x = 1e100000000
|
||||||
|
_ = x*x*x*x*x*x* /* ERROR not representable */ x
|
||||||
|
)
|
||||||
92
src/cmd/compile/internal/types2/fixedbugs/issue39634.go2
Normal file
92
src/cmd/compile/internal/types2/fixedbugs/issue39634.go2
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Examples adjusted to match new [T any] syntax for type parameters.
|
||||||
|
// Also, previously permitted empty type parameter lists and instantiations
|
||||||
|
// are now syntax errors.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
// crash 1
|
||||||
|
type nt1[_ any]interface{g /* ERROR undeclared name */ }
|
||||||
|
type ph1[e nt1[e],g(d /* ERROR undeclared name */ )]s /* ERROR undeclared name */
|
||||||
|
func(*ph1[e,e /* ERROR redeclared */ ])h(d /* ERROR undeclared name */ )
|
||||||
|
|
||||||
|
// crash 2
|
||||||
|
// Disabled: empty []'s are now syntax errors. This example leads to too many follow-on errors.
|
||||||
|
// type Numeric2 interface{t2 /* ERROR not a type */ }
|
||||||
|
// func t2[T Numeric2](s[]T){0 /* ERROR not a type */ []{s /* ERROR cannot index */ [0][0]}}
|
||||||
|
|
||||||
|
// crash 3
|
||||||
|
type t3 *interface{ t3.p /* ERROR no field or method p */ }
|
||||||
|
|
||||||
|
// crash 4
|
||||||
|
type Numeric4 interface{t4 /* ERROR not a type */ }
|
||||||
|
func t4[T Numeric4](s[]T){if( /* ERROR non-boolean */ 0){*s /* ERROR cannot indirect */ [0]}}
|
||||||
|
|
||||||
|
// crash 7
|
||||||
|
type foo7 interface { bar() }
|
||||||
|
type x7[A any] struct{ foo7 }
|
||||||
|
func main7() { var _ foo7 = x7[int]{} }
|
||||||
|
|
||||||
|
// crash 8
|
||||||
|
type foo8[A any] interface { type A }
|
||||||
|
func bar8[A foo8[A]](a A) {}
|
||||||
|
func main8() {}
|
||||||
|
|
||||||
|
// crash 9
|
||||||
|
type foo9[A any] interface { type foo9 /* ERROR interface contains type constraints */ [A] }
|
||||||
|
func _() { var _ = new(foo9 /* ERROR interface contains type constraints */ [int]) }
|
||||||
|
|
||||||
|
// crash 12
|
||||||
|
var u /* ERROR cycle */ , i [func /* ERROR used as value */ /* ERROR used as value */ (u, c /* ERROR undeclared */ /* ERROR undeclared */ ) {}(0, len)]c /* ERROR undeclared */ /* ERROR undeclared */
|
||||||
|
|
||||||
|
// crash 15
|
||||||
|
func y15() { var a /* ERROR declared but not used */ interface{ p() } = G15[string]{} }
|
||||||
|
type G15[X any] s /* ERROR undeclared name */
|
||||||
|
func (G15 /* ERROR generic type .* without instantiation */ ) p()
|
||||||
|
|
||||||
|
// crash 16
|
||||||
|
type Foo16[T any] r16 /* ERROR not a type */
|
||||||
|
func r16[T any]() Foo16[Foo16[T]]
|
||||||
|
|
||||||
|
// crash 17
|
||||||
|
type Y17 interface{ c() }
|
||||||
|
type Z17 interface {
|
||||||
|
c() Y17
|
||||||
|
Y17 /* ERROR duplicate method */
|
||||||
|
}
|
||||||
|
func F17[T Z17](T)
|
||||||
|
|
||||||
|
// crash 18
|
||||||
|
type o18[T any] []func(_ o18[[]_ /* ERROR cannot use _ */ ])
|
||||||
|
|
||||||
|
// crash 19
|
||||||
|
type Z19 [][[]Z19{}[0][0]]c19 /* ERROR undeclared */
|
||||||
|
|
||||||
|
// crash 20
|
||||||
|
type Z20 /* ERROR illegal cycle */ interface{ Z20 }
|
||||||
|
func F20[t Z20]() { F20(t /* ERROR invalid composite literal type */ {}) }
|
||||||
|
|
||||||
|
// crash 21
|
||||||
|
type Z21 /* ERROR illegal cycle */ interface{ Z21 }
|
||||||
|
func F21[T Z21]() { ( /* ERROR not used */ F21[Z21]) }
|
||||||
|
|
||||||
|
// crash 24
|
||||||
|
type T24[P any] P
|
||||||
|
func (r T24[P]) m() { T24 /* ERROR without instantiation */ .m() }
|
||||||
|
|
||||||
|
// crash 25
|
||||||
|
type T25[A any] int
|
||||||
|
func (t T25[A]) m1() {}
|
||||||
|
var x T25 /* ERROR without instantiation */ .m1
|
||||||
|
|
||||||
|
// crash 26
|
||||||
|
type T26 = interface{ F26[ /* ERROR cannot have type parameters */ Z any]() }
|
||||||
|
func F26[Z any]() T26 { return F26 /* ERROR without instantiation */ /* ERROR missing method */ [] /* ERROR operand */ }
|
||||||
|
|
||||||
|
// crash 27
|
||||||
|
func e27[T any]() interface{ x27 /* ERROR not a type */ }
|
||||||
|
func x27() { e27( /* ERROR cannot infer T */ ) }
|
||||||
16
src/cmd/compile/internal/types2/fixedbugs/issue39664.go2
Normal file
16
src/cmd/compile/internal/types2/fixedbugs/issue39664.go2
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
type T[_ any] struct {}
|
||||||
|
|
||||||
|
func (T /* ERROR instantiation */ ) m()
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
var x interface { m() }
|
||||||
|
x = T[int]{}
|
||||||
|
_ = x
|
||||||
|
}
|
||||||
28
src/cmd/compile/internal/types2/fixedbugs/issue39680.go2
Normal file
28
src/cmd/compile/internal/types2/fixedbugs/issue39680.go2
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Minimal test case.
|
||||||
|
func _[T interface{type T}](x T) T{
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case from issue.
|
||||||
|
type constr[T any] interface {
|
||||||
|
type T
|
||||||
|
}
|
||||||
|
|
||||||
|
func Print[T constr[T]](s []T) {
|
||||||
|
for _, v := range s {
|
||||||
|
fmt.Print(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func f() {
|
||||||
|
Print([]string{"Hello, ", "playground\n"})
|
||||||
|
}
|
||||||
15
src/cmd/compile/internal/types2/fixedbugs/issue39693.go2
Normal file
15
src/cmd/compile/internal/types2/fixedbugs/issue39693.go2
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
type Number interface {
|
||||||
|
int /* ERROR int is not an interface */
|
||||||
|
float64 /* ERROR float64 is not an interface */
|
||||||
|
}
|
||||||
|
|
||||||
|
func Add[T Number](a, b T) T {
|
||||||
|
return a /* ERROR not defined */ + b
|
||||||
|
}
|
||||||
30
src/cmd/compile/internal/types2/fixedbugs/issue39699.go2
Normal file
30
src/cmd/compile/internal/types2/fixedbugs/issue39699.go2
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
type T0 interface{
|
||||||
|
}
|
||||||
|
|
||||||
|
type T1 interface{
|
||||||
|
type int
|
||||||
|
}
|
||||||
|
|
||||||
|
type T2 interface{
|
||||||
|
comparable
|
||||||
|
}
|
||||||
|
|
||||||
|
type T3 interface {
|
||||||
|
T0
|
||||||
|
T1
|
||||||
|
T2
|
||||||
|
}
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
_ = T0(0)
|
||||||
|
_ = T1 /* ERROR cannot use interface T1 in conversion */ (1)
|
||||||
|
_ = T2 /* ERROR cannot use interface T2 in conversion */ (2)
|
||||||
|
_ = T3 /* ERROR cannot use interface T3 in conversion */ (3)
|
||||||
|
}
|
||||||
12
src/cmd/compile/internal/types2/fixedbugs/issue39711.go2
Normal file
12
src/cmd/compile/internal/types2/fixedbugs/issue39711.go2
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
// UNREVIEWED
|
||||||
|
// Copyright 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package p
|
||||||
|
|
||||||
|
// Do not report a duplicate type error for this type list.
|
||||||
|
// (Check types after interfaces have been completed.)
|
||||||
|
type _ interface {
|
||||||
|
type interface{ Error() string }, interface{ String() string }
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue