Skip to content

Commit 9ce87a6

Browse files
mvdangriesemer
authored andcommitted
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í <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent d76b1cd commit 9ce87a6

File tree

2 files changed

+33
-4
lines changed

2 files changed

+33
-4
lines changed

src/cmd/compile/internal/gc/main.go

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ func Main(archInit func(*Arch)) {
480480

481481
// Process top-level declarations in phases.
482482

483-
// Phase 1: const, type, and names and types of funcs.
483+
// Phase 1: type, and names and types of funcs.
484484
// This will gather all the information about types
485485
// and methods but doesn't depend on any of it.
486486
defercheckwidth()
@@ -489,16 +489,29 @@ func Main(archInit func(*Arch)) {
489489
timings.Start("fe", "typecheck", "top1")
490490
for i := 0; i < len(xtop); i++ {
491491
n := xtop[i]
492-
if op := n.Op; op != ODCL && op != OAS && op != OAS2 {
492+
if op := n.Op; op != ODCL && op != OAS && op != OAS2 && op != ODCLCONST {
493493
xtop[i] = typecheck(n, Etop)
494494
}
495495
}
496496

497-
// Phase 2: Variable assignments.
498-
// To check interface assignments, depends on phase 1.
497+
// Phase 2: Constant declarations.
498+
// To have named types fully type checked, depends on phase 1.
499499

500500
// Don't use range--typecheck can add closures to xtop.
501501
timings.Start("fe", "typecheck", "top2")
502+
for i := 0; i < len(xtop); i++ {
503+
n := xtop[i]
504+
if op := n.Op; op == ODCLCONST {
505+
xtop[i] = typecheck(n, Etop)
506+
}
507+
}
508+
509+
// Phase 3: Variable assignments.
510+
// To check interface assignments, depends on phase 1.
511+
// To use constants, depends on phase 2.
512+
513+
// Don't use range--typecheck can add closures to xtop.
514+
timings.Start("fe", "typecheck", "top3")
502515
for i := 0; i < len(xtop); i++ {
503516
n := xtop[i]
504517
if op := n.Op; op == ODCL || op == OAS || op == OAS2 {

test/fixedbugs/issue24755.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// errorcheck
2+
3+
// Copyright 2018 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// Tests that all types and functions are type-checked before any constant
8+
// declaration is. Issue #24755.
9+
package p
10+
11+
type I interface{ F() }
12+
type T struct{}
13+
14+
const _ = I(T{}) // ERROR "const initializer I\(T literal\) is not a constant"
15+
16+
func (T) F() {}

0 commit comments

Comments
 (0)