diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index cec88277a0b2..19b28fa0a114 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -3814,7 +3814,6 @@ class FuncDeclaration : public Declaration bool needsClosure(); bool checkClosure(); bool hasNestedFrameRefs(); - static bool needsFensure(FuncDeclaration* fd); ParameterList getParameterList(); static FuncDeclaration* genCfunc(Array* fparams, Type* treturn, const char* name, StorageClass stc = 0); static FuncDeclaration* genCfunc(Array* fparams, Type* treturn, Identifier* id, StorageClass stc = 0); diff --git a/compiler/src/dmd/func.d b/compiler/src/dmd/func.d index 46874ad8e3f2..55974f16e53f 100644 --- a/compiler/src/dmd/func.d +++ b/compiler/src/dmd/func.d @@ -1501,27 +1501,6 @@ extern (C++) class FuncDeclaration : Declaration return false; } - /**************************************************** - * Determine whether an 'out' contract is declared inside - * the given function or any of its overrides. - * Params: - * fd = the function to search - * Returns: - * true found an 'out' contract - */ - static bool needsFensure(FuncDeclaration fd) @safe - { - if (fd.fensures) - return true; - - foreach (fdv; fd.foverrides) - { - if (needsFensure(fdv)) - return true; - } - return false; - } - /********************************************* * Returns: the function's parameter list, and whether * it is variadic or not. @@ -1578,127 +1557,6 @@ extern (C++) class FuncDeclaration : Declaration return fd; } - /+ - + Checks the parameter and return types iff this is a `main` function. - + - + The following signatures are allowed for a `D main`: - + - Either no or a single parameter of type `string[]` - + - Return type is either `void`, `int` or `noreturn` - + - + The following signatures are standard C: - + - `int main()` - + - `int main(int, char**)` - + - + This function accepts the following non-standard extensions: - + - `char** envp` as a third parameter - + - `void` / `noreturn` as return type - + - + This function will issue errors for unexpected arguments / return types. - +/ - extern (D) final void checkMain() - { - if (ident != Id.main || isMember() || isNested()) - return; // Not a main function - - TypeFunction tf = type.toTypeFunction(); - - Type retType = tf.nextOf(); - if (!retType) - { - // auto main(), check after semantic - assert(this.inferRetType); - return; - } - - /// Checks whether `t` is equivalent to `char**` - /// Ignores qualifiers and treats enums according to their base type - static bool isCharPtrPtr(Type t) - { - auto tp = t.toBasetype().isTypePointer(); - if (!tp) - return false; - - tp = tp.next.toBasetype().isTypePointer(); - if (!tp) - return false; - - return tp.next.toBasetype().ty == Tchar; - } - - // Neither of these qualifiers is allowed because they affect the ABI - enum invalidSTC = STC.out_ | STC.ref_ | STC.lazy_; - - const nparams = tf.parameterList.length; - bool argerr; - - const linkage = resolvedLinkage(); - if (linkage == LINK.d) - { - if (nparams == 1) - { - auto fparam0 = tf.parameterList[0]; - auto t = fparam0.type.toBasetype(); - if (t.ty != Tarray || - t.nextOf().ty != Tarray || - t.nextOf().nextOf().ty != Tchar || - fparam0.storageClass & invalidSTC) - { - argerr = true; - } - } - - if (tf.parameterList.varargs || nparams >= 2 || argerr) - .error(loc, "%s `%s` parameter list must be empty or accept one parameter of type `string[]`", kind, toPrettyChars); - } - - else if (linkage == LINK.c) - { - if (nparams == 2 || nparams == 3) - { - // Argument count must be int - auto argCount = tf.parameterList[0]; - argerr |= !!(argCount.storageClass & invalidSTC); - argerr |= argCount.type.toBasetype().ty != Tint32; - - // Argument pointer must be char** - auto argPtr = tf.parameterList[1]; - argerr |= !!(argPtr.storageClass & invalidSTC); - argerr |= !isCharPtrPtr(argPtr.type); - - // `char** environ` is a common extension, see J.5.1 of the C standard - if (nparams == 3) - { - auto envPtr = tf.parameterList[2]; - argerr |= !!(envPtr.storageClass & invalidSTC); - argerr |= !isCharPtrPtr(envPtr.type); - } - } - else - argerr = nparams != 0; - - // Disallow variadic main() - except for K&R declarations in C files. - // E.g. int main(), int main(argc, argv) int argc, char** argc { ... } - if (tf.parameterList.varargs && (!this.isCsymbol() || (!tf.parameterList.hasIdentifierList && nparams))) - argerr |= true; - - if (argerr) - { - .error(loc, "%s `%s` parameters must match one of the following signatures", kind, toPrettyChars); - loc.errorSupplemental("`main()`"); - loc.errorSupplemental("`main(int argc, char** argv)`"); - loc.errorSupplemental("`main(int argc, char** argv, char** environ)` [POSIX extension]"); - } - } - else - return; // Neither C nor D main, ignore (should probably be an error) - - // Allow enums with appropriate base types (same ABI) - retType = retType.toBasetype(); - - if (retType.ty != Tint32 && retType.ty != Tvoid && retType.ty != Tnoreturn) - .error(loc, "%s `%s` must return `int`, `void` or `noreturn`, not `%s`", kind, toPrettyChars, tf.nextOf().toChars()); - } - /*********************************************** * Check all return statements for a function to verify that returning * using NRVO is possible. @@ -1960,23 +1818,6 @@ unittest assert(mismatches.isMutable); } -/************************************** - * Returns an indirect type one step from t. - */ -Type getIndirection(Type t) -{ - t = t.baseElemOf(); - if (t.ty == Tarray || t.ty == Tpointer) - return t.nextOf().toBasetype(); - if (t.ty == Taarray || t.ty == Tclass) - return t; - if (t.ty == Tstruct) - return t.hasPointers() ? t : null; // TODO - - // should consider TypeDelegate? - return null; -} - /************************************** * Performs type-based alias analysis between a newly created value and a pre- * existing memory reference: diff --git a/compiler/src/dmd/funcsem.d b/compiler/src/dmd/funcsem.d index 1c16248c9c7e..94d94e63ba77 100644 --- a/compiler/src/dmd/funcsem.d +++ b/compiler/src/dmd/funcsem.d @@ -2602,6 +2602,27 @@ void buildEnsureRequire(FuncDeclaration thisfd) } } +/**************************************************** + * Determine whether an 'out' contract is declared inside + * the given function or any of its overrides. + * Params: + * fd = the function to search + * Returns: + * true found an 'out' contract + */ +bool needsFensure(FuncDeclaration fd) @safe +{ + if (fd.fensures) + return true; + + foreach (fdv; fd.foverrides) + { + if (needsFensure(fdv)) + return true; + } + return false; +} + /**************************************************** * Merge into this function the 'out' contracts of all it overrides. * 'out's are AND'd together, i.e. all of them need to pass. @@ -2624,7 +2645,7 @@ Statement mergeFensure(FuncDeclaration fd, Statement sf, Identifier oid, Express * https://issues.dlang.org/show_bug.cgi?id=3602 and * https://issues.dlang.org/show_bug.cgi?id=5230 */ - if (fd.needsFensure(fdv) && fdv.semanticRun != PASS.semantic3done) + if (needsFensure(fdv) && fdv.semanticRun != PASS.semantic3done) { assert(fdv._scope); Scope* sc = fdv._scope.push(); @@ -2844,3 +2865,124 @@ bool setUnsafePreview(Scope* sc, FeatureState fs, bool gag, Loc loc, const(char) return false; } } + +/+ + + Checks the parameter and return types iff this is a `main` function. + + + + The following signatures are allowed for a `D main`: + + - Either no or a single parameter of type `string[]` + + - Return type is either `void`, `int` or `noreturn` + + + + The following signatures are standard C: + + - `int main()` + + - `int main(int, char**)` + + + + This function accepts the following non-standard extensions: + + - `char** envp` as a third parameter + + - `void` / `noreturn` as return type + + + + This function will issue errors for unexpected arguments / return types. + +/ +extern (D) void checkMain(FuncDeclaration fd) +{ + if (fd.ident != Id.main || fd.isMember() || fd.isNested()) + return; // Not a main function + + TypeFunction tf = fd.type.toTypeFunction(); + + Type retType = tf.nextOf(); + if (!retType) + { + // auto main(), check after semantic + assert(fd.inferRetType); + return; + } + + /// Checks whether `t` is equivalent to `char**` + /// Ignores qualifiers and treats enums according to their base type + static bool isCharPtrPtr(Type t) + { + auto tp = t.toBasetype().isTypePointer(); + if (!tp) + return false; + + tp = tp.next.toBasetype().isTypePointer(); + if (!tp) + return false; + + return tp.next.toBasetype().ty == Tchar; + } + + // Neither of these qualifiers is allowed because they affect the ABI + enum invalidSTC = STC.out_ | STC.ref_ | STC.lazy_; + + const nparams = tf.parameterList.length; + bool argerr; + + const linkage = fd.resolvedLinkage(); + if (linkage == LINK.d) + { + if (nparams == 1) + { + auto fparam0 = tf.parameterList[0]; + auto t = fparam0.type.toBasetype(); + if (t.ty != Tarray || + t.nextOf().ty != Tarray || + t.nextOf().nextOf().ty != Tchar || + fparam0.storageClass & invalidSTC) + { + argerr = true; + } + } + + if (tf.parameterList.varargs || nparams >= 2 || argerr) + .error(fd.loc, "%s `%s` parameter list must be empty or accept one parameter of type `string[]`", fd.kind, fd.toPrettyChars); + } + + else if (linkage == LINK.c) + { + if (nparams == 2 || nparams == 3) + { + // Argument count must be int + auto argCount = tf.parameterList[0]; + argerr |= !!(argCount.storageClass & invalidSTC); + argerr |= argCount.type.toBasetype().ty != Tint32; + + // Argument pointer must be char** + auto argPtr = tf.parameterList[1]; + argerr |= !!(argPtr.storageClass & invalidSTC); + argerr |= !isCharPtrPtr(argPtr.type); + + // `char** environ` is a common extension, see J.5.1 of the C standard + if (nparams == 3) + { + auto envPtr = tf.parameterList[2]; + argerr |= !!(envPtr.storageClass & invalidSTC); + argerr |= !isCharPtrPtr(envPtr.type); + } + } + else + argerr = nparams != 0; + + // Disallow variadic main() - except for K&R declarations in C files. + // E.g. int main(), int main(argc, argv) int argc, char** argc { ... } + if (tf.parameterList.varargs && (!fd.isCsymbol() || (!tf.parameterList.hasIdentifierList && nparams))) + argerr |= true; + + if (argerr) + { + .error(fd.loc, "%s `%s` parameters must match one of the following signatures", fd.kind, fd.toPrettyChars); + fd.loc.errorSupplemental("`main()`"); + fd.loc.errorSupplemental("`main(int argc, char** argv)`"); + fd.loc.errorSupplemental("`main(int argc, char** argv, char** environ)` [POSIX extension]"); + } + } + else + return; // Neither C nor D main, ignore (should probably be an error) + + // Allow enums with appropriate base types (same ABI) + retType = retType.toBasetype(); + + if (retType.ty != Tint32 && retType.ty != Tvoid && retType.ty != Tnoreturn) + .error(fd.loc, "%s `%s` must return `int`, `void` or `noreturn`, not `%s`", fd.kind, fd.toPrettyChars, tf.nextOf().toChars()); +} diff --git a/compiler/src/dmd/semantic3.d b/compiler/src/dmd/semantic3.d index d88face9c6c8..963fa9238a08 100644 --- a/compiler/src/dmd/semantic3.d +++ b/compiler/src/dmd/semantic3.d @@ -314,7 +314,7 @@ private extern(C++) final class Semantic3Visitor : Visitor fds.checkInContractOverrides(); // Remember whether we need to generate an 'out' contract. - immutable bool needEnsure = FuncDeclaration.needsFensure(funcdecl); + immutable bool needEnsure = funcdecl.needsFensure(); if (funcdecl.fbody || funcdecl.frequires || needEnsure) { diff --git a/compiler/src/dmd/typesem.d b/compiler/src/dmd/typesem.d index 28c90a23a3e3..31ebc4c809c6 100644 --- a/compiler/src/dmd/typesem.d +++ b/compiler/src/dmd/typesem.d @@ -1282,6 +1282,23 @@ bool hasPointers(Type t) } } +/************************************** + * Returns an indirect type one step from t. + */ +Type getIndirection(Type t) +{ + t = t.baseElemOf(); + if (t.ty == Tarray || t.ty == Tpointer) + return t.nextOf().toBasetype(); + if (t.ty == Taarray || t.ty == Tclass) + return t; + if (t.ty == Tstruct) + return t.hasPointers() ? t : null; // TODO + + // should consider TypeDelegate? + return null; +} + /****************************************** * Perform semantic analysis on a type. * Params: