Commit 9ce87a63 authored by Daniel Martí's avatar Daniel Martí Committed by Robert Griesemer

cmd/compile: typecheck types and funcs before consts

This way, once the constant declarations are typechecked, all named
types are fully typechecked and have all of their methods added.

Usually this isn't important, as methods and interfaces cannot be used
in constant declarations. However, it can lead to confusing and
incorrect errors, such as:

	$ cat f.go
	package p

	type I interface{ F() }
	type T struct{}

	const _ = I(T{})

	func (T) F() {}
	$ go build f.go
	./f.go:6:12: cannot convert T literal (type T) to type I:
		T does not implement I (missing F method)

The error is clearly wrong, as T does have an F method. If we ensure
that all funcs are typechecked before all constant declarations, we get
the correct error:

	$ go build f2.go
	# command-line-arguments
	./f.go:6:7: const initializer I(T literal) is not a constant

Fixes #24755.

Change-Id: I182b60397b9cac521d9a9ffadb11b42fd42e42fe
Reviewed-on: https://go-review.googlesource.com/c/115096
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarRobert Griesemer <gri@golang.org>
parent d76b1cdf
...@@ -480,7 +480,7 @@ func Main(archInit func(*Arch)) { ...@@ -480,7 +480,7 @@ func Main(archInit func(*Arch)) {
// Process top-level declarations in phases. // Process top-level declarations in phases.
// Phase 1: const, type, and names and types of funcs. // Phase 1: type, and names and types of funcs.
// This will gather all the information about types // This will gather all the information about types
// and methods but doesn't depend on any of it. // and methods but doesn't depend on any of it.
defercheckwidth() defercheckwidth()
...@@ -489,16 +489,29 @@ func Main(archInit func(*Arch)) { ...@@ -489,16 +489,29 @@ func Main(archInit func(*Arch)) {
timings.Start("fe", "typecheck", "top1") timings.Start("fe", "typecheck", "top1")
for i := 0; i < len(xtop); i++ { for i := 0; i < len(xtop); i++ {
n := xtop[i] n := xtop[i]
if op := n.Op; op != ODCL && op != OAS && op != OAS2 { if op := n.Op; op != ODCL && op != OAS && op != OAS2 && op != ODCLCONST {
xtop[i] = typecheck(n, Etop) xtop[i] = typecheck(n, Etop)
} }
} }
// Phase 2: Variable assignments. // Phase 2: Constant declarations.
// To check interface assignments, depends on phase 1. // To have named types fully type checked, depends on phase 1.
// Don't use range--typecheck can add closures to xtop. // Don't use range--typecheck can add closures to xtop.
timings.Start("fe", "typecheck", "top2") timings.Start("fe", "typecheck", "top2")
for i := 0; i < len(xtop); i++ {
n := xtop[i]
if op := n.Op; op == ODCLCONST {
xtop[i] = typecheck(n, Etop)
}
}
// Phase 3: Variable assignments.
// To check interface assignments, depends on phase 1.
// To use constants, depends on phase 2.
// Don't use range--typecheck can add closures to xtop.
timings.Start("fe", "typecheck", "top3")
for i := 0; i < len(xtop); i++ { for i := 0; i < len(xtop); i++ {
n := xtop[i] n := xtop[i]
if op := n.Op; op == ODCL || op == OAS || op == OAS2 { if op := n.Op; op == ODCL || op == OAS || op == OAS2 {
......
// errorcheck
// 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.
// Tests that all types and functions are type-checked before any constant
// declaration is. Issue #24755.
package p
type I interface{ F() }
type T struct{}
const _ = I(T{}) // ERROR "const initializer I\(T literal\) is not a constant"
func (T) F() {}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment