Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disallow qualified types in base class list #16882

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions changelog/dmd.basic-vs-primary-type.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
The term *basic type* is now *primary type*

The parser now uses the term *primary type* for what used to be referred to as *basic type.*
The term *basic type* is used specifically for type expressions that are not immediately recursive.

A *basic type* is a *fundamental type* (those which have keywords)
or a type given by an identifier, a `typeof`, a `mixin`, or a `__traits`.
Absent from this list are `__vector` types and types spelled out using a qualifier and parentheses (e.g. `const(int)`).

A *primary type* is a *basic type* or a `__vector` type or a qualified type.

For the most part, *primary type* is the term you want to use and will see in error messages now.
There are some corners where indeed a *basic type* is required.
18 changes: 18 additions & 0 deletions changelog/dmd.const-base.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Specifying a qualified type as a base class or interface is an error now

A type like `const(Object)` could be used as a base class in the base class list
of a class or interface declaration or of an anonymous class object.
There, qualifiers were simply ignored semantically.

Explicitly qualifying types in base class lists is a parse error now.

This only affects parsing.
If a `mixin` or `typeof` or an identifier is used, nothing changes.
If it resolves to a qualified type,
the qualifier is ignored.
---
class C : const(Object) { } // Parse error now
auto obj = new class const(Object) { }; // Parse error now
---
There is no deprecation period.
The fix is to remove the unnecessary and misleading qualifier from your code.
66 changes: 41 additions & 25 deletions compiler/src/dmd/parse.d
Original file line number Diff line number Diff line change
Expand Up @@ -2859,7 +2859,7 @@
{
case TOK.rightParenthesis:
if (storageClass != 0 || udas !is null)
error("basic type expected, not `)`");
error("primary type expected, not `)`");

Check warning on line 2862 in compiler/src/dmd/parse.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/parse.d#L2862

Added line #L2862 was not covered by tests
break;

case TOK.dotDotDot:
Expand Down Expand Up @@ -3074,7 +3074,7 @@
nextToken();
int alt = 0;
const typeLoc = token.loc;
memtype = parseBasicType();
memtype = parsePrimaryType();
memtype = parseDeclarator(memtype, alt, null);
checkCstyleTypeSyntax(typeLoc, memtype, alt, null);
}
Expand Down Expand Up @@ -3186,7 +3186,7 @@
else
{
Token* t = &token;
if (isBasicType(&t))
if (isPrimaryType(&t))

Check warning on line 3189 in compiler/src/dmd/parse.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/parse.d#L3189

Added line #L3189 was not covered by tests
{
error("named enum cannot declare member with type", (*t).toChars());
nextToken();
Expand Down Expand Up @@ -3582,7 +3582,7 @@
const typeLoc = token.loc;

AST.Type t;
t = parseBasicType();
t = parsePrimaryType();

if (pdeclLoc)
*pdeclLoc = token.loc;
Expand All @@ -3594,7 +3594,7 @@
return t;
}

private AST.Type parseBasicType(bool dontLookDotIdents = false)
private AST.Type parseBasicType(bool dontLookDotIdents = false, bool parsingPrimaryType = false)
{
AST.Type t;
Loc loc;
Expand Down Expand Up @@ -3723,11 +3723,11 @@
{
// ident!(template_arguments)
auto tempinst = new AST.TemplateInstance(loc, id, parseTemplateArguments());
t = parseBasicTypeStartingAt(new AST.TypeInstance(loc, tempinst), dontLookDotIdents);
t = parsePrimaryTypeStartingAt(new AST.TypeInstance(loc, tempinst), dontLookDotIdents);
}
else
{
t = parseBasicTypeStartingAt(new AST.TypeIdentifier(loc, id), dontLookDotIdents);
t = parsePrimaryTypeStartingAt(new AST.TypeIdentifier(loc, id), dontLookDotIdents);
}
break;

Expand All @@ -3743,14 +3743,33 @@

case TOK.dot:
// Leading . as in .foo
t = parseBasicTypeStartingAt(new AST.TypeIdentifier(token.loc, Id.empty), dontLookDotIdents);
t = parsePrimaryTypeStartingAt(new AST.TypeIdentifier(token.loc, Id.empty), dontLookDotIdents);
break;

case TOK.typeof_:
// typeof(expression)
t = parseBasicTypeStartingAt(parseTypeof(), dontLookDotIdents);
t = parsePrimaryTypeStartingAt(parseTypeof(), dontLookDotIdents);
break;

default:
immutable char* kind = parsingPrimaryType ? "primary" : "basic";
error("%s type expected, not `%s`", kind, token.toChars());
if (token.value == TOK.else_)
eSink.errorSupplemental(token.loc, "There's no `static else`, use `else` instead.");
t = AST.Type.terror;
break;
}
return t;
}

private AST.Type parsePrimaryType(bool dontLookDotIdents = false)
{
AST.Type t;
Loc loc;
Identifier id;
//printf("parsePrimaryType()\n");
switch (token.value)
{
case TOK.vector:
t = parseVector();
break;
Expand Down Expand Up @@ -3798,20 +3817,17 @@
break;

default:
error("basic type expected, not `%s`", token.toChars());
if (token.value == TOK.else_)
eSink.errorSupplemental(token.loc, "There's no `static else`, use `else` instead.");
t = AST.Type.terror;
t = parseBasicType(dontLookDotIdents, /+parsingPrimaryType:+/true);
break;
}
return t;
}

private AST.Type parseBasicTypeStartingAt(AST.TypeQualified tid, bool dontLookDotIdents)
private AST.Type parsePrimaryTypeStartingAt(AST.TypeQualified tid, bool dontLookDotIdents)
{
AST.Type maybeArray = null;
// See https://issues.dlang.org/show_bug.cgi?id=1215
// A basic type can look like MyType (typical case), but also:
// A primary type can look like MyType (typical case), but also:
// MyType.T -> A type
// MyType[expr] -> Either a static array of MyType or a type (iif MyType is a Ttuple)
// MyType[expr].T -> A type.
Expand Down Expand Up @@ -4517,7 +4533,7 @@
}
else
{
ts = parseBasicType();
ts = parsePrimaryType();
ts = parseTypeSuffixes(ts);
}
}
Expand Down Expand Up @@ -4971,7 +4987,7 @@
if (udas)
error("user-defined attributes not allowed for `alias` declarations");

auto t = parseBasicType();
auto t = parsePrimaryType();
t = parseTypeSuffixes(t);
if (token.value == TOK.identifier)
{
Expand Down Expand Up @@ -5115,7 +5131,7 @@
{
// function type (parameters) { statements... }
// delegate type (parameters) { statements... }
tret = parseBasicType();
tret = parsePrimaryType();
tret = parseTypeSuffixes(tret); // function return type
}

Expand Down Expand Up @@ -7211,7 +7227,7 @@
break;
}

if (!isBasicType(&t))
if (!isPrimaryType(&t))
{
goto Lisnot;
}
Expand Down Expand Up @@ -7240,10 +7256,10 @@
return false;
}

// pt = test token. If found, pt is set to the token after BasicType
private bool isBasicType(Token** pt)
// pt = test token. If found, pt is set to the token after PrimaryType
private bool isPrimaryType(Token** pt)
{
// This code parallels parseBasicType()
// This code parallels parsePrimaryType()
Token* t = *pt;
switch (t.value)
{
Expand Down Expand Up @@ -7746,7 +7762,7 @@

default:
{
if (!isBasicType(&t))
if (!isPrimaryType(&t))
return false;
L2:
int tmp = false;
Expand Down Expand Up @@ -8785,7 +8801,7 @@
{
StorageClass stc = parseTypeCtor();

AST.Type t = parseBasicType();
AST.Type t = parsePrimaryType();
t = t.addSTC(stc);

if (stc == 0 && token.value == TOK.dot)
Expand Down Expand Up @@ -9545,7 +9561,7 @@
}

const stc = parseTypeCtor();
auto t = parseBasicType(true);
auto t = parsePrimaryType(true);
t = parseTypeSuffixes(t);
t = t.addSTC(stc);
if (t.ty == Taarray)
Expand Down
12 changes: 12 additions & 0 deletions compiler/test/fail_compilation/qualbaseclass1.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* TEST_OUTPUT:
---
fail_compilation\qualbaseclass1.d(101): Error: basic type expected, not `const`
fail_compilation\qualbaseclass1.d(101): Error: { } expected following `class` declaration
fail_compilation\qualbaseclass1.d(101): Error: no identifier for declarator `const(Object)`
fail_compilation\qualbaseclass1.d(101): Error: declaration expected, not `{`
---
*/

#line 100

class C : const(Object) { }
15 changes: 15 additions & 0 deletions compiler/test/fail_compilation/qualbaseclass2.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* TEST_OUTPUT:
---
fail_compilation\qualbaseclass2.d(103): Error: basic type expected, not `const`
fail_compilation\qualbaseclass2.d(103): Error: `{ members }` expected for anonymous class
fail_compilation\qualbaseclass2.d(103): Error: semicolon expected following auto declaration, not `const`
fail_compilation\qualbaseclass2.d(103): Error: no identifier for declarator `const(Object)`
---
*/

#line 100

void test()
{
auto obj = new class () const(Object) { };
}
Loading