Skip to content

Commit f82d50a

Browse files
committed
Fix class member using declaration naming a constructor
1 parent 6a81087 commit f82d50a

File tree

3 files changed

+134
-56
lines changed

3 files changed

+134
-56
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.eclipse.cdt.internal.core.dom.parser.cpp;
2+
3+
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
4+
import org.eclipse.cdt.core.dom.ast.IASTNode;
5+
import org.eclipse.cdt.internal.core.dom.parser.ASTAmbiguousNode;
6+
7+
public class CPPASTAmbiguousUsingDeclaration extends ASTAmbiguousNode implements IASTDeclaration {
8+
private final IASTDeclaration fDeclaration;
9+
10+
public CPPASTAmbiguousUsingDeclaration(IASTDeclaration declaration) {
11+
fDeclaration = declaration;
12+
}
13+
14+
@Override
15+
public IASTDeclaration copy() {
16+
throw new UnsupportedOperationException();
17+
}
18+
19+
@Override
20+
public IASTDeclaration copy(CopyStyle style) {
21+
throw new UnsupportedOperationException();
22+
}
23+
24+
@Override
25+
public IASTNode[] getNodes() {
26+
return new IASTNode[] { fDeclaration };
27+
}
28+
}

core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTCompositeTypeSpecifier.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@
2121
import org.eclipse.cdt.core.dom.ast.IASTNode;
2222
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTClassVirtSpecifier;
2323
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
24+
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier;
25+
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
26+
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration;
2427
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
2528
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope;
2629
import org.eclipse.cdt.core.parser.util.ArrayUtil;
30+
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
2731
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
2832

2933
/**
@@ -154,6 +158,49 @@ public void addMemberDeclaration(IASTDeclaration decl) {
154158

155159
@Override
156160
public final void addDeclaration(IASTDeclaration decl) {
161+
// [C++20] 6.5.4.2 Class members [class.qual]
162+
// (2) In a lookup in which function names are not ignored
163+
// and the nested-name-specifier nominates a class C:
164+
// (2.1) if the name specified after the nested-name-specifier,
165+
// when looked up in C, is the injected-class-name of C (11.1), or
166+
// (2.2) in a using-declarator of a using-declaration (9.9) that is a member-declaration,
167+
// if the name specified after the nested-name-specifier is the same as the
168+
// identifier or the simple-template-id's template-name in the last component
169+
// of the nested-name-specifier,
170+
// the name is instead considered to name the constructor of class C.
171+
// ...
172+
// Such a constructor name shall be used only in the declarator-id of
173+
// a declaration that names a constructor or in a using-declaration.
174+
//
175+
// AST2TemplateTests.testInheritedConstructor_489710() has the following code fragment:
176+
// template <int I, typename T>
177+
// struct B : public B<I - 1, T> {
178+
// ...
179+
// using Base = B<I - 1, T>;
180+
// using Base::Base;
181+
// ...
182+
// };
183+
//
184+
// Here, while resolving ambiguities finds CPPASTAmbiguousTemplateArgument of first member 'using Base = B<I - 1, T>'
185+
// declaration name lookup for 'I' in 'struct B' scope causes cache population of CPPClassScope of 'struct B'.
186+
// Cache population finds second member 'using Base::Base' declaration but at that point it is not possible
187+
// to tell whether this member using-declaration nominates a class because first using declaration still contains
188+
// unresolved ambiguous template argument. The needed typedef is found but mapped type is deferred class
189+
// which yields ProblemBinding.
190+
//
191+
// To fix this detect if using-declaration potentially names a constructor and return CPPASTAmbiguousUsingDeclaration
192+
// wrapping real declaration. This will be skipped while populating 'struct B' class scope and resolved after
193+
// first 'using Base = B<...>' member declaration ambiguities are resolved. Cache will be populated after resolution is complete.
194+
195+
if (decl instanceof ICPPASTUsingDeclaration using && using.getName() instanceof ICPPASTQualifiedName qName) {
196+
final ICPPASTNameSpecifier[] qualifier = qName.getQualifier();
197+
if (qualifier.length > 0 && qualifier[qualifier.length - 1] instanceof IASTName segmentName) {
198+
if (CharArrayUtils.equals(segmentName.getSimpleID(), qName.getLastName().getSimpleID())) {
199+
decl = new CPPASTAmbiguousUsingDeclaration(decl);
200+
}
201+
}
202+
}
203+
157204
addMemberDeclaration(decl);
158205
}
159206

core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPClassScope.java

Lines changed: 59 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,11 @@
2525
import static org.eclipse.cdt.internal.core.dom.parser.cpp.CPPBasicType.VOID;
2626
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.TDEF;
2727

28-
import java.util.Arrays;
29-
3028
import org.eclipse.cdt.core.CCorePlugin;
3129
import org.eclipse.cdt.core.dom.IName;
3230
import org.eclipse.cdt.core.dom.ast.EScopeKind;
31+
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
3332
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
34-
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
3533
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
3634
import org.eclipse.cdt.core.dom.ast.IASTFieldReference;
3735
import org.eclipse.cdt.core.dom.ast.IASTName;
@@ -43,7 +41,6 @@
4341
import org.eclipse.cdt.core.dom.ast.IScope;
4442
import org.eclipse.cdt.core.dom.ast.ISemanticProblem;
4543
import org.eclipse.cdt.core.dom.ast.IType;
46-
import org.eclipse.cdt.core.dom.ast.ITypedef;
4744
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
4845
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier;
4946
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNewExpression;
@@ -146,58 +143,6 @@ public void createImplicitMembers() {
146143
implicits[i++] = m;
147144
addBinding(m);
148145
}
149-
150-
markInheritedConstructorsSourceBases(compTypeSpec);
151-
}
152-
153-
/**
154-
* Marks bases that serve as sources of inherited constructors.
155-
*/
156-
private void markInheritedConstructorsSourceBases(ICPPASTCompositeTypeSpecifier compositeTypeSpec) {
157-
ICPPBase[] bases = getClassType().getBases();
158-
if (bases.length == 0)
159-
return;
160-
IASTDeclaration[] members = compositeTypeSpec.getMembers();
161-
for (IASTDeclaration member : members) {
162-
if (member instanceof ICPPASTUsingDeclaration) {
163-
IASTName name = ((ICPPASTUsingDeclaration) member).getName();
164-
if (!(name instanceof ICPPASTQualifiedName))
165-
continue;
166-
ICPPASTQualifiedName qName = (ICPPASTQualifiedName) name;
167-
ICPPASTNameSpecifier[] qualifier = qName.getQualifier();
168-
if (qualifier.length == 0)
169-
continue;
170-
IBinding parent = qualifier[qualifier.length - 1].resolveBinding();
171-
if (!(parent instanceof IType) || parent instanceof IProblemBinding)
172-
continue;
173-
if (isConstructorNameForType(qName.getLastName().getSimpleID(), (IType) parent)) {
174-
IType type = SemanticUtil.getNestedType((IType) parent, TDEF);
175-
for (ICPPBase base : bases) {
176-
IType baseClass = base.getBaseClassType();
177-
if (type.isSameType(baseClass)) {
178-
if (base instanceof CPPBaseClause) {
179-
((CPPBaseClause) base).setInheritedConstructorsSource(true);
180-
} else {
181-
CCorePlugin.log(IStatus.ERROR, "Unexpected type of base (" //$NON-NLS-1$
182-
+ base.getClass().getSimpleName() + ") for '" //$NON-NLS-1$
183-
+ compositeTypeSpec.getRawSignature() + "'"); //$NON-NLS-1$
184-
}
185-
}
186-
}
187-
}
188-
}
189-
}
190-
}
191-
192-
private static boolean isConstructorNameForType(char[] lastName, IType type) {
193-
while (type instanceof IBinding) {
194-
if (Arrays.equals(((IBinding) type).getNameCharArray(), lastName))
195-
return true;
196-
if (!(type instanceof ITypedef))
197-
break;
198-
type = ((ITypedef) type).getType();
199-
}
200-
return false;
201146
}
202147

203148
@Override
@@ -255,7 +200,65 @@ public void addName(IASTName name, boolean adlOnly) {
255200
addConstructor(name);
256201
return;
257202
}
203+
} else if (parent.getParent() instanceof ICPPASTUsingDeclaration usingDeclaration
204+
&& parent instanceof ICPPASTQualifiedName qName) {
205+
206+
// In addition to normal class scope population procedure this method will be called after
207+
// ambiguity resolution for CPPASTAmbiguousUsingDeclaration to handle nominated base class constructors.
208+
209+
// [C++20] 9.9 The using declaration [namespace.udecl]
210+
// 3) In a using-declaration used as a member-declaration, each using-declarator shall either
211+
// name an enumerator or have a nested-name-specifier naming a base class of the class being defined.
212+
// If a using-declarator names a constructor, its nested-name-specifier shall name a direct base class
213+
// of the class being defined
214+
215+
// [C++20] 6.5.4.2 Class members [class.qual]
216+
// (2) In a lookup in which function names are not ignored
217+
// and the nested-name-specifier nominates a class C:
218+
// (2.1) if the name specified after the nested-name-specifier,
219+
// when looked up in C, is the injected-class-name of C (11.1), or
220+
// (2.2) in a using-declarator of a using-declaration (9.9) that is a member-declaration,
221+
// if the name specified after the nested-name-specifier is the same as the
222+
// identifier or the simple-template-id's template-name in the last component
223+
// of the nested-name-specifier,
224+
// the name is instead considered to name the constructor of class C.
225+
// ...
226+
// Such a constructor name shall be used only in the declarator-id of
227+
// a declaration that names a constructor or in a using-declaration.
228+
229+
final ICPPASTNameSpecifier[] qualifier = qName.getQualifier();
230+
final char[] lastName = name.getSimpleID();
231+
232+
if (usingDeclaration.getPropertyInParent() == IASTCompositeTypeSpecifier.MEMBER_DECLARATION
233+
&& qualifier.length > 0 && qualifier[qualifier.length - 1] instanceof IASTName lastSegmentName
234+
&& CharArrayUtils.equals(lastSegmentName.getSimpleID(), lastName)) {
235+
ICPPBase[] bases = getClassType().getBases();
236+
if (bases.length > 0) {
237+
IBinding nominatedBinding = lastSegmentName.resolveBinding();
238+
if (nominatedBinding instanceof IType nominatedType
239+
&& !(nominatedType instanceof IProblemBinding)) {
240+
IType type = SemanticUtil.getNestedType(nominatedType, TDEF);
241+
for (ICPPBase base : bases) {
242+
IType baseClass = base.getBaseClassType();
243+
if (type.isSameType(baseClass)) {
244+
// Member using-declaration names direct base class constructor
245+
// Mark nominated base class as inherited constructors source and skip adding to scope
246+
if (base instanceof CPPBaseClause baseClause) {
247+
baseClause.setInheritedConstructorsSource(true);
248+
} else {
249+
ICPPASTCompositeTypeSpecifier compTypeSpec = (ICPPASTCompositeTypeSpecifier) getPhysicalNode();
250+
CCorePlugin.log(IStatus.ERROR, "Unexpected type of base (" //$NON-NLS-1$
251+
+ base.getClass().getSimpleName() + ") for '" //$NON-NLS-1$
252+
+ compTypeSpec.getRawSignature() + "'"); //$NON-NLS-1$
253+
}
254+
return;
255+
}
256+
}
257+
}
258+
}
259+
}
258260
}
261+
259262
super.addName(name, adlOnly);
260263
}
261264

0 commit comments

Comments
 (0)