diff --git a/changelog/dmd.basic-vs-primary-type.dd b/changelog/dmd.basic-vs-primary-type.dd new file mode 100644 index 000000000000..91e792020171 --- /dev/null +++ b/changelog/dmd.basic-vs-primary-type.dd @@ -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. diff --git a/changelog/dmd.const-base.dd b/changelog/dmd.const-base.dd new file mode 100644 index 000000000000..adcfe3f527a2 --- /dev/null +++ b/changelog/dmd.const-base.dd @@ -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. diff --git a/compiler/src/dmd/parse.d b/compiler/src/dmd/parse.d index bb2411825fae..0d126ed69f70 100644 --- a/compiler/src/dmd/parse.d +++ b/compiler/src/dmd/parse.d @@ -2859,7 +2859,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { case TOK.rightParenthesis: if (storageClass != 0 || udas !is null) - error("basic type expected, not `)`"); + error("primary type expected, not `)`"); break; case TOK.dotDotDot: @@ -3074,7 +3074,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer nextToken(); int alt = 0; const typeLoc = token.loc; - memtype = parseBasicType(); + memtype = parsePrimaryType(); memtype = parseDeclarator(memtype, alt, null); checkCstyleTypeSyntax(typeLoc, memtype, alt, null); } @@ -3186,7 +3186,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer else { Token* t = &token; - if (isBasicType(&t)) + if (isPrimaryType(&t)) { error("named enum cannot declare member with type", (*t).toChars()); nextToken(); @@ -3582,7 +3582,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer const typeLoc = token.loc; AST.Type t; - t = parseBasicType(); + t = parsePrimaryType(); if (pdeclLoc) *pdeclLoc = token.loc; @@ -3594,7 +3594,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer return t; } - private AST.Type parseBasicType(bool dontLookDotIdents = false) + private AST.Type parseBasicType(bool dontLookDotIdents = false, bool parsingPrimaryType = false) { AST.Type t; Loc loc; @@ -3723,11 +3723,11 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { // 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; @@ -3743,14 +3743,33 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer 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; @@ -3798,20 +3817,17 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer 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. @@ -4517,7 +4533,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } else { - ts = parseBasicType(); + ts = parsePrimaryType(); ts = parseTypeSuffixes(ts); } } @@ -4971,7 +4987,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer 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) { @@ -5115,7 +5131,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { // function type (parameters) { statements... } // delegate type (parameters) { statements... } - tret = parseBasicType(); + tret = parsePrimaryType(); tret = parseTypeSuffixes(tret); // function return type } @@ -7211,7 +7227,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer break; } - if (!isBasicType(&t)) + if (!isPrimaryType(&t)) { goto Lisnot; } @@ -7240,10 +7256,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer 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) { @@ -7746,7 +7762,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer default: { - if (!isBasicType(&t)) + if (!isPrimaryType(&t)) return false; L2: int tmp = false; @@ -8785,7 +8801,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { StorageClass stc = parseTypeCtor(); - AST.Type t = parseBasicType(); + AST.Type t = parsePrimaryType(); t = t.addSTC(stc); if (stc == 0 && token.value == TOK.dot) @@ -9545,7 +9561,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } const stc = parseTypeCtor(); - auto t = parseBasicType(true); + auto t = parsePrimaryType(true); t = parseTypeSuffixes(t); t = t.addSTC(stc); if (t.ty == Taarray) diff --git a/compiler/test/fail_compilation/qualbaseclass1.d b/compiler/test/fail_compilation/qualbaseclass1.d new file mode 100644 index 000000000000..5b35a894b74b --- /dev/null +++ b/compiler/test/fail_compilation/qualbaseclass1.d @@ -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) { } diff --git a/compiler/test/fail_compilation/qualbaseclass2.d b/compiler/test/fail_compilation/qualbaseclass2.d new file mode 100644 index 000000000000..d670a666c3ff --- /dev/null +++ b/compiler/test/fail_compilation/qualbaseclass2.d @@ -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) { }; +}