+ if (cond) {
+ cond = process_condition(parser, cond, &ifnot);
+ if (!cond)
+ goto onerr;
+ }
+ aloop = ast_loop_new(ctx, initexpr, cond, ifnot, NULL, false, increment, ontrue);
+ *out = (ast_expression*)aloop;
+
+ if (!parser_leaveblock(parser)) {
+ ast_delete(aloop);
+ return false;
+ }
+ return true;
+onerr:
+ if (initexpr) ast_unref(initexpr);
+ if (cond) ast_unref(cond);
+ if (increment) ast_unref(increment);
+ (void)!parser_leaveblock(parser);
+ return false;
+}
+
+static bool parse_return(parser_t *parser, ast_block *block, ast_expression **out)
+{
+ ast_expression *exp = NULL;
+ ast_expression *var = NULL;
+ ast_return *ret = NULL;
+ ast_value *retval = parser->function->return_value;
+ ast_value *expected = parser->function->vtype;
+
+ lex_ctx_t ctx = parser_ctx(parser);
+
+ (void)block; /* not touching */
+
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected return expression");
+ return false;
+ }
+
+ /* return assignments */
+ if (parser->tok == '=') {
+ if (!OPTS_FLAG(RETURN_ASSIGNMENTS)) {
+ parseerror(parser, "return assignments not activated, try using -freturn-assigments");
+ return false;
+ }
+
+ if (type_store_instr[expected->expression.next->vtype] == VINSTR_END) {
+ char ty1[1024];
+ ast_type_to_string(expected->expression.next, ty1, sizeof(ty1));
+ parseerror(parser, "invalid return type: `%s'", ty1);
+ return false;
+ }
+
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected return assignment expression");
+ return false;
+ }
+
+ if (!(exp = parse_expression_leave(parser, false, false, false)))
+ return false;
+
+ /* prepare the return value */
+ if (!retval) {
+ retval = ast_value_new(ctx, "#LOCAL_RETURN", TYPE_VOID);
+ ast_type_adopt(retval, expected->expression.next);
+ parser->function->return_value = retval;
+ }
+
+ if (!ast_compare_type(exp, (ast_expression*)retval)) {
+ char ty1[1024], ty2[1024];
+ ast_type_to_string(exp, ty1, sizeof(ty1));
+ ast_type_to_string(&retval->expression, ty2, sizeof(ty2));
+ parseerror(parser, "invalid type for return value: `%s', expected `%s'", ty1, ty2);
+ }
+
+ /* store to 'return' local variable */
+ var = (ast_expression*)ast_store_new(
+ ctx,
+ type_store_instr[expected->expression.next->vtype],
+ (ast_expression*)retval, exp);
+
+ if (!var) {
+ ast_unref(exp);
+ return false;
+ }
+
+ if (parser->tok != ';')
+ parseerror(parser, "missing semicolon after return assignment");
+ else if (!parser_next(parser))
+ parseerror(parser, "parse error after return assignment");
+
+ *out = var;
+ return true;
+ }
+
+ if (parser->tok != ';') {
+ exp = parse_expression(parser, false, false);
+ if (!exp)
+ return false;
+
+ if (exp->vtype != TYPE_NIL &&
+ exp->vtype != ((ast_expression*)expected)->next->vtype)
+ {
+ parseerror(parser, "return with invalid expression");
+ }
+
+ ret = ast_return_new(ctx, exp);
+ if (!ret) {
+ ast_unref(exp);
+ return false;
+ }
+ } else {
+ if (!parser_next(parser))
+ parseerror(parser, "parse error");
+
+ if (!retval && expected->expression.next->vtype != TYPE_VOID)
+ {
+ (void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
+ }
+ ret = ast_return_new(ctx, (ast_expression*)retval);
+ }
+ *out = (ast_expression*)ret;
+ return true;
+}
+
+static bool parse_break_continue(parser_t *parser, ast_block *block, ast_expression **out, bool is_continue)
+{
+ size_t i;
+ unsigned int levels = 0;
+ lex_ctx_t ctx = parser_ctx(parser);
+ const char **loops = (is_continue ? parser->continues : parser->breaks);
+
+ (void)block; /* not touching */
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected semicolon or loop label");
+ return false;
+ }
+
+ if (!vec_size(loops)) {
+ if (is_continue)
+ parseerror(parser, "`continue` can only be used inside loops");
+ else
+ parseerror(parser, "`break` can only be used inside loops or switches");
+ }
+
+ if (parser->tok == TOKEN_IDENT) {
+ if (!OPTS_FLAG(LOOP_LABELS))
+ parseerror(parser, "labeled loops not activated, try using -floop-labels");
+ i = vec_size(loops);
+ while (i--) {
+ if (loops[i] && !strcmp(loops[i], parser_tokval(parser)))
+ break;
+ if (!i) {
+ parseerror(parser, "no such loop to %s: `%s`",
+ (is_continue ? "continue" : "break out of"),
+ parser_tokval(parser));
+ return false;
+ }
+ ++levels;
+ }
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected semicolon");
+ return false;
+ }
+ }
+
+ if (parser->tok != ';') {
+ parseerror(parser, "expected semicolon");
+ return false;
+ }
+
+ if (!parser_next(parser))
+ parseerror(parser, "parse error");
+
+ *out = (ast_expression*)ast_breakcont_new(ctx, is_continue, levels);
+ return true;
+}
+
+/* returns true when it was a variable qualifier, false otherwise!
+ * on error, cvq is set to CV_WRONG
+ */
+static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *noref, bool *is_static, uint32_t *_flags, char **message)
+{
+ bool had_const = false;
+ bool had_var = false;
+ bool had_noref = false;
+ bool had_attrib = false;
+ bool had_static = false;
+ uint32_t flags = 0;
+
+ *cvq = CV_NONE;
+ for (;;) {
+ if (parser->tok == TOKEN_ATTRIBUTE_OPEN) {
+ had_attrib = true;
+ /* parse an attribute */
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected attribute after `[[`");
+ *cvq = CV_WRONG;
+ return false;
+ }
+ if (!strcmp(parser_tokval(parser), "noreturn")) {
+ flags |= AST_FLAG_NORETURN;
+ if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+ parseerror(parser, "`noreturn` attribute has no parameters, expected `]]`");
+ *cvq = CV_WRONG;
+ return false;
+ }
+ }
+ else if (!strcmp(parser_tokval(parser), "noref")) {
+ had_noref = true;
+ if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+ parseerror(parser, "`noref` attribute has no parameters, expected `]]`");
+ *cvq = CV_WRONG;
+ return false;
+ }
+ }
+ else if (!strcmp(parser_tokval(parser), "inline")) {
+ flags |= AST_FLAG_INLINE;
+ if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) {
+ parseerror(parser, "`noref` attribute has no parameters, expected `]]`");
+ *cvq = CV_WRONG;
+ return false;
+ }
+ }
+ else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) {
+ flags |= AST_FLAG_ALIAS;
+ *message = NULL;
+
+ if (!parser_next(parser)) {
+ parseerror(parser, "parse error in attribute");
+ goto argerr;
+ }