Skip to content

Commit 02c0db7

Browse files
committed
Multiline contract comments #2113
1 parent 8a62c12 commit 02c0db7

File tree

12 files changed

+209
-58
lines changed

12 files changed

+209
-58
lines changed

releasenotes.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- Improve error on unsigned implicit conversion to signed.
1717
- Update error message for struct initialization #2286
1818
- `$is_const` is deprecated in favour of `@is_const` based on `$defined`.
19+
- Multiline contract comments #2113
1920

2021
### Fixes
2122
- mkdir/rmdir would not work properly with substring paths on non-windows platforms.
@@ -45,6 +46,7 @@
4546
- Correctly poison the analysis after a failed $assert or $error. #2284
4647
- `$foo` variables could be assigned non-compile time values.
4748
- `$foo[0] = ...` was incorrectly requiring that the assigned values were compile time constants.
49+
- "Inlined at" would sometimes show the current location.
4850

4951
### Stdlib changes
5052
- Improve contract for readline. #2280

src/compiler/c_codegen.c

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -637,9 +637,50 @@ static void c_emit_local_decl(GenContext *c, Decl *decl, CValue *value)
637637
value->kind = CV_VALUE;
638638
}
639639

640-
PRINT("/* TODO ZERO INIT */\n");
641-
//llvm_store_zero(c, value);
642-
//llvm_value_set(value, llvm_get_zero(c, var_type), var_type);
640+
switch (var_type->type_kind)
641+
{
642+
case TYPE_BOOL:
643+
PRINTF("___var_%d = false;\n", value->var);
644+
break;
645+
case ALL_INTS:
646+
case ALL_FLOATS:
647+
PRINTF("___var_%d = 0;\n", value->var);
648+
break;
649+
case TYPE_POISONED:
650+
case TYPE_VOID:
651+
case TYPE_DISTINCT:
652+
case TYPE_CONST_ENUM:
653+
case TYPE_FUNC_RAW:
654+
case TYPE_BITSTRUCT:
655+
case TYPE_TYPEDEF:
656+
case TYPE_UNTYPED_LIST:
657+
case TYPE_FLEXIBLE_ARRAY:
658+
case TYPE_INFERRED_ARRAY:
659+
case TYPE_INFERRED_VECTOR:
660+
case TYPE_OPTIONAL:
661+
case TYPE_WILDCARD:
662+
case TYPE_TYPEINFO:
663+
case TYPE_MEMBER:
664+
UNREACHABLE
665+
case TYPE_ANY:
666+
case TYPE_INTERFACE:
667+
PRINTF("___var_%d = (c3_any_t){ NULL, NULL };\n", value->var);
668+
break;
669+
case TYPE_ANYFAULT:
670+
case TYPE_TYPEID:
671+
case TYPE_FUNC_PTR:
672+
case TYPE_POINTER:
673+
case TYPE_ENUM:
674+
PRINTF("___var_%d = 0;\n", value->var);
675+
break;
676+
case TYPE_STRUCT:
677+
case TYPE_UNION:
678+
case TYPE_SLICE:
679+
case TYPE_ARRAY:
680+
case TYPE_VECTOR:
681+
PRINT("/* TODO ZERO INIT */\n");
682+
683+
}
643684
}
644685

645686
static void c_emit_return(GenContext *c, Ast *stmt)
@@ -758,9 +799,9 @@ static void c_emit_stmt(GenContext *c, Ast *stmt)
758799
case AST_CASE_STMT:
759800
break;
760801
case AST_COMPOUND_STMT:
761-
PRINT(" {\n");
802+
PRINT("{\n");
762803
c_emit_stmt_chain(c, stmt->compound_stmt.first_stmt);
763-
PRINT(" }\n");
804+
PRINT("}\n");
764805
return;
765806
case AST_CONTINUE_STMT:
766807
break;

src/compiler/compiler_internal.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ typedef uint16_t FileId;
6767
#define SEMA_NOTE(_node, ...) sema_note_prev_at((_node)->span, __VA_ARGS__)
6868
#define SEMA_DEPRECATED(_node, ...) do { if (compiler.build.test_output && !compiler.build.silence_deprecation) print_error_at((_node)->span, __VA_ARGS__); if (!compiler.build.silence_deprecation) \
6969
sema_note_prev_at((_node)->span, __VA_ARGS__); } while (0)
70+
#define PRINT_DEPRECATED_AT(span__, ...) do { if (compiler.build.test_output && !compiler.build.silence_deprecation) print_error_at(span__, __VA_ARGS__); if (!compiler.build.silence_deprecation) \
71+
sema_note_prev_at(span__, __VA_ARGS__); } while (0)
7072

7173
#define EXPAND_EXPR_STRING(str_) (str_)->const_expr.bytes.len, (str_)->const_expr.bytes.ptr
7274
#define TABLE_MAX_LOAD 0.5
@@ -2982,7 +2984,7 @@ INLINE bool type_convert_will_trunc(Type *destination, Type *source)
29822984
// Useful sanity check function.
29832985
INLINE void advance_and_verify(ParseContext *context, TokenType token_type)
29842986
{
2985-
ASSERT(context->tok == token_type);
2987+
ASSERT_SPAN(context, context->tok == token_type);
29862988
advance(context);
29872989
}
29882990

src/compiler/parse_expr.c

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1904,20 +1904,16 @@ static Expr *parse_double(ParseContext *c, Expr *left, SourceSpan lhs_start)
19041904
return number;
19051905
}
19061906

1907-
/**
1908-
* string_literal ::= STRING+
1909-
*/
1910-
static Expr *parse_string_literal(ParseContext *c, Expr *left, SourceSpan lhs_start)
1911-
{
1912-
ASSERT(!left && "Had left hand side");
1913-
Expr *expr_string = EXPR_NEW_TOKEN(EXPR_CONST);
19141907

1908+
bool parse_joined_strings(ParseContext *c, const char **str_ref, size_t *len_ref)
1909+
{
19151910
const char *str = symstr(c);
19161911
size_t len = c->data.strlen;
19171912
advance_and_verify(c, TOKEN_STRING);
1918-
1913+
if (!str_ref) scratch_buffer_append(str);
19191914
// This is wasteful for adding many tokens together
19201915
// and can be optimized.
1916+
if (tok_is(c, TOKEN_DOCS_EOL) && peek(c) == TOKEN_STRING) advance(c);
19211917
while (tok_is(c, TOKEN_STRING))
19221918
{
19231919
// Grab the token.
@@ -1928,21 +1924,44 @@ static Expr *parse_string_literal(ParseContext *c, Expr *left, SourceSpan lhs_st
19281924
advance_and_verify(c, TOKEN_STRING);
19291925
continue;
19301926
}
1931-
// Create new string and copy.
1932-
char *buffer = malloc_string(len + next_len + 1);
1933-
memcpy(buffer, str, len);
1934-
memcpy(buffer + len, symstr(c), next_len);
1935-
len += next_len;
1936-
buffer[len] = '\0';
1937-
str = buffer;
1927+
if (!str_ref)
1928+
{
1929+
scratch_buffer_append(symstr(c));
1930+
}
1931+
else
1932+
{
1933+
// Create new string and copy.
1934+
char *buffer = malloc_string(len + next_len + 1);
1935+
memcpy(buffer, str, len);
1936+
memcpy(buffer + len, symstr(c), next_len);
1937+
len += next_len;
1938+
buffer[len] = '\0';
1939+
str = buffer;
1940+
}
19381941
advance_and_verify(c, TOKEN_STRING);
1942+
if (tok_is(c, TOKEN_DOCS_EOL) && peek(c) == TOKEN_STRING) advance(c);
19391943
}
19401944
if (len > UINT32_MAX)
19411945
{
19421946
PRINT_ERROR_HERE("String exceeded max size.");
1943-
return poisoned_expr;
1947+
return false;
19441948
}
1949+
if (!str_ref) return true;
19451950
ASSERT(str);
1951+
*str_ref = str;
1952+
*len_ref = len;
1953+
return true;
1954+
}
1955+
/**
1956+
* string_literal ::= STRING+
1957+
*/
1958+
static Expr *parse_string_literal(ParseContext *c, Expr *left, SourceSpan lhs_start)
1959+
{
1960+
ASSERT(!left && "Had left hand side");
1961+
Expr *expr_string = EXPR_NEW_TOKEN(EXPR_CONST);
1962+
const char *str;
1963+
size_t len;
1964+
if (!parse_joined_strings(c, &str, &len)) return poisoned_expr;
19461965
expr_string->const_expr.bytes.ptr = str;
19471966
expr_string->const_expr.bytes.len = (uint32_t)len;
19481967
expr_string->type = type_string;

src/compiler/parse_global.c

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2756,6 +2756,49 @@ INLINE bool parse_doc_to_eol(ParseContext *c)
27562756
return false;
27572757
}
27582758

2759+
INLINE bool parse_doc_check_skip_string_eos(ParseContext *c)
2760+
{
2761+
if (tok_is(c, TOKEN_STRING)) return true;
2762+
if (!tok_is(c, TOKEN_DOCS_EOL)) return false;
2763+
if (peek(c) != TOKEN_STRING) return false;
2764+
advance_and_verify(c, TOKEN_DOCS_EOL);
2765+
return true;
2766+
}
2767+
2768+
INLINE bool parse_docs_to_comment(ParseContext *c)
2769+
{
2770+
if (try_consume(c, TOKEN_COLON)) return true;
2771+
if (!tok_is(c, TOKEN_DOCS_EOL)) return false;
2772+
if (peek(c) == TOKEN_COLON)
2773+
{
2774+
advance_and_verify(c, TOKEN_DOCS_EOL);
2775+
advance_and_verify(c, TOKEN_COLON);
2776+
return true;
2777+
}
2778+
return false;
2779+
}
2780+
2781+
static bool parse_doc_discarded_comment(ParseContext *c)
2782+
{
2783+
if (try_consume(c, TOKEN_STRING))
2784+
{
2785+
PRINT_DEPRECATED_AT(c->span, "Not using ':' before the description is deprecated");
2786+
return true;
2787+
}
2788+
if (!parse_docs_to_comment(c)) return true;
2789+
if (!parse_doc_check_skip_string_eos(c)) return true;
2790+
return parse_joined_strings(c, NULL, NULL);
2791+
}
2792+
static bool parse_doc_direct_comment(ParseContext *c)
2793+
{
2794+
if (tok_is(c, TOKEN_DOCS_EOL) && peek(c) == TOKEN_STRING)
2795+
{
2796+
advance(c);
2797+
}
2798+
if (!tok_is(c, TOKEN_STRING)) return true;
2799+
return parse_joined_strings(c, NULL, NULL);
2800+
}
2801+
27592802
/**
27602803
* contract ::= expression_list (':'? STRING)?
27612804
*/
@@ -2782,9 +2825,11 @@ static inline bool parse_doc_contract(ParseContext *c, AstId *docs, AstId **docs
27822825
}
27832826
scratch_buffer_append_remove_space(start, (int)(end - start));
27842827
scratch_buffer_append("\" violated");
2785-
if (try_consume(c, TOKEN_COLON))
2828+
bool docs_to_comment = false;
2829+
if (parse_docs_to_comment(c))
27862830
{
2787-
if (!tok_is(c, TOKEN_STRING))
2831+
docs_to_comment = true;
2832+
if (!parse_doc_check_skip_string_eos(c))
27882833
{
27892834
print_error_at(c->prev_span, "Expected a string after ':'");
27902835
return false;
@@ -2793,10 +2838,13 @@ static inline bool parse_doc_contract(ParseContext *c, AstId *docs, AstId **docs
27932838
if (tok_is(c, TOKEN_STRING))
27942839
{
27952840
scratch_buffer_append(": '");
2796-
scratch_buffer_append(symstr(c));
2841+
if (!parse_joined_strings(c, NULL, NULL)) return false;
27972842
scratch_buffer_append("'.");
27982843
ast->contract_stmt.contract.comment = scratch_buffer_copy();
2799-
advance(c);
2844+
if (!docs_to_comment)
2845+
{
2846+
SEMA_DEPRECATED(ast, "Not using ':' before the description is deprecated");
2847+
}
28002848
}
28012849
else
28022850
{
@@ -2855,17 +2903,21 @@ static inline bool parse_contract_param(ParseContext *c, AstId *docs, AstId **do
28552903
case TOKEN_HASH_IDENT:
28562904
break;
28572905
default:
2858-
PRINT_ERROR_HERE("Expected a parameter name here.");
2859-
return false;
2906+
RETURN_PRINT_ERROR_HERE("Expected a parameter name here.");
28602907
}
28612908
ast->contract_stmt.param.name = symstr(c);
28622909
ast->contract_stmt.param.span = c->span;
28632910
ast->contract_stmt.param.modifier = mod;
28642911
ast->contract_stmt.param.by_ref = is_ref;
28652912
advance(c);
2866-
if (try_consume(c, TOKEN_COLON))
2913+
2914+
if (parse_docs_to_comment(c))
28672915
{
2868-
CONSUME_OR_RET(TOKEN_STRING, false);
2916+
if (!parse_doc_check_skip_string_eos(c))
2917+
{
2918+
RETURN_PRINT_ERROR_LAST("Expected a string after ':'");
2919+
}
2920+
if (!parse_joined_strings(c, NULL, NULL)) return false;
28692921
}
28702922
else
28712923
{
@@ -2910,14 +2962,7 @@ static inline bool parse_doc_optreturn(ParseContext *c, AstId *docs, AstId **doc
29102962
}
29112963
RANGE_EXTEND_PREV(ast);
29122964
// Just ignore our potential string:
2913-
if (try_consume(c, TOKEN_COLON))
2914-
{
2915-
CONSUME_OR_RET(TOKEN_STRING, false);
2916-
}
2917-
else if (try_consume(c, TOKEN_STRING))
2918-
{
2919-
RETURN_PRINT_ERROR_LAST("Expected a ':' before the description.");
2920-
}
2965+
if (!parse_doc_discarded_comment(c)) return false;
29212966
ast->contract_stmt.faults = returns;
29222967
append_docs(docs_next, docs, ast);
29232968
return true;
@@ -2944,13 +2989,10 @@ static bool parse_contracts(ParseContext *c, AstId *contracts_ref)
29442989

29452990
while (!try_consume(c, TOKEN_DOCS_END))
29462991
{
2947-
// Skip empty lines.
29482992
if (try_consume(c, TOKEN_DOCS_EOL)) continue;
2949-
29502993
if (!tok_is(c, TOKEN_AT_IDENT))
29512994
{
2952-
PRINT_ERROR_HERE("Expected a directive starting with '@' here, like '@param' or `@require`");
2953-
return false;
2995+
RETURN_PRINT_ERROR_HERE("Expected a directive starting with '@' here, like '@param' or `@require`");
29542996
}
29552997
const char *name = symstr(c);
29562998
if (name == kw_at_param)
@@ -2966,13 +3008,13 @@ static bool parse_contracts(ParseContext *c, AstId *contracts_ref)
29663008
}
29673009
else
29683010
{
2969-
if (!consume(c, TOKEN_STRING, "Expected a string description.")) return false;
3011+
if (!parse_doc_direct_comment(c)) return false;
29703012
}
29713013
}
29723014
else if (name == kw_at_deprecated)
29733015
{
29743016
advance(c);
2975-
(void)try_consume(c, TOKEN_STRING);
3017+
if (!parse_doc_discarded_comment(c)) return false;
29763018
REMINDER("Implement @deprecated tracking");
29773019
}
29783020
else if (name == kw_at_require)
@@ -2987,17 +3029,19 @@ static bool parse_contracts(ParseContext *c, AstId *contracts_ref)
29873029
{
29883030
Ast *ast = ast_new_curr(c, AST_CONTRACT);
29893031
ast->contract_stmt.kind = CONTRACT_PURE;
2990-
append_docs(next, contracts_ref, ast);
29913032
advance(c);
3033+
if (!parse_doc_discarded_comment(c)) return false;
3034+
append_docs(next, contracts_ref, ast);
29923035
}
29933036
else
29943037
{
29953038
advance(c);
3039+
if (!parse_doc_direct_comment(c)) return false;
29963040
if (parse_doc_to_eol(c)) continue;
2997-
if (!consume(c, TOKEN_STRING, "Expected a string description for the custom contract '%s'.", name)) return false;
3041+
RETURN_PRINT_ERROR_HERE("Expected a string description for the custom contract '%s'.", name);
29983042
}
29993043
if (parse_doc_to_eol(c)) continue;
3000-
PRINT_ERROR_HERE("Expected end of line here.");
3044+
PRINT_ERROR_HERE("Expected the end of the contract here.");
30013045
return false;
30023046
}
30033047
return true;

src/compiler/parser_internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Expr *parse_expression_list(ParseContext *c, bool allow_decls);
5454
Decl *parse_local_decl_after_type(ParseContext *c, TypeInfo *type);
5555
Decl *parse_var_decl(ParseContext *c);
5656
bool parse_current_is_expr(ParseContext *c);
57-
57+
bool parse_joined_strings(ParseContext *c, const char **str_ref, size_t *len_ref);
5858
bool parse_expr_list(ParseContext *c, Expr ***exprs_ref, TokenType end_token);
5959
bool parse_parameters(ParseContext *c, Decl ***params_ref,
6060
Variadic *variadic, int *vararg_index_ref, ParameterParseKind parse_kind);

src/compiler/sema_casts.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ typedef struct // NOLINT
2020
bool is_binary_conversion;
2121
} CastContext;
2222

23-
#define RETURN_CAST_ERROR(_node, ...) do { print_error_at((_node)->span, __VA_ARGS__); sema_print_inline(cc->context); return false; } while (0)
23+
#define RETURN_CAST_ERROR(_node, ...) do { print_error_at((_node)->span, __VA_ARGS__); sema_print_inline(cc->context, (_node)->span); return false; } while (0)
2424

2525
static bool sema_error_const_int_out_of_range(CastContext *cc, Expr *expr, Expr *problem, Type *to_type);
2626
static Expr *recursive_may_narrow(Expr *expr, Type *type);

src/compiler/sema_internal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#define SEMA_WARN(_node, ...) (sema_warn_at(context, (_node)->span, __VA_ARGS__))
2121
#define SEMA_ERROR(_node, ...) sema_error_at(context, (_node)->span, __VA_ARGS__)
2222
#define RETURN_SEMA_ERROR(_node, ...) do { sema_error_at(context, (_node)->span, __VA_ARGS__); return false; } while (0)
23+
#define RETURN_VAL_SEMA_ERROR(val__, _node, ...) do { sema_error_at(context, (_node)->span, __VA_ARGS__); return (val__); } while (0)
2324
#define RETURN_NULL_SEMA_ERROR(_node, ...) do { sema_error_at(context, (_node)->span, __VA_ARGS__); return NULL; } while (0)
2425
#define RETURN_SEMA_ERROR_AT(span__, ...) do { sema_error_at(context, span__, __VA_ARGS__); return false; } while (0)
2526
#define SCOPE_OUTER_START do { DynamicScope stored_scope = context->active_scope; context_change_scope_with_flags(context, SCOPE_NONE);
@@ -55,7 +56,7 @@ void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags);
5556
SemaContext *context_transform_for_eval(SemaContext *context, SemaContext *temp_context, CompilationUnit *eval_unit);
5657

5758
TokenType sema_splitpathref(const char *string, ArraySize len, Path **path_ref, const char **ident_ref);
58-
void sema_print_inline(SemaContext *context);
59+
void sema_print_inline(SemaContext *context, SourceSpan span_original);
5960
void sema_error_at(SemaContext *context, SourceSpan span, const char *message, ...);
6061
bool sema_warn_at(SemaContext *context, SourceSpan span, const char *message, ...);
6162

0 commit comments

Comments
 (0)