Skip to content

Commit 556ae92

Browse files
demangle: parse C++20 pointer-to-member conversions
1 parent 0294712 commit 556ae92

File tree

4 files changed

+111
-16
lines changed

4 files changed

+111
-16
lines changed

ast.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2549,6 +2549,70 @@ func (so *Subobject) goString(indent int, field string) string {
25492549
indent+2, "", so.PastEnd)
25502550
}
25512551

2552+
// PtrMemCast is a conversion of an expression to a pointer-to-member
2553+
// type. This is used for C++20 manglings of class types used as the
2554+
// type of non-type template arguments.
2555+
//
2556+
// See https://github.com/itanium-cxx-abi/cxx-abi/issues/47.
2557+
type PtrMemCast struct {
2558+
Type AST
2559+
Expr AST
2560+
Offset int
2561+
}
2562+
2563+
func (pmc *PtrMemCast) print(ps *printState) {
2564+
ps.writeString("(")
2565+
ps.print(pmc.Type)
2566+
ps.writeString(")(")
2567+
ps.print(pmc.Expr)
2568+
ps.writeString(")")
2569+
}
2570+
2571+
func (pmc *PtrMemCast) Traverse(fn func(AST) bool) {
2572+
if fn(pmc) {
2573+
pmc.Type.Traverse(fn)
2574+
pmc.Expr.Traverse(fn)
2575+
}
2576+
}
2577+
2578+
func (pmc *PtrMemCast) Copy(fn func(AST) AST, skip func(AST) bool) AST {
2579+
if skip(pmc) {
2580+
return nil
2581+
}
2582+
typ := pmc.Type.Copy(fn, skip)
2583+
expr := pmc.Expr.Copy(fn, skip)
2584+
if typ == nil && expr == nil {
2585+
return nil
2586+
}
2587+
if typ == nil {
2588+
typ = pmc.Type
2589+
}
2590+
if expr == nil {
2591+
expr = pmc.Expr
2592+
}
2593+
pmc = &PtrMemCast{
2594+
Type: typ,
2595+
Expr: expr,
2596+
Offset: pmc.Offset,
2597+
}
2598+
if r := fn(pmc); r != nil {
2599+
return r
2600+
}
2601+
return pmc
2602+
}
2603+
2604+
func (pmc *PtrMemCast) GoString() string {
2605+
return pmc.goString(0, "")
2606+
}
2607+
2608+
func (pmc *PtrMemCast) goString(indent int, field string) string {
2609+
return fmt.Sprintf("%*s%sPtrMemCast:\n%s\n%s\n%*sOffset: %d",
2610+
indent, "", field,
2611+
pmc.Type.goString(indent+2, "Type: "),
2612+
pmc.Expr.goString(indent+2, "Expr: "),
2613+
indent+2, "", pmc.Offset)
2614+
}
2615+
25522616
// New is a use of operator new in an expression.
25532617
type New struct {
25542618
Op AST

cases_test.go

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30178,46 +30178,58 @@ var casesExpectedFailures = map[string]bool{
3017830178
"_ZZZZN6abcdef9abcdefghi29abcdefabcdefabcdefabcefabcdef27xxxxxxxxxxxxxxxxxxxxxxxxxxxEN4absl8DurationERKNSt3__u12basic_stringIcNS4_11char_traitsIcEENS4_9allocatorIcEEEEPNS1_19yyyyyyyyyyyyyyyyyyyEENK3$_5clEvENKUlvE_clEvE6zzzzzz": true,
3017930179
"_Z1fIXtl1BadsoiL_Z6nestedE_EEEEvv": true,
3018030180
"_Z1fIXtl1BcvPiplcvPcadL_Z7derivedELl16EEEEvv": true,
30181-
"_Z1fIXtl1DmcM7DerivedKiadL_ZN11MoreDerived1zEEn8EEEEvv": true,
3018230181
"_Z1fIXtl1Edi1nLi42EEEEvv": true,
3018330182
"_Z1h1XIJZ1fIiEDaOT_E1AZ1gIdEDaS2_E1BEE": true,
3018430183
"_ZN1A1gIiEEDTcldtptfpT1b1fIT_EEEv": true,
3018530184
}
3018630185

3018730186
// caseExceptions is a list of exceptions from the LLVM list that we
3018830187
// do not handle the same as the LLVM demangler. We keep a list of
30189-
// exceptions so that we can use an exact copy of the test cases.
30190-
var casesExceptions = map[string]bool{
30188+
// exceptions so that we can use an exact copy of the test cases. We
30189+
// map to an empty string if we expect a demangling failure; this
30190+
// differs from caseExpectedFailures in that we've decided that we
30191+
// intentionally should not demangle this case. Otherwise this maps
30192+
// to the expected demangling.
30193+
var casesExceptions = map[string]string{
30194+
"_Z1fIXtl1DmcM7DerivedKiadL_ZN11MoreDerived1zEEn8EEEEvv" : "void f<D{(int const Derived::*)(&MoreDerived::z)}>()",
3019130195
}
3019230196

3019330197
func TestCases(t *testing.T) {
30198+
t.Parallel()
3019430199
expectedFails := 0
3019530200
found := make(map[string]bool)
3019630201
for _, test := range cases {
3019730202
expectedFail := casesExpectedFailures[test[0]]
30198-
exception := casesExceptions[test[0]]
30203+
exception, haveException := casesExceptions[test[0]]
30204+
if expectedFail && haveException {
30205+
t.Errorf("test case error: %s in both expectedFailures and exceptions", test[0])
30206+
}
30207+
want := test[1]
30208+
if haveException && exception != "" {
30209+
want = exception
30210+
}
3019930211
if got, err := ToString(test[0]); err != nil {
30200-
if expectedFail {
30212+
if expectedFail || (haveException && exception == "") {
3020130213
t.Logf("demangling %s: expected failure: error %v", test[0], err)
30202-
expectedFails++
30203-
found[test[0]] = true
30204-
} else if exception {
30205-
t.Logf("demangling %s: ignore expected difference: error %v", test[0], err)
30214+
if expectedFail {
30215+
expectedFails++
30216+
found[test[0]] = true
30217+
}
3020630218
} else {
3020730219
t.Errorf("demangling %s: unexpected error %v", test[0], err)
3020830220
}
30209-
} else if got != test[1] {
30221+
} else if got != want {
3021030222
if expectedFail {
30211-
t.Logf("demangling %s: expected failure: got %s, want %s", test[0], got, test[1])
30223+
t.Logf("demangling %s: expected failure: got %s, want %s", test[0], got, want)
3021230224
expectedFails++
3021330225
found[test[0]] = true
30214-
} else if exception {
30215-
t.Logf("demangling %s: ignore expected difference: got %s, want %s", test[0], got, test[1])
30226+
} else if haveException && exception == "" {
30227+
t.Errorf("demangling %s: expected to fail, but succeeded with %s", test[0], got)
3021630228
} else {
30217-
t.Errorf("demangling %s: got %s, want %s", test[0], got, test[1])
30229+
t.Errorf("demangling %s: got %s, want %s", test[0], got, want)
3021830230
}
30219-
} else if expectedFail || exception {
30220-
t.Errorf("demangling %s: expected to fail, but succeeded", test[0])
30231+
} else if expectedFail || (haveException && exception == "") {
30232+
t.Errorf("demangling %s: expected to fail, but succeeded with %s", test[0], got)
3022130233
if expectedFail {
3022230234
found[test[0]] = true
3022330235
}

demangle.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,6 +1968,7 @@ func (st *state) exprList(stop byte) AST {
19681968
// ::= dc <type> <expression>
19691969
// ::= sc <type> <expression>
19701970
// ::= cc <type> <expression>
1971+
// ::= mc <parameter type> <expr> [<offset number>] E
19711972
// ::= rc <type> <expression>
19721973
// ::= ti <type>
19731974
// ::= te <expression>
@@ -2067,6 +2068,23 @@ func (st *state) expression() AST {
20672068
st.cvQualifiers()
20682069
index := st.compactNumber()
20692070
return &FunctionParam{Index: index + 1}
2071+
} else if st.str[0] == 'm' && len(st.str) > 1 && st.str[1] == 'c' {
2072+
st.advance(2)
2073+
typ := st.demangleType(false)
2074+
expr := st.expression()
2075+
offset := 0
2076+
if len(st.str) > 0 && (st.str[0] == 'n' || isDigit(st.str[0])) {
2077+
offset = st.number()
2078+
}
2079+
if len(st.str) == 0 || st.str[0] != 'E' {
2080+
st.fail("expected E after pointer-to-member conversion")
2081+
}
2082+
st.advance(1)
2083+
return &PtrMemCast{
2084+
Type: typ,
2085+
Expr: expr,
2086+
Offset: offset,
2087+
}
20702088
} else if isDigit(st.str[0]) || (st.str[0] == 'o' && len(st.str) > 1 && st.str[1] == 'n') {
20712089
if st.str[0] == 'o' {
20722090
// Skip operator function ID.

expected_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ var exceptions = map[string]bool{
5050
// the syntax. We ignore all tests that are not --format=gnu-v3 or
5151
// --format=auto with a string starting with _Z.
5252
func TestExpected(t *testing.T) {
53+
t.Parallel()
5354
f, err := os.Open(filename)
5455
if err != nil {
5556
t.Fatal(err)

0 commit comments

Comments
 (0)