mirror of
https://gcc.gnu.org/git/gcc.git
synced 2026-02-22 12:00:03 -05:00
Compare commits
48 Commits
basepoints
...
devel/c++-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df3709d167 | ||
|
|
a01c9dfe60 | ||
|
|
7817afdb87 | ||
|
|
43ff4caa5c | ||
|
|
061f0031b2 | ||
|
|
eaee89c99c | ||
|
|
7dbd1db4df | ||
|
|
e378b7f825 | ||
|
|
a05f106baf | ||
|
|
4333447720 | ||
|
|
0c073c813d | ||
|
|
4f428e8b3f | ||
|
|
bfcb6e7373 | ||
|
|
2031dff9d9 | ||
|
|
f5a360ff9b | ||
|
|
5080aec5f3 | ||
|
|
51c5fc25b3 | ||
|
|
6f047a4540 | ||
|
|
ce5faeb1f5 | ||
|
|
a7a4a4ea95 | ||
|
|
3a3e900ba6 | ||
|
|
d7cb97b26d | ||
|
|
0cda8a7e83 | ||
|
|
598a58d553 | ||
|
|
2492743871 | ||
|
|
de497a4f4b | ||
|
|
b141fc304e | ||
|
|
fcce3a45f0 | ||
|
|
704294a049 | ||
|
|
c436179eec | ||
|
|
0a35364554 | ||
|
|
99e1cabe02 | ||
|
|
61b1ec945d | ||
|
|
0199003d90 | ||
|
|
493164dddd | ||
|
|
a70e678aec | ||
|
|
d5c78dacbf | ||
|
|
9160ebeefb | ||
|
|
e7d3169ccd | ||
|
|
52587776b6 | ||
|
|
270966b506 | ||
|
|
58aa1b5f57 | ||
|
|
6e2be2d051 | ||
|
|
7e03bf10f4 | ||
|
|
d1ee78da00 | ||
|
|
fd571461f8 | ||
|
|
3920778fb8 | ||
|
|
80efc142d3 |
@@ -1087,6 +1087,12 @@ c_cpp_builtins (cpp_reader *pfile)
|
||||
else
|
||||
cpp_define (pfile, "__cpp_concepts=201507L");
|
||||
}
|
||||
if (flag_contracts)
|
||||
{
|
||||
cpp_define (pfile, "__cpp_contracts=201906L");
|
||||
cpp_define (pfile, "__cpp_contracts_literal_semantics=201906L");
|
||||
cpp_define (pfile, "__cpp_contracts_roles=201906L");
|
||||
}
|
||||
if (flag_modules)
|
||||
/* The std-defined value is 201907L, but I don't think we can
|
||||
claim victory yet. 201810 is the p1103 date. */
|
||||
|
||||
@@ -1674,6 +1674,47 @@ fconstexpr-ops-limit=
|
||||
C++ ObjC++ Joined RejectNegative Host_Wide_Int Var(constexpr_ops_limit) Init(33554432)
|
||||
-fconstexpr-ops-limit=<number> Specify maximum number of constexpr operations during a single constexpr evaluation.
|
||||
|
||||
fcontracts
|
||||
C++ ObjC++ Var(flag_contracts) Init(0)
|
||||
Enable certain features present drafts of C++ Contracts.
|
||||
|
||||
Enum
|
||||
Name(on_off) Type(int) UnknownError(argument %qs must be either %<on%> or %<off%>)
|
||||
|
||||
EnumValue
|
||||
Enum(on_off) String(off) Value(0)
|
||||
|
||||
EnumValue
|
||||
Enum(on_off) String(on) Value(1)
|
||||
|
||||
fcontract-assumption-mode=
|
||||
C++ Joined
|
||||
-fcontract-assumption-mode=[on|off] Enable or disable treating axiom level contracts as assumptions (default on).
|
||||
|
||||
fcontract-build-level=
|
||||
C++ Joined RejectNegative
|
||||
-fcontract-build-level=[off|default|audit] Specify max contract level to generate runtime checks for
|
||||
|
||||
fcontract-strict-declarations=
|
||||
C++ Var(flag_contract_strict_declarations) Enum(on_off) Joined Init(0) RejectNegative
|
||||
-fcontract-strict-declarations=[on|off] Enable or disable warnings on generalized redeclaration of functions with contracts (default off).
|
||||
|
||||
fcontract-mode=
|
||||
C++ Var(flag_contract_mode) Enum(on_off) Joined Init(1) RejectNegative
|
||||
-fcontract-mode=[on|off] Enable or disable all contract facilities (default on).
|
||||
|
||||
fcontract-continuation-mode=
|
||||
C++ Joined
|
||||
-fcontract-continuation-mode=[on|off] Enable or disable contract continuation mode (default off).
|
||||
|
||||
fcontract-role=
|
||||
C++ Joined
|
||||
-fcontract-role=<name>:<semantics> Specify the semantics for all levels in a role (default, review), or a custom contract role with given semantics (ex: opt:assume,assume,assume)
|
||||
|
||||
fcontract-semantic=
|
||||
C++ Joined
|
||||
-fcontract-semantic=<level>:<semantic> Specify the concrete semantics for level
|
||||
|
||||
fcoroutines
|
||||
C++ LTO Var(flag_coroutines)
|
||||
Enable C++ coroutines (experimental).
|
||||
|
||||
@@ -89,7 +89,7 @@ CXX_AND_OBJCXX_OBJS = \
|
||||
cp/call.o cp/class.o cp/constexpr.o cp/constraint.o \
|
||||
cp/coroutines.o cp/cp-gimplify.o \
|
||||
cp/cp-objcp-common.o cp/cp-ubsan.o \
|
||||
cp/cvt.o cp/cxx-pretty-print.o \
|
||||
cp/cvt.o cp/contracts.o cp/cxx-pretty-print.o \
|
||||
cp/decl.o cp/decl2.o cp/dump.o \
|
||||
cp/error.o cp/except.o cp/expr.o \
|
||||
cp/friend.o cp/init.o \
|
||||
|
||||
@@ -39,6 +39,7 @@ gtfiles="\
|
||||
\$(srcdir)/c-family/c-common.cc \$(srcdir)/c-family/c-format.cc \
|
||||
\$(srcdir)/c-family/c-cppbuiltin.cc \$(srcdir)/c-family/c-pragma.cc \
|
||||
\$(srcdir)/cp/call.cc \$(srcdir)/cp/class.cc \$(srcdir)/cp/constexpr.cc \
|
||||
\$(srcdir)/cp/contracts.cc \
|
||||
\$(srcdir)/cp/constraint.cc \$(srcdir)/cp/coroutines.cc \
|
||||
\$(srcdir)/cp/cp-gimplify.cc \
|
||||
\$(srcdir)/cp/cp-lang.cc \$(srcdir)/cp/cp-objcp-common.cc \
|
||||
|
||||
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "stringpool.h"
|
||||
#include "attribs.h"
|
||||
#include "fold-const.h"
|
||||
#include "intl.h"
|
||||
|
||||
static bool verify_constant (tree, bool, bool *, bool *);
|
||||
#define VERIFY_CONSTANT(X) \
|
||||
@@ -1936,6 +1937,61 @@ diagnose_failing_condition (tree bad, location_t cloc, bool show_expr_p,
|
||||
inform (cloc, "%qE evaluates to false", bad);
|
||||
}
|
||||
|
||||
/* Process an assert/assume of ORIG_ARG. If it's not supposed to be evaluated,
|
||||
do it without changing the current evaluation state. If it evaluates to
|
||||
false, complain and return false; otherwise, return true. */
|
||||
|
||||
static bool
|
||||
cxx_eval_assert (const constexpr_ctx *ctx, tree arg, const char *msg,
|
||||
location_t loc, bool evaluated,
|
||||
bool *non_constant_p, bool *overflow_p)
|
||||
{
|
||||
if (*non_constant_p)
|
||||
return true;
|
||||
|
||||
tree eval;
|
||||
if (!evaluated)
|
||||
{
|
||||
if (!potential_rvalue_constant_expression (arg))
|
||||
return true;
|
||||
|
||||
constexpr_ctx new_ctx = *ctx;
|
||||
new_ctx.quiet = true;
|
||||
bool new_non_constant_p = false, new_overflow_p = false;
|
||||
/* Avoid modification of existing values. */
|
||||
modifiable_tracker ms (new_ctx.global);
|
||||
eval = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
|
||||
&new_non_constant_p,
|
||||
&new_overflow_p);
|
||||
}
|
||||
else
|
||||
eval = cxx_eval_constant_expression (ctx, arg, vc_prvalue,
|
||||
non_constant_p,
|
||||
overflow_p);
|
||||
if (!*non_constant_p && integer_zerop (eval))
|
||||
{
|
||||
if (!ctx->quiet)
|
||||
{
|
||||
/* See if we can find which clause was failing
|
||||
(for logical AND). */
|
||||
tree bad = find_failing_clause (ctx, arg);
|
||||
/* If not, or its location is unusable, fall back to the
|
||||
previous location. */
|
||||
location_t cloc = cp_expr_loc_or_loc (bad, loc);
|
||||
|
||||
/* Report the error. */
|
||||
auto_diagnostic_group d;
|
||||
error_at (cloc, msg);
|
||||
diagnose_failing_condition (bad, cloc, true, ctx);
|
||||
return bad;
|
||||
}
|
||||
*non_constant_p = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Evaluate a call T to a GCC internal function when possible and return
|
||||
the evaluated result or, under the control of CTX, give an error, set
|
||||
NON_CONSTANT_P, and return the unevaluated call T otherwise. */
|
||||
@@ -1956,41 +2012,11 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
|
||||
return void_node;
|
||||
|
||||
case IFN_ASSUME:
|
||||
if (potential_rvalue_constant_expression (CALL_EXPR_ARG (t, 0)))
|
||||
{
|
||||
constexpr_ctx new_ctx = *ctx;
|
||||
new_ctx.quiet = true;
|
||||
tree arg = CALL_EXPR_ARG (t, 0);
|
||||
bool new_non_constant_p = false, new_overflow_p = false;
|
||||
/* Avoid modification of existing values. */
|
||||
modifiable_tracker ms (new_ctx.global);
|
||||
arg = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue,
|
||||
&new_non_constant_p,
|
||||
&new_overflow_p);
|
||||
if (!new_non_constant_p && !new_overflow_p && integer_zerop (arg))
|
||||
{
|
||||
if (!*non_constant_p && !ctx->quiet)
|
||||
{
|
||||
/* See if we can find which clause was failing
|
||||
(for logical AND). */
|
||||
tree bad = find_failing_clause (&new_ctx,
|
||||
CALL_EXPR_ARG (t, 0));
|
||||
/* If not, or its location is unusable, fall back to the
|
||||
previous location. */
|
||||
location_t cloc = cp_expr_loc_or_loc (bad, EXPR_LOCATION (t));
|
||||
|
||||
auto_diagnostic_group d;
|
||||
|
||||
/* Report the error. */
|
||||
error_at (cloc,
|
||||
"failed %<assume%> attribute assumption");
|
||||
diagnose_failing_condition (bad, cloc, false, &new_ctx);
|
||||
}
|
||||
|
||||
*non_constant_p = true;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
if (!cxx_eval_assert (ctx, CALL_EXPR_ARG (t, 0),
|
||||
G_("failed %<assume%> attribute assumption"),
|
||||
EXPR_LOCATION (t), /*eval*/false,
|
||||
non_constant_p, overflow_p))
|
||||
return t;
|
||||
return void_node;
|
||||
|
||||
case IFN_ADD_OVERFLOW:
|
||||
@@ -7845,6 +7871,24 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
|
||||
r = void_node;
|
||||
break;
|
||||
|
||||
case ASSERTION_STMT:
|
||||
case PRECONDITION_STMT:
|
||||
case POSTCONDITION_STMT:
|
||||
{
|
||||
contract_semantic semantic = get_contract_semantic (t);
|
||||
if (semantic == CCS_IGNORE)
|
||||
break;
|
||||
|
||||
if (!cxx_eval_assert (ctx, CONTRACT_CONDITION (t),
|
||||
G_("contract predicate is false in "
|
||||
"constant expression"),
|
||||
EXPR_LOCATION (t), checked_contract_p (semantic),
|
||||
non_constant_p, overflow_p))
|
||||
*non_constant_p = true;
|
||||
r = void_node;
|
||||
}
|
||||
break;
|
||||
|
||||
case TEMPLATE_ID_EXPR:
|
||||
{
|
||||
/* We can evaluate template-id that refers to a concept only if
|
||||
@@ -9819,6 +9863,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
|
||||
return false;
|
||||
}
|
||||
|
||||
case ASSERTION_STMT:
|
||||
case PRECONDITION_STMT:
|
||||
case POSTCONDITION_STMT:
|
||||
if (!checked_contract_p (get_contract_semantic (t)))
|
||||
return true;
|
||||
return RECUR (CONTRACT_CONDITION (t), rval);
|
||||
|
||||
case LABEL_EXPR:
|
||||
t = LABEL_EXPR_LABEL (t);
|
||||
if (DECL_ARTIFICIAL (t) || cxx_dialect >= cxx23)
|
||||
|
||||
2240
gcc/cp/contracts.cc
Normal file
2240
gcc/cp/contracts.cc
Normal file
File diff suppressed because it is too large
Load Diff
305
gcc/cp/contracts.h
Normal file
305
gcc/cp/contracts.h
Normal file
@@ -0,0 +1,305 @@
|
||||
/* Definitions for C++ contract levels. Implements functionality described in
|
||||
the N4820 working draft version of contracts, P1290, P1332, and P1429.
|
||||
Copyright (C) 2020-2022 Free Software Foundation, Inc.
|
||||
Contributed by Jeff Chapman II (jchapman@lock3software.com)
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef GCC_CP_CONTRACT_H
|
||||
#define GCC_CP_CONTRACT_H
|
||||
|
||||
/* Contract levels approximate the complexity of the expression. */
|
||||
|
||||
enum contract_level
|
||||
{
|
||||
CONTRACT_INVALID,
|
||||
CONTRACT_DEFAULT,
|
||||
CONTRACT_AUDIT,
|
||||
CONTRACT_AXIOM
|
||||
};
|
||||
|
||||
/* The concrete semantics determine the behavior of a contract. */
|
||||
|
||||
enum contract_semantic
|
||||
{
|
||||
CCS_INVALID,
|
||||
CCS_IGNORE,
|
||||
CCS_ASSUME,
|
||||
CCS_NEVER,
|
||||
CCS_MAYBE
|
||||
};
|
||||
|
||||
/* True if the contract is unchecked. */
|
||||
|
||||
inline bool
|
||||
unchecked_contract_p (contract_semantic cs)
|
||||
{
|
||||
return cs == CCS_IGNORE || cs == CCS_ASSUME;
|
||||
}
|
||||
|
||||
/* True if the contract is checked. */
|
||||
|
||||
inline bool
|
||||
checked_contract_p (contract_semantic cs)
|
||||
{
|
||||
return cs >= CCS_NEVER;
|
||||
}
|
||||
|
||||
/* Must match std::contract_violation_continuation_mode in <contract>. */
|
||||
enum contract_continuation
|
||||
{
|
||||
NEVER_CONTINUE,
|
||||
MAYBE_CONTINUE
|
||||
};
|
||||
|
||||
/* Assertion role info. */
|
||||
struct contract_role
|
||||
{
|
||||
const char *name;
|
||||
contract_semantic default_semantic;
|
||||
contract_semantic audit_semantic;
|
||||
contract_semantic axiom_semantic;
|
||||
};
|
||||
|
||||
/* Information for configured contract semantics. */
|
||||
|
||||
struct contract_configuration
|
||||
{
|
||||
contract_level level;
|
||||
contract_role* role;
|
||||
};
|
||||
|
||||
/* A contract mode contains information used to derive the checking
|
||||
and assumption semantics of a contract. This is either a dynamic
|
||||
configuration, meaning it derives from the build mode, or it is
|
||||
explicitly specified. */
|
||||
|
||||
struct contract_mode
|
||||
{
|
||||
contract_mode () : kind(cm_invalid) {}
|
||||
contract_mode (contract_level level, contract_role *role = NULL)
|
||||
: kind(cm_dynamic)
|
||||
{
|
||||
contract_configuration cc;
|
||||
cc.level = level;
|
||||
cc.role = role;
|
||||
u.config = cc;
|
||||
}
|
||||
contract_mode (contract_semantic semantic) : kind(cm_explicit)
|
||||
{
|
||||
u.semantic = semantic;
|
||||
}
|
||||
|
||||
contract_level get_level () const
|
||||
{
|
||||
gcc_assert (kind == cm_dynamic);
|
||||
return u.config.level;
|
||||
}
|
||||
|
||||
contract_role *get_role () const
|
||||
{
|
||||
gcc_assert (kind == cm_dynamic);
|
||||
return u.config.role;
|
||||
}
|
||||
|
||||
contract_semantic get_semantic () const
|
||||
{
|
||||
gcc_assert (kind == cm_explicit);
|
||||
return u.semantic;
|
||||
}
|
||||
|
||||
enum { cm_invalid, cm_dynamic, cm_explicit } kind;
|
||||
|
||||
union
|
||||
{
|
||||
contract_configuration config;
|
||||
contract_semantic semantic;
|
||||
} u;
|
||||
};
|
||||
|
||||
extern contract_role *get_contract_role (const char *);
|
||||
extern contract_role *add_contract_role (const char *,
|
||||
contract_semantic,
|
||||
contract_semantic,
|
||||
contract_semantic,
|
||||
bool = true);
|
||||
extern void validate_contract_role (contract_role *);
|
||||
extern void setup_default_contract_role (bool = true);
|
||||
extern contract_semantic lookup_concrete_semantic (const char *);
|
||||
|
||||
/* Map a source level semantic or level name to its value, or invalid. */
|
||||
extern contract_semantic map_contract_semantic (const char *);
|
||||
extern contract_level map_contract_level (const char *);
|
||||
|
||||
/* Check if an attribute is a cxx contract attribute. */
|
||||
extern bool cxx_contract_attribute_p (const_tree);
|
||||
extern bool cp_contract_assertion_p (const_tree);
|
||||
|
||||
/* Returns the default role. */
|
||||
|
||||
inline contract_role *
|
||||
get_default_contract_role ()
|
||||
{
|
||||
return get_contract_role ("default");
|
||||
}
|
||||
|
||||
/* Handle various command line arguments related to semantic mapping. */
|
||||
extern void handle_OPT_fcontract_build_level_ (const char *);
|
||||
extern void handle_OPT_fcontract_assumption_mode_ (const char *);
|
||||
extern void handle_OPT_fcontract_continuation_mode_ (const char *);
|
||||
extern void handle_OPT_fcontract_role_ (const char *);
|
||||
extern void handle_OPT_fcontract_semantic_ (const char *);
|
||||
|
||||
enum contract_matching_context
|
||||
{
|
||||
cmc_declaration,
|
||||
cmc_override
|
||||
};
|
||||
|
||||
/* True if NODE is any kind of contract. */
|
||||
#define CONTRACT_P(NODE) \
|
||||
(TREE_CODE (NODE) == ASSERTION_STMT \
|
||||
|| TREE_CODE (NODE) == PRECONDITION_STMT \
|
||||
|| TREE_CODE (NODE) == POSTCONDITION_STMT)
|
||||
|
||||
/* True if NODE is a contract condition. */
|
||||
#define CONTRACT_CONDITION_P(NODE) \
|
||||
(TREE_CODE (NODE) == PRECONDITION_STMT \
|
||||
|| TREE_CODE (NODE) == POSTCONDITION_STMT)
|
||||
|
||||
/* True if NODE is a precondition. */
|
||||
#define PRECONDITION_P(NODE) \
|
||||
(TREE_CODE (NODE) == PRECONDITION_STMT)
|
||||
|
||||
/* True if NODE is a postcondition. */
|
||||
#define POSTCONDITION_P(NODE) \
|
||||
(TREE_CODE (NODE) == POSTCONDITION_STMT)
|
||||
|
||||
#define CONTRACT_CHECK(NODE) \
|
||||
(TREE_CHECK3 (NODE, ASSERTION_STMT, PRECONDITION_STMT, POSTCONDITION_STMT))
|
||||
|
||||
/* True iff the FUNCTION_DECL NODE currently has any contracts. */
|
||||
#define DECL_HAS_CONTRACTS_P(NODE) \
|
||||
(DECL_CONTRACTS (NODE) != NULL_TREE)
|
||||
|
||||
/* For a FUNCTION_DECL of a guarded function, this points to a list of the pre
|
||||
and post contracts of the first decl of NODE in original order. */
|
||||
#define DECL_CONTRACTS(NODE) \
|
||||
(find_contract (DECL_ATTRIBUTES (NODE)))
|
||||
|
||||
/* The next contract (if any) after this one in an attribute list. */
|
||||
#define CONTRACT_CHAIN(NODE) \
|
||||
(find_contract (TREE_CHAIN (NODE)))
|
||||
|
||||
/* The wrapper of the original source location of a list of contracts. */
|
||||
#define CONTRACT_SOURCE_LOCATION_WRAPPER(NODE) \
|
||||
(TREE_PURPOSE (TREE_VALUE (NODE)))
|
||||
|
||||
/* The original source location of a list of contracts. */
|
||||
#define CONTRACT_SOURCE_LOCATION(NODE) \
|
||||
(EXPR_LOCATION (CONTRACT_SOURCE_LOCATION_WRAPPER (NODE)))
|
||||
|
||||
/* The actual code _STMT for a contract attribute. */
|
||||
#define CONTRACT_STATEMENT(NODE) \
|
||||
(TREE_VALUE (TREE_VALUE (NODE)))
|
||||
|
||||
/* True if the contract semantic was specified literally. If true, the
|
||||
contract mode is an identifier containing the semantic. Otherwise,
|
||||
it is a TREE_LIST whose TREE_VALUE is the level and whose TREE_PURPOSE
|
||||
is the role. */
|
||||
#define CONTRACT_LITERAL_MODE_P(NODE) \
|
||||
(CONTRACT_MODE (NODE) != NULL_TREE \
|
||||
&& TREE_CODE (CONTRACT_MODE (NODE)) == IDENTIFIER_NODE)
|
||||
|
||||
/* The identifier denoting the literal semantic of the contract. */
|
||||
#define CONTRACT_LITERAL_SEMANTIC(NODE) \
|
||||
(TREE_OPERAND (NODE, 0))
|
||||
|
||||
/* The written "mode" of the contract. Either an IDENTIFIER with the
|
||||
literal semantic or a TREE_LIST containing the level and role. */
|
||||
#define CONTRACT_MODE(NODE) \
|
||||
(TREE_OPERAND (CONTRACT_CHECK (NODE), 0))
|
||||
|
||||
/* The identifier denoting the build level of the contract. */
|
||||
#define CONTRACT_LEVEL(NODE) \
|
||||
(TREE_VALUE (CONTRACT_MODE (NODE)))
|
||||
|
||||
/* The identifier denoting the role of the contract */
|
||||
#define CONTRACT_ROLE(NODE) \
|
||||
(TREE_PURPOSE (CONTRACT_MODE (NODE)))
|
||||
|
||||
/* The parsed condition of the contract. */
|
||||
#define CONTRACT_CONDITION(NODE) \
|
||||
(TREE_OPERAND (CONTRACT_CHECK (NODE), 1))
|
||||
|
||||
/* True iff the condition of the contract NODE is not yet parsed. */
|
||||
#define CONTRACT_CONDITION_DEFERRED_P(NODE) \
|
||||
(TREE_CODE (CONTRACT_CONDITION (NODE)) == DEFERRED_PARSE)
|
||||
|
||||
/* The raw comment of the contract. */
|
||||
#define CONTRACT_COMMENT(NODE) \
|
||||
(TREE_OPERAND (CONTRACT_CHECK (NODE), 2))
|
||||
|
||||
/* The VAR_DECL of a postcondition result. For deferred contracts, this
|
||||
is an IDENTIFIER. */
|
||||
#define POSTCONDITION_IDENTIFIER(NODE) \
|
||||
(TREE_OPERAND (POSTCONDITION_STMT_CHECK (NODE), 3))
|
||||
|
||||
/* For a FUNCTION_DECL of a guarded function, this holds the function decl
|
||||
where pre contract checks are emitted. */
|
||||
#define DECL_PRE_FN(NODE) \
|
||||
(get_precondition_function ((NODE)))
|
||||
|
||||
/* For a FUNCTION_DECL of a guarded function, this holds the function decl
|
||||
where post contract checks are emitted. */
|
||||
#define DECL_POST_FN(NODE) \
|
||||
(get_postcondition_function ((NODE)))
|
||||
|
||||
/* True iff the FUNCTION_DECL is the pre function for a guarded function. */
|
||||
#define DECL_IS_PRE_FN_P(NODE) \
|
||||
(DECL_ABSTRACT_ORIGIN (NODE) && DECL_PRE_FN (DECL_ABSTRACT_ORIGIN (NODE)) == NODE)
|
||||
|
||||
/* True iff the FUNCTION_DECL is the post function for a guarded function. */
|
||||
#define DECL_IS_POST_FN_P(NODE) \
|
||||
(DECL_ABSTRACT_ORIGIN (NODE) && DECL_POST_FN (DECL_ABSTRACT_ORIGIN (NODE)) == NODE)
|
||||
|
||||
extern void remove_contract_attributes (tree);
|
||||
extern void copy_contract_attributes (tree, tree);
|
||||
extern void remap_contracts (tree, tree, tree, bool);
|
||||
extern void maybe_update_postconditions (tree);
|
||||
extern void rebuild_postconditions (tree);
|
||||
extern bool check_postcondition_result (tree, tree, location_t);
|
||||
extern tree get_precondition_function (tree);
|
||||
extern tree get_postcondition_function (tree);
|
||||
extern void duplicate_contracts (tree, tree);
|
||||
extern void match_deferred_contracts (tree);
|
||||
extern void defer_guarded_contract_match (tree, tree, tree);
|
||||
extern bool diagnose_misapplied_contracts (tree);
|
||||
extern tree finish_contract_attribute (tree, tree);
|
||||
extern tree invalidate_contract (tree);
|
||||
extern void update_late_contract (tree, tree, tree);
|
||||
extern tree splice_out_contracts (tree);
|
||||
extern bool all_attributes_are_contracts_p (tree);
|
||||
extern void inherit_base_contracts (tree, tree);
|
||||
extern tree apply_postcondition_to_return (tree);
|
||||
extern void start_function_contracts (tree);
|
||||
extern void finish_function_contracts (tree);
|
||||
extern void set_contract_functions (tree, tree, tree);
|
||||
extern tree build_contract_check (tree);
|
||||
extern void emit_assertion (tree);
|
||||
|
||||
#endif /* ! GCC_CP_CONTRACT_H */
|
||||
@@ -1427,6 +1427,23 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
|
||||
wtd->bind_expr_stack.pop ();
|
||||
break;
|
||||
|
||||
case ASSERTION_STMT:
|
||||
case PRECONDITION_STMT:
|
||||
case POSTCONDITION_STMT:
|
||||
{
|
||||
if (tree check = build_contract_check (stmt))
|
||||
{
|
||||
*stmt_p = check;
|
||||
return cp_genericize_r (stmt_p, walk_subtrees, data);
|
||||
}
|
||||
|
||||
/* If we didn't build a check, replace it with void_node so we don't
|
||||
leak contracts into GENERIC. */
|
||||
*stmt_p = void_node;
|
||||
*walk_subtrees = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case USING_STMT:
|
||||
{
|
||||
tree block = NULL_TREE;
|
||||
|
||||
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "cp-objcp-common.h"
|
||||
#include "dwarf2.h"
|
||||
#include "stringpool.h"
|
||||
#include "contracts.h"
|
||||
|
||||
/* Special routine to get the alias set for C++. */
|
||||
|
||||
@@ -86,6 +87,9 @@ cp_tree_size (enum tree_code code)
|
||||
case CONSTRAINT_INFO: return sizeof (tree_constraint_info);
|
||||
case USERDEF_LITERAL: return sizeof (tree_userdef_literal);
|
||||
case TEMPLATE_DECL: return sizeof (tree_template_decl);
|
||||
case ASSERTION_STMT: return sizeof (tree_exp);
|
||||
case PRECONDITION_STMT: return sizeof (tree_exp);
|
||||
case POSTCONDITION_STMT: return sizeof (tree_exp);
|
||||
default:
|
||||
switch (TREE_CODE_CLASS (code))
|
||||
{
|
||||
@@ -565,6 +569,10 @@ cp_common_init_ts (void)
|
||||
MARK_TS_EXP (CO_YIELD_EXPR);
|
||||
MARK_TS_EXP (CO_RETURN_EXPR);
|
||||
|
||||
MARK_TS_EXP (ASSERTION_STMT);
|
||||
MARK_TS_EXP (PRECONDITION_STMT);
|
||||
MARK_TS_EXP (POSTCONDITION_STMT);
|
||||
|
||||
c_common_init_ts ();
|
||||
}
|
||||
|
||||
@@ -577,6 +585,39 @@ cp_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
|
||||
{
|
||||
if (handle_module_option (unsigned (scode), arg, value))
|
||||
return true;
|
||||
|
||||
enum opt_code code = (enum opt_code) scode;
|
||||
bool handled_p = true;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case OPT_fcontract_build_level_:
|
||||
handle_OPT_fcontract_build_level_ (arg);
|
||||
break;
|
||||
|
||||
case OPT_fcontract_assumption_mode_:
|
||||
handle_OPT_fcontract_assumption_mode_ (arg);
|
||||
break;
|
||||
|
||||
case OPT_fcontract_continuation_mode_:
|
||||
handle_OPT_fcontract_continuation_mode_ (arg);
|
||||
break;
|
||||
|
||||
case OPT_fcontract_role_:
|
||||
handle_OPT_fcontract_role_ (arg);
|
||||
break;
|
||||
|
||||
case OPT_fcontract_semantic_:
|
||||
handle_OPT_fcontract_semantic_ (arg);
|
||||
break;
|
||||
|
||||
default:
|
||||
handled_p = false;
|
||||
break;
|
||||
}
|
||||
if (handled_p)
|
||||
return handled_p;
|
||||
|
||||
return c_common_handle_option (scode, arg, value, kind, loc, handlers);
|
||||
}
|
||||
|
||||
|
||||
@@ -587,6 +587,17 @@ DEFTREECODE (CO_YIELD_EXPR, "co_yield", tcc_expression, 2)
|
||||
|
||||
DEFTREECODE (CO_RETURN_EXPR, "co_return", tcc_statement, 2)
|
||||
|
||||
/* Different flavors of contracts.
|
||||
|
||||
Assertions and preconditions have two operands: a node containing
|
||||
the their mode and condition. Postconditions have an additional
|
||||
operand to store the optional name for the result value.
|
||||
|
||||
CONTRACT_SEMANTIC has the computed behavior of the contract. */
|
||||
DEFTREECODE (ASSERTION_STMT, "assertion_stmt", tcc_statement, 3)
|
||||
DEFTREECODE (PRECONDITION_STMT, "precondition_stmt", tcc_statement, 3)
|
||||
DEFTREECODE (POSTCONDITION_STMT, "postcondition_stmt", tcc_statement, 4)
|
||||
|
||||
/*
|
||||
Local variables:
|
||||
mode:c
|
||||
|
||||
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "hard-reg-set.h"
|
||||
#include "function.h"
|
||||
#include "tristate.h"
|
||||
#include "contracts.h"
|
||||
|
||||
/* In order for the format checking to accept the C++ front end
|
||||
diagnostic framework extensions, you must include this file before
|
||||
@@ -232,6 +233,8 @@ enum cp_tree_index
|
||||
CPTI_DSO_HANDLE,
|
||||
CPTI_DCAST,
|
||||
|
||||
CPTI_PSEUDO_CONTRACT_VIOLATION,
|
||||
|
||||
CPTI_SOURCE_LOCATION_IMPL,
|
||||
|
||||
CPTI_FALLBACK_DFLOAT32_TYPE,
|
||||
@@ -266,6 +269,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
|
||||
#define current_aggr cp_global_trees[CPTI_AGGR_TAG]
|
||||
/* std::align_val_t */
|
||||
#define align_type_node cp_global_trees[CPTI_ALIGN_TYPE]
|
||||
#define pseudo_contract_violation_type cp_global_trees[CPTI_PSEUDO_CONTRACT_VIOLATION]
|
||||
|
||||
/* We cache these tree nodes so as to call get_identifier less frequently.
|
||||
For identifiers for functions, including special member functions such
|
||||
@@ -455,6 +459,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
|
||||
ALIGNOF_EXPR_STD_P (in ALIGNOF_EXPR)
|
||||
OVL_DEDUP_P (in OVERLOAD)
|
||||
ATOMIC_CONSTR_MAP_INSTANTIATED_P (in ATOMIC_CONSTR)
|
||||
contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
|
||||
1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE)
|
||||
TI_PENDING_TEMPLATE_FLAG.
|
||||
TEMPLATE_PARMS_FOR_INLINE.
|
||||
@@ -493,6 +498,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
|
||||
LAMBDA_EXPR_CAPTURE_OPTIMIZED (in LAMBDA_EXPR)
|
||||
IMPLICIT_CONV_EXPR_BRACED_INIT (in IMPLICIT_CONV_EXPR)
|
||||
PACK_EXPANSION_AUTO_P (in *_PACK_EXPANSION)
|
||||
contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
|
||||
3: IMPLICIT_RVALUE_P (in NON_LVALUE_EXPR or STATIC_CAST_EXPR)
|
||||
ICS_BAD_FLAG (in _CONV)
|
||||
FN_TRY_BLOCK_P (in TRY_BLOCK)
|
||||
@@ -505,6 +511,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
|
||||
PACK_EXPANSION_FORCE_EXTRA_ARGS_P (in *_PACK_EXPANSION)
|
||||
LAMBDA_EXPR_STATIC_P (in LAMBDA_EXPR)
|
||||
TARGET_EXPR_ELIDING_P (in TARGET_EXPR)
|
||||
contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
|
||||
4: IDENTIFIER_MARKED (IDENTIFIER_NODEs)
|
||||
TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
|
||||
CALL_EXPR, or FIELD_DECL).
|
||||
@@ -1858,6 +1865,7 @@ struct GTY(()) saved_scope {
|
||||
int x_processing_template_decl;
|
||||
int x_processing_specialization;
|
||||
int x_processing_constraint;
|
||||
int x_processing_contract_condition;
|
||||
int suppress_location_wrappers;
|
||||
BOOL_BITFIELD x_processing_explicit_instantiation : 1;
|
||||
BOOL_BITFIELD need_pop_function_context : 1;
|
||||
@@ -1932,6 +1940,12 @@ extern GTY(()) struct saved_scope *scope_chain;
|
||||
#define processing_specialization scope_chain->x_processing_specialization
|
||||
#define processing_explicit_instantiation scope_chain->x_processing_explicit_instantiation
|
||||
|
||||
/* Nonzero if we are parsing the conditional expression of a contract
|
||||
condition. These expressions appear outside the paramter list (like a
|
||||
trailing return type), but are potentially evaluated. */
|
||||
|
||||
#define processing_contract_condition scope_chain->x_processing_contract_condition
|
||||
|
||||
#define in_discarded_stmt scope_chain->discarded_stmt
|
||||
#define in_consteval_if_p scope_chain->consteval_if_p
|
||||
|
||||
@@ -5646,6 +5660,11 @@ extern int comparing_specializations;
|
||||
FIXME we should always do this except during deduction/ordering. */
|
||||
extern int comparing_dependent_aliases;
|
||||
|
||||
/* Nonzero if we want to consider different member expressions to compare
|
||||
equal if they designate the same entity. This is set when comparing
|
||||
contract conditions of overrides. */
|
||||
extern bool comparing_override_contracts;
|
||||
|
||||
/* In parser.cc. */
|
||||
|
||||
/* Nonzero if we are parsing an unevaluated operand: an operand to
|
||||
@@ -7449,8 +7468,10 @@ extern hashval_t iterative_hash_template_arg (tree arg, hashval_t val);
|
||||
extern tree coerce_template_parms (tree, tree, tree, tsubst_flags_t,
|
||||
bool = true);
|
||||
extern tree canonicalize_type_argument (tree, tsubst_flags_t);
|
||||
extern void register_local_identity (tree);
|
||||
extern void register_local_specialization (tree, tree);
|
||||
extern tree retrieve_local_specialization (tree);
|
||||
extern void register_parameter_specializations (tree, tree);
|
||||
extern tree extract_fnparm_pack (tree, tree *);
|
||||
extern tree template_parm_to_arg (tree);
|
||||
extern tree dguide_name (tree);
|
||||
@@ -8520,6 +8541,49 @@ extern tree coro_get_actor_function (tree);
|
||||
extern tree coro_get_destroy_function (tree);
|
||||
extern tree coro_get_ramp_function (tree);
|
||||
|
||||
/* contracts.cc */
|
||||
extern tree make_postcondition_variable (cp_expr);
|
||||
extern tree make_postcondition_variable (cp_expr, tree);
|
||||
extern tree grok_contract (tree, tree, tree, cp_expr, location_t);
|
||||
extern tree finish_contract_condition (cp_expr);
|
||||
|
||||
/* Return the first contract in ATTRS, or NULL_TREE if there are none. */
|
||||
|
||||
inline tree
|
||||
find_contract (tree attrs)
|
||||
{
|
||||
while (attrs && !cxx_contract_attribute_p (attrs))
|
||||
attrs = TREE_CHAIN (attrs);
|
||||
return attrs;
|
||||
}
|
||||
|
||||
inline void
|
||||
set_decl_contracts (tree decl, tree contract_attrs)
|
||||
{
|
||||
remove_contract_attributes (decl);
|
||||
DECL_ATTRIBUTES (decl) = chainon (DECL_ATTRIBUTES (decl), contract_attrs);
|
||||
}
|
||||
|
||||
/* Returns the computed semantic of the node. */
|
||||
|
||||
inline contract_semantic
|
||||
get_contract_semantic (const_tree t)
|
||||
{
|
||||
return (contract_semantic) (TREE_LANG_FLAG_3 (CONTRACT_CHECK (t))
|
||||
| (TREE_LANG_FLAG_2 (t) << 1)
|
||||
| (TREE_LANG_FLAG_0 ((t)) << 2));
|
||||
}
|
||||
|
||||
/* Sets the computed semantic of the node. */
|
||||
|
||||
inline void
|
||||
set_contract_semantic (tree t, contract_semantic semantic)
|
||||
{
|
||||
TREE_LANG_FLAG_3 (CONTRACT_CHECK (t)) = semantic & 0x01;
|
||||
TREE_LANG_FLAG_2 (t) = (semantic & 0x02) >> 1;
|
||||
TREE_LANG_FLAG_0 (t) = (semantic & 0x04) >> 2;
|
||||
}
|
||||
|
||||
/* Inline bodies. */
|
||||
|
||||
inline tree
|
||||
|
||||
115
gcc/cp/decl.cc
115
gcc/cp/decl.cc
@@ -2202,6 +2202,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
|
||||
= DECL_OVERLOADED_OPERATOR_CODE_RAW (olddecl);
|
||||
new_defines_function = DECL_INITIAL (newdecl) != NULL_TREE;
|
||||
|
||||
duplicate_contracts (newdecl, olddecl);
|
||||
|
||||
/* Optionally warn about more than one declaration for the same
|
||||
name, but don't warn about a function declaration followed by a
|
||||
definition. */
|
||||
@@ -2275,6 +2277,13 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
|
||||
specializations. */
|
||||
gcc_assert (!DECL_TEMPLATE_SPECIALIZATIONS (newdecl));
|
||||
|
||||
/* Make sure the contracts are equivalent. */
|
||||
duplicate_contracts (newdecl, olddecl);
|
||||
|
||||
/* Remove contracts from old_result so they aren't appended to
|
||||
old_result by the merge function. */
|
||||
remove_contract_attributes (old_result);
|
||||
|
||||
DECL_ATTRIBUTES (old_result)
|
||||
= (*targetm.merge_decl_attributes) (old_result, new_result);
|
||||
|
||||
@@ -2797,11 +2806,23 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
|
||||
}
|
||||
if (! types_match || new_defines_function)
|
||||
{
|
||||
/* These are the final DECL_ARGUMENTS that will be used within the
|
||||
body; update any references to old DECL_ARGUMENTS in the
|
||||
contracts, if present. */
|
||||
if (tree contracts = DECL_CONTRACTS (newdecl))
|
||||
remap_contracts (olddecl, newdecl, contracts, true);
|
||||
|
||||
/* These need to be copied so that the names are available.
|
||||
Note that if the types do match, we'll preserve inline
|
||||
info and other bits, but if not, we won't. */
|
||||
DECL_ARGUMENTS (olddecl) = DECL_ARGUMENTS (newdecl);
|
||||
DECL_RESULT (olddecl) = DECL_RESULT (newdecl);
|
||||
|
||||
/* In some cases, duplicate_contracts will remove contracts from
|
||||
OLDDECL, to avoid duplications. Sometimes, the contracts end up
|
||||
shared. If we removed them, re-add them. */
|
||||
if (!DECL_CONTRACTS (olddecl))
|
||||
copy_contract_attributes (olddecl, newdecl);
|
||||
}
|
||||
/* If redeclaring a builtin function, it stays built in
|
||||
if newdecl is a gnu_inline definition, or if newdecl is just
|
||||
@@ -2845,7 +2866,38 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
|
||||
/* Don't clear out the arguments if we're just redeclaring a
|
||||
function. */
|
||||
if (DECL_ARGUMENTS (olddecl))
|
||||
DECL_ARGUMENTS (newdecl) = DECL_ARGUMENTS (olddecl);
|
||||
{
|
||||
/* If we removed contracts from previous definition, re-attach
|
||||
them. Otherwise, rewrite the contracts so they match the
|
||||
parameters of the new declaration. */
|
||||
if (DECL_INITIAL (olddecl)
|
||||
&& DECL_CONTRACTS (newdecl)
|
||||
&& !DECL_CONTRACTS (olddecl))
|
||||
copy_contract_attributes (olddecl, newdecl);
|
||||
else
|
||||
{
|
||||
/* Temporarily undo the re-contexting of parameters so we can
|
||||
actually remap parameters. The inliner won't replace
|
||||
parameters if we don't do this. */
|
||||
tree args = DECL_ARGUMENTS (newdecl);
|
||||
for (tree p = args; p; p = DECL_CHAIN (p))
|
||||
DECL_CONTEXT (p) = newdecl;
|
||||
|
||||
/* Save new argument names for use in contracts parsing,
|
||||
unless we've already started parsing the body of olddecl
|
||||
(particular issues arise when newdecl is from a prior
|
||||
friend decl with no argument names, see
|
||||
modules/contracts-tpl-friend-1). */
|
||||
if (tree contracts = DECL_CONTRACTS (olddecl))
|
||||
remap_contracts (newdecl, olddecl, contracts, true);
|
||||
|
||||
/* And reverse this operation again. */
|
||||
for (tree p = args; p; p = DECL_CHAIN (p))
|
||||
DECL_CONTEXT (p) = olddecl;
|
||||
}
|
||||
|
||||
DECL_ARGUMENTS (newdecl) = DECL_ARGUMENTS (olddecl);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (TREE_CODE (newdecl) == NAMESPACE_DECL)
|
||||
@@ -5456,6 +5508,12 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
|
||||
warn_misplaced_attr_for_class_type (loc, declared_type);
|
||||
}
|
||||
|
||||
/* Diagnose invalid application of contracts, if any. */
|
||||
if (find_contract (declspecs->attributes))
|
||||
diagnose_misapplied_contracts (declspecs->attributes);
|
||||
else
|
||||
diagnose_misapplied_contracts (declspecs->std_attributes);
|
||||
|
||||
return declared_type;
|
||||
}
|
||||
|
||||
@@ -5747,9 +5805,16 @@ start_decl (const cp_declarator *declarator,
|
||||
if (DECL_EXTERNAL (decl) && ! DECL_TEMPLATE_SPECIALIZATION (decl)
|
||||
/* Aliases are definitions. */
|
||||
&& !alias)
|
||||
permerror (declarator->id_loc,
|
||||
"declaration of %q#D outside of class is not definition",
|
||||
decl);
|
||||
{
|
||||
if (DECL_VIRTUAL_P (decl) || !flag_contracts)
|
||||
permerror (declarator->id_loc,
|
||||
"declaration of %q#D outside of class is not definition",
|
||||
decl);
|
||||
else if (flag_contract_strict_declarations)
|
||||
warning_at (declarator->id_loc, OPT_fcontract_strict_declarations_,
|
||||
"declaration of %q#D outside of class is not definition",
|
||||
decl);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a DECL_LANG_SPECIFIC so that DECL_DECOMPOSITION_P works. */
|
||||
@@ -10531,6 +10596,9 @@ grokfndecl (tree ctype,
|
||||
*attrlist = NULL_TREE;
|
||||
}
|
||||
|
||||
if (DECL_HAS_CONTRACTS_P (decl))
|
||||
rebuild_postconditions (decl);
|
||||
|
||||
/* Check main's type after attributes have been applied. */
|
||||
if (ctype == NULL_TREE && DECL_MAIN_P (decl))
|
||||
{
|
||||
@@ -12731,7 +12799,8 @@ grokdeclarator (const cp_declarator *declarator,
|
||||
}
|
||||
}
|
||||
|
||||
if (declspecs->std_attributes)
|
||||
if (declspecs->std_attributes
|
||||
&& !diagnose_misapplied_contracts (declspecs->std_attributes))
|
||||
{
|
||||
location_t attr_loc = declspecs->locations[ds_std_attribute];
|
||||
if (warning_at (attr_loc, OPT_Wattributes, "attribute ignored"))
|
||||
@@ -12739,6 +12808,9 @@ grokdeclarator (const cp_declarator *declarator,
|
||||
"is ignored");
|
||||
}
|
||||
|
||||
if (attrlist)
|
||||
diagnose_misapplied_contracts (*attrlist);
|
||||
|
||||
/* Determine the type of the entity declared by recurring on the
|
||||
declarator. */
|
||||
for (; declarator; declarator = declarator->declarator)
|
||||
@@ -12776,6 +12848,12 @@ grokdeclarator (const cp_declarator *declarator,
|
||||
|
||||
inner_declarator = declarator->declarator;
|
||||
|
||||
/* Check that contracts aren't misapplied. */
|
||||
if (tree contract_attr = find_contract (declarator->std_attributes))
|
||||
if (declarator->kind != cdk_function
|
||||
|| innermost_code != cdk_function)
|
||||
diagnose_misapplied_contracts (contract_attr);
|
||||
|
||||
/* We don't want to warn in parameter context because we don't
|
||||
yet know if the parse will succeed, and this might turn out
|
||||
to be a constructor call. */
|
||||
@@ -13168,6 +13246,23 @@ grokdeclarator (const cp_declarator *declarator,
|
||||
else
|
||||
returned_attrs = attr_chainon (returned_attrs, att);
|
||||
}
|
||||
|
||||
/* Actually apply the contract attributes to the declaration. */
|
||||
for (tree *p = &attrs; *p;)
|
||||
{
|
||||
tree l = *p;
|
||||
if (cxx_contract_attribute_p (l))
|
||||
{
|
||||
*p = TREE_CHAIN (l);
|
||||
/* Intentionally reverse order of contracts so they're
|
||||
reversed back into their lexical order. */
|
||||
TREE_CHAIN (l) = NULL_TREE;
|
||||
returned_attrs = chainon (l, returned_attrs);
|
||||
}
|
||||
else
|
||||
p = &TREE_CHAIN (l);
|
||||
}
|
||||
|
||||
if (attrs)
|
||||
/* [dcl.fct]/2:
|
||||
|
||||
@@ -14190,7 +14285,10 @@ grokdeclarator (const cp_declarator *declarator,
|
||||
{
|
||||
/* Packages tend to use GNU attributes on friends, so we only
|
||||
warn for standard attributes. */
|
||||
if (attrlist && !funcdef_flag && cxx11_attribute_p (*attrlist))
|
||||
if (attrlist
|
||||
&& !funcdef_flag
|
||||
&& cxx11_attribute_p (*attrlist)
|
||||
&& !all_attributes_are_contracts_p (*attrlist))
|
||||
{
|
||||
*attrlist = NULL_TREE;
|
||||
if (warning_at (id_loc, OPT_Wattributes, "attribute ignored"))
|
||||
@@ -17410,6 +17508,8 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
|
||||
|
||||
store_parm_decls (current_function_parms);
|
||||
|
||||
start_function_contracts (decl1);
|
||||
|
||||
if (!processing_template_decl
|
||||
&& (flag_lifetime_dse > 1)
|
||||
&& DECL_CONSTRUCTOR_P (decl1)
|
||||
@@ -18130,6 +18230,9 @@ finish_function (bool inline_p)
|
||||
current_function_decl = NULL_TREE;
|
||||
|
||||
invoke_plugin_callbacks (PLUGIN_FINISH_PARSE_FUNCTION, fndecl);
|
||||
|
||||
finish_function_contracts (fndecl);
|
||||
|
||||
return fndecl;
|
||||
}
|
||||
|
||||
|
||||
@@ -1561,6 +1561,9 @@ cp_check_const_attributes (tree attributes)
|
||||
tree attr;
|
||||
for (attr = attributes; attr; attr = TREE_CHAIN (attr))
|
||||
{
|
||||
if (cxx_contract_attribute_p (attr))
|
||||
continue;
|
||||
|
||||
tree arg;
|
||||
/* As we implement alignas using gnu::aligned attribute and
|
||||
alignas argument is a constant expression, force manifestly
|
||||
@@ -2106,7 +2109,17 @@ void
|
||||
comdat_linkage (tree decl)
|
||||
{
|
||||
if (flag_weak)
|
||||
make_decl_one_only (decl, cxx_comdat_group (decl));
|
||||
{
|
||||
make_decl_one_only (decl, cxx_comdat_group (decl));
|
||||
if (HAVE_COMDAT_GROUP && flag_contracts && DECL_CONTRACTS (decl))
|
||||
{
|
||||
symtab_node *n = symtab_node::get (decl);
|
||||
if (tree pre = DECL_PRE_FN (decl))
|
||||
cgraph_node::get_create (pre)->add_to_same_comdat_group (n);
|
||||
if (tree post = DECL_POST_FN (decl))
|
||||
cgraph_node::get_create (post)->add_to_same_comdat_group (n);
|
||||
}
|
||||
}
|
||||
else if (TREE_CODE (decl) == FUNCTION_DECL
|
||||
|| (VAR_P (decl) && DECL_ARTIFICIAL (decl)))
|
||||
/* We can just emit function and compiler-generated variables
|
||||
|
||||
@@ -567,7 +567,8 @@ dump_type (cxx_pretty_printer *pp, tree t, int flags)
|
||||
else
|
||||
{
|
||||
pp_cxx_cv_qualifier_seq (pp, t);
|
||||
pp_cxx_tree_identifier (pp, TYPE_IDENTIFIER (t));
|
||||
if (tree id = TYPE_IDENTIFIER (t))
|
||||
pp_cxx_tree_identifier (pp, id);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ along with GCC; see the file COPYING3. If not see
|
||||
#define WITHLIBC (1<<3)
|
||||
/* Skip this option. */
|
||||
#define SKIPOPT (1<<4)
|
||||
/* Add -lstdc++exp for experimental features that need library support. */
|
||||
#define EXPERIMENTAL (1<<5)
|
||||
|
||||
#ifndef MATH_LIBRARY
|
||||
#define MATH_LIBRARY "m"
|
||||
@@ -158,6 +160,11 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options,
|
||||
|
||||
switch (decoded_options[i].opt_index)
|
||||
{
|
||||
case OPT_fcontracts:
|
||||
args[i] |= EXPERIMENTAL;
|
||||
++added;
|
||||
break;
|
||||
|
||||
case OPT_nostdlib:
|
||||
case OPT_nostdlib__:
|
||||
case OPT_nodefaultlibs:
|
||||
@@ -348,6 +355,11 @@ lang_specific_driver (struct cl_decoded_option **in_decoded_options,
|
||||
&new_decoded_options[j]);
|
||||
}
|
||||
|
||||
if ((args[i] & EXPERIMENTAL)
|
||||
&& which_library == USE_LIBSTDCXX)
|
||||
generate_option (OPT_l, "stdc++exp", 1, CL_DRIVER,
|
||||
&new_decoded_options[++j]);
|
||||
|
||||
if ((args[i] & SKIPOPT) != 0)
|
||||
--j;
|
||||
|
||||
|
||||
@@ -856,6 +856,13 @@ write_encoding (const tree decl)
|
||||
mangle_return_type_p (decl),
|
||||
d);
|
||||
|
||||
/* If this is the pre/post function for a guarded function, append
|
||||
.pre/post, like something from create_virtual_clone. */
|
||||
if (DECL_IS_PRE_FN_P (decl))
|
||||
write_string (".pre");
|
||||
else if (DECL_IS_POST_FN_P (decl))
|
||||
write_string (".post");
|
||||
|
||||
/* If this is a coroutine helper, then append an appropriate string to
|
||||
identify which. */
|
||||
if (tree ramp = DECL_RAMP_FN (decl))
|
||||
|
||||
@@ -10184,6 +10184,20 @@ trees_out::fn_parms_init (tree fn)
|
||||
base_tag - ix, ix, parm, fn);
|
||||
tree_node_vals (parm);
|
||||
}
|
||||
|
||||
if (!streaming_p ())
|
||||
{
|
||||
/* We must walk contract attrs so the dependency graph is complete. */
|
||||
for (tree contract = DECL_CONTRACTS (fn);
|
||||
contract;
|
||||
contract = CONTRACT_CHAIN (contract))
|
||||
tree_node (contract);
|
||||
}
|
||||
|
||||
/* Write a reference to contracts pre/post functions, if any, to avoid
|
||||
regenerating them in importers. */
|
||||
tree_node (DECL_PRE_FN (fn));
|
||||
tree_node (DECL_POST_FN (fn));
|
||||
}
|
||||
|
||||
/* Build skeleton parm nodes, read their flags, type & parm indices. */
|
||||
@@ -10218,6 +10232,11 @@ trees_in::fn_parms_init (tree fn)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reload references to contract functions, if any. */
|
||||
tree pre_fn = tree_node ();
|
||||
tree post_fn = tree_node ();
|
||||
set_contract_functions (fn, pre_fn, post_fn);
|
||||
|
||||
return base_tag;
|
||||
}
|
||||
|
||||
@@ -10805,7 +10824,15 @@ check_mergeable_decl (merge_kind mk, tree decl, tree ovl, merge_key const &key)
|
||||
Matches decls_match behaviour. */
|
||||
&& (!DECL_IS_UNDECLARED_BUILTIN (m_inner)
|
||||
|| !DECL_EXTERN_C_P (m_inner)
|
||||
|| DECL_EXTERN_C_P (d_inner)))
|
||||
|| DECL_EXTERN_C_P (d_inner))
|
||||
/* Reject if one is a different member of a
|
||||
guarded/pre/post fn set. */
|
||||
&& (!flag_contracts
|
||||
|| (DECL_IS_PRE_FN_P (d_inner)
|
||||
== DECL_IS_PRE_FN_P (m_inner)))
|
||||
&& (!flag_contracts
|
||||
|| (DECL_IS_POST_FN_P (d_inner)
|
||||
== DECL_IS_POST_FN_P (m_inner))))
|
||||
{
|
||||
tree m_reqs = get_constraints (m_inner);
|
||||
if (m_reqs)
|
||||
@@ -14573,6 +14600,7 @@ module_state_config::get_dialect ()
|
||||
cxx_dialect < cxx20 && flag_concepts ? "/concepts" : "",
|
||||
flag_coroutines ? "/coroutines" : "",
|
||||
flag_module_implicit_inline ? "/implicit-inline" : "",
|
||||
flag_contracts ? "/contracts" : "",
|
||||
NULL);
|
||||
|
||||
return dialect;
|
||||
|
||||
522
gcc/cp/parser.cc
522
gcc/cp/parser.cc
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "cp-name-hint.h"
|
||||
#include "memmodel.h"
|
||||
#include "c-family/known-headers.h"
|
||||
#include "contracts.h"
|
||||
#include "bitmap.h"
|
||||
|
||||
|
||||
@@ -2192,11 +2193,14 @@ cp_parser_context_new (cp_parser_context* next)
|
||||
parser->unparsed_queues->last ().nsdmis
|
||||
#define unparsed_noexcepts \
|
||||
parser->unparsed_queues->last ().noexcepts
|
||||
#define unparsed_contracts \
|
||||
parser->unparsed_queues->last ().contracts
|
||||
|
||||
static void
|
||||
push_unparsed_function_queues (cp_parser *parser)
|
||||
{
|
||||
cp_unparsed_functions_entry e = { NULL, make_tree_vector (), NULL, NULL };
|
||||
cp_unparsed_functions_entry e
|
||||
= { NULL, make_tree_vector (), NULL, NULL, NULL };
|
||||
vec_safe_push (parser->unparsed_queues, e);
|
||||
}
|
||||
|
||||
@@ -2695,6 +2699,10 @@ static tree cp_parser_transaction_cancel
|
||||
static tree cp_parser_yield_expression
|
||||
(cp_parser *);
|
||||
|
||||
/* Contracts */
|
||||
|
||||
static void cp_parser_late_contract_condition
|
||||
(cp_parser *, tree, tree);
|
||||
|
||||
enum pragma_context {
|
||||
pragma_external,
|
||||
@@ -2907,6 +2915,8 @@ static bool cp_parser_array_designator_p
|
||||
(cp_parser *);
|
||||
static bool cp_parser_init_statement_p
|
||||
(cp_parser *);
|
||||
static bool cp_parser_skip_up_to_closing_square_bracket
|
||||
(cp_parser *);
|
||||
static bool cp_parser_skip_to_closing_square_bracket
|
||||
(cp_parser *);
|
||||
static size_t cp_parser_skip_balanced_tokens (cp_parser *, size_t);
|
||||
@@ -12024,6 +12034,16 @@ cp_parser_handle_statement_omp_attributes (cp_parser *parser, tree attrs)
|
||||
return attrs;
|
||||
}
|
||||
|
||||
/* True if and only if the name is one of the contract types. */
|
||||
|
||||
static bool
|
||||
contract_attribute_p (const_tree id)
|
||||
{
|
||||
return is_attribute_p ("assert", id)
|
||||
|| is_attribute_p ("pre", id)
|
||||
|| is_attribute_p ("post", id);
|
||||
}
|
||||
|
||||
/* Handle omp::directive and omp::sequence attributes in *PATTRS
|
||||
(if any) at the start or after declaration-id of a declaration. */
|
||||
|
||||
@@ -12157,7 +12177,10 @@ cp_parser_handle_directive_omp_attributes (cp_parser *parser, tree *pattrs,
|
||||
is a (possibly labeled) if statement which is not enclosed in braces
|
||||
and has an else clause. This is used to implement -Wparentheses.
|
||||
|
||||
CHAIN is a vector of if-else-if conditions. */
|
||||
CHAIN is a vector of if-else-if conditions.
|
||||
|
||||
Note that this version of parsing restricts assertions to be attached to
|
||||
empty statements. */
|
||||
|
||||
static void
|
||||
cp_parser_statement (cp_parser* parser, tree in_statement_expr,
|
||||
@@ -12199,6 +12222,23 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
|
||||
|
||||
/* Peek at the next token. */
|
||||
token = cp_lexer_peek_token (parser->lexer);
|
||||
|
||||
/* If we have contracts, check that they're valid in this context. */
|
||||
if (std_attrs != error_mark_node)
|
||||
{
|
||||
if (tree pre = lookup_attribute ("pre", std_attrs))
|
||||
error_at (EXPR_LOCATION (TREE_VALUE (pre)),
|
||||
"preconditions cannot be statements");
|
||||
else if (tree post = lookup_attribute ("post", std_attrs))
|
||||
error_at (EXPR_LOCATION (TREE_VALUE (post)),
|
||||
"postconditions cannot be statements");
|
||||
|
||||
/* Check that assertions are null statements. */
|
||||
if (cp_contract_assertion_p (std_attrs))
|
||||
if (token->type != CPP_SEMICOLON)
|
||||
error_at (token->location, "assertions must be followed by %<;%>");
|
||||
}
|
||||
|
||||
bool omp_attrs_forbidden_p;
|
||||
omp_attrs_forbidden_p = parser->omp_attrs_forbidden_p;
|
||||
|
||||
@@ -12514,6 +12554,15 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
|
||||
"%<fallthrough%> attribute not followed by %<;%>");
|
||||
std_attrs = NULL_TREE;
|
||||
}
|
||||
|
||||
/* Handle [[assert: ...]]; */
|
||||
if (cp_contract_assertion_p (std_attrs))
|
||||
{
|
||||
/* Add the assertion as a statement in the current block. */
|
||||
gcc_assert (!statement || statement == error_mark_node);
|
||||
emit_assertion (std_attrs);
|
||||
std_attrs = NULL_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the line number for the statement. */
|
||||
@@ -15697,7 +15746,12 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
|
||||
declared. */;
|
||||
else
|
||||
{
|
||||
if (decl_specs->type && CLASS_TYPE_P (decl_specs->type))
|
||||
if (find_contract (attrs))
|
||||
{
|
||||
diagnose_misapplied_contracts (attrs);
|
||||
attrs = NULL_TREE;
|
||||
}
|
||||
else if (decl_specs->type && CLASS_TYPE_P (decl_specs->type))
|
||||
{
|
||||
/* This is an attribute following a
|
||||
class-specifier. */
|
||||
@@ -25398,11 +25452,11 @@ cp_parser_braced_list (cp_parser* parser, bool* non_constant_p)
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Consume tokens up to, and including, the next non-nested closing `]'.
|
||||
/* Consume tokens up to, but not including, the next non-nested closing `]'.
|
||||
Returns true iff we found a closing `]'. */
|
||||
|
||||
static bool
|
||||
cp_parser_skip_to_closing_square_bracket (cp_parser *parser)
|
||||
cp_parser_skip_up_to_closing_square_bracket (cp_parser *parser)
|
||||
{
|
||||
unsigned square_depth = 0;
|
||||
|
||||
@@ -25427,21 +25481,30 @@ cp_parser_skip_to_closing_square_bracket (cp_parser *parser)
|
||||
|
||||
case CPP_CLOSE_SQUARE:
|
||||
if (!square_depth--)
|
||||
{
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Consume the token. */
|
||||
/* Consume the current token, skipping it. */
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
}
|
||||
}
|
||||
|
||||
/* Consume tokens up to, and including, the next non-nested closing `]'.
|
||||
Returns true iff we found a closing `]'. */
|
||||
|
||||
static bool
|
||||
cp_parser_skip_to_closing_square_bracket (cp_parser *parser)
|
||||
{
|
||||
bool found = cp_parser_skip_up_to_closing_square_bracket (parser);
|
||||
if (found)
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
return found;
|
||||
}
|
||||
|
||||
/* Return true if we are looking at an array-designator, false otherwise. */
|
||||
|
||||
static bool
|
||||
@@ -26284,6 +26347,56 @@ cp_parser_class_specifier (cp_parser* parser)
|
||||
cp_parser_late_parsing_nsdmi (parser, decl);
|
||||
}
|
||||
vec_safe_truncate (unparsed_nsdmis, 0);
|
||||
|
||||
/* Now contract attributes. */
|
||||
FOR_EACH_VEC_SAFE_ELT (unparsed_contracts, ix, decl)
|
||||
{
|
||||
tree ctx = DECL_CONTEXT (decl);
|
||||
if (class_type != ctx)
|
||||
{
|
||||
if (pushed_scope)
|
||||
pop_scope (pushed_scope);
|
||||
class_type = ctx;
|
||||
pushed_scope = push_scope (class_type);
|
||||
}
|
||||
|
||||
temp_override<tree> cfd(current_function_decl, decl);
|
||||
|
||||
/* Make sure that any template parameters are in scope. */
|
||||
maybe_begin_member_template_processing (decl);
|
||||
|
||||
/* Make sure that any member-function parameters are in scope.
|
||||
This function doesn't expect ccp to be set. */
|
||||
current_class_ptr = current_class_ref = NULL_TREE;
|
||||
inject_parm_decls (decl);
|
||||
|
||||
/* 'this' is not allowed in static member functions. */
|
||||
unsigned char local_variables_forbidden_p
|
||||
= parser->local_variables_forbidden_p;
|
||||
if (DECL_THIS_STATIC (decl))
|
||||
parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
|
||||
|
||||
/* Now we can parse contract conditions. */
|
||||
for (tree a = DECL_ATTRIBUTES (decl); a; a = TREE_CHAIN (a))
|
||||
{
|
||||
if (cxx_contract_attribute_p (a))
|
||||
cp_parser_late_contract_condition (parser, decl, a);
|
||||
}
|
||||
|
||||
/* Restore the state of local_variables_forbidden_p. */
|
||||
parser->local_variables_forbidden_p = local_variables_forbidden_p;
|
||||
|
||||
/* Remove any member-function parameters from the symbol table. */
|
||||
pop_injected_parms ();
|
||||
|
||||
/* Remove any template parameters from the symbol table. */
|
||||
maybe_end_member_template_processing ();
|
||||
|
||||
/* Perform any deferred contract matching. */
|
||||
match_deferred_contracts (decl);
|
||||
}
|
||||
vec_safe_truncate (unparsed_contracts, 0);
|
||||
|
||||
current_class_ptr = save_ccp;
|
||||
current_class_ref = save_ccr;
|
||||
if (pushed_scope)
|
||||
@@ -26387,6 +26500,8 @@ cp_parser_class_head (cp_parser* parser,
|
||||
|
||||
/* Parse the attributes. */
|
||||
attributes = cp_parser_attributes_opt (parser);
|
||||
if (find_contract (attributes))
|
||||
diagnose_misapplied_contracts (attributes);
|
||||
|
||||
/* If the next token is `::', that is invalid -- but sometimes
|
||||
people do try to write:
|
||||
@@ -29317,10 +29432,361 @@ cp_parser_std_attribute_list (cp_parser *parser, tree attr_ns)
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/* Optionally parse a C++20 contract role. A NULL return means that no
|
||||
contract role was specified.
|
||||
|
||||
contract-role:
|
||||
% default
|
||||
% identifier
|
||||
|
||||
If the identifier does not name a known contract role, it will
|
||||
be assumed to be default. Returns the identifier for the role
|
||||
token. */
|
||||
|
||||
static tree
|
||||
cp_parser_contract_role (cp_parser *parser)
|
||||
{
|
||||
gcc_assert (cp_lexer_next_token_is (parser->lexer, CPP_MOD));
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
|
||||
cp_token *token = cp_lexer_peek_token (parser->lexer);
|
||||
tree role_id = NULL_TREE;
|
||||
if (token->type == CPP_NAME)
|
||||
role_id = token->u.value;
|
||||
else if (token->type == CPP_KEYWORD && token->keyword == RID_DEFAULT)
|
||||
role_id = get_identifier ("default");
|
||||
else
|
||||
{
|
||||
error_at (token->location, "expected contract-role");
|
||||
return error_mark_node;
|
||||
}
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
|
||||
/* FIXME: Warn about invalid/unknown roles? */
|
||||
return role_id;
|
||||
}
|
||||
|
||||
/* Parse an optional contract mode.
|
||||
|
||||
contract-mode:
|
||||
contract-semantic
|
||||
[contract-level] [contract-role]
|
||||
|
||||
contract-semantic:
|
||||
check_never_continue
|
||||
check_maybe_continue
|
||||
check_always_continue
|
||||
|
||||
contract-level:
|
||||
default
|
||||
audit
|
||||
axiom
|
||||
|
||||
contract-role:
|
||||
default
|
||||
identifier
|
||||
|
||||
This grammar is taken from P1332R0. During parsing, this sets options
|
||||
on the MODE object to determine the configuration of the contract.
|
||||
|
||||
Returns a tree containing the identifiers used in the configuration.
|
||||
This is either an IDENTIFIER with the literal semantic or a TREE_LIST
|
||||
whose TREE_VALUE is the contract-level and whose TREE_PURPOSE is the
|
||||
contract-role, if any. NULL_TREE is returned if no information is
|
||||
given (i.e., all defaults selected). */
|
||||
|
||||
static tree
|
||||
cp_parser_contract_mode_opt (cp_parser *parser,
|
||||
bool postcondition_p)
|
||||
{
|
||||
/* The mode is empty; the level and role are default. */
|
||||
if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
|
||||
return NULL_TREE;
|
||||
|
||||
/* There is only a role; the level is default. */
|
||||
if (cp_lexer_next_token_is (parser->lexer, CPP_MOD))
|
||||
{
|
||||
tree role_id = cp_parser_contract_role (parser);
|
||||
return build_tree_list (role_id, get_identifier ("default"));
|
||||
}
|
||||
|
||||
/* Otherwise, match semantic or level. */
|
||||
cp_token *token = cp_lexer_peek_token (parser->lexer);
|
||||
contract_level level = CONTRACT_INVALID;
|
||||
contract_semantic semantic = CCS_INVALID;
|
||||
tree config_id;
|
||||
if (token->type == CPP_NAME)
|
||||
{
|
||||
config_id = token->u.value;
|
||||
|
||||
/* Either a named level, a concrete semantic, or an identifier
|
||||
for a postcondition. */
|
||||
const char *ident = IDENTIFIER_POINTER (token->u.value);
|
||||
level = map_contract_level (ident);
|
||||
semantic = map_contract_semantic (ident);
|
||||
|
||||
/* The identifier is the return value for a postcondition. */
|
||||
if (level == CONTRACT_INVALID && semantic == CCS_INVALID
|
||||
&& postcondition_p)
|
||||
return NULL_TREE;
|
||||
}
|
||||
else if (token->type == CPP_KEYWORD && token->keyword == RID_DEFAULT)
|
||||
{
|
||||
config_id = get_identifier ("default");
|
||||
level = CONTRACT_DEFAULT;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We got some other token other than a ':'. */
|
||||
error_at (token->location, "expected contract semantic or level");
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Consume the literal semantic or level token. */
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
|
||||
if (semantic == CCS_INVALID && level == CONTRACT_INVALID)
|
||||
{
|
||||
error_at (token->location,
|
||||
"expected contract level: "
|
||||
"%<default%>, %<audit%>, or %<axiom%>");
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* We matched an explicit semantic. */
|
||||
if (semantic != CCS_INVALID)
|
||||
{
|
||||
if (cp_lexer_next_token_is (parser->lexer, CPP_MOD))
|
||||
{
|
||||
error ("invalid use of contract role for explicit semantic");
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
}
|
||||
return config_id;
|
||||
}
|
||||
|
||||
/* We matched a level, there may be a role; otherwise this is default. */
|
||||
if (cp_lexer_next_token_is (parser->lexer, CPP_MOD))
|
||||
{
|
||||
tree role_id = cp_parser_contract_role (parser);
|
||||
return build_tree_list (role_id, config_id);
|
||||
}
|
||||
|
||||
return build_tree_list (NULL_TREE, config_id);
|
||||
}
|
||||
|
||||
static tree
|
||||
find_error (tree *tp, int *, void *)
|
||||
{
|
||||
if (*tp == error_mark_node)
|
||||
return *tp;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
static bool
|
||||
contains_error_p (tree t)
|
||||
{
|
||||
return walk_tree (&t, find_error, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Parse a standard C++20 contract attribute specifier.
|
||||
|
||||
contract-attribute-specifier:
|
||||
[ [ assert contract-level [opt] : conditional-expression ] ]
|
||||
[ [ pre contract-level [opt] : conditional-expression ] ]
|
||||
[ [ post contract-level [opt] identifier [opt] : conditional-expression ] ]
|
||||
|
||||
For free functions, we cannot determine the type of the postcondition
|
||||
identifier because the we haven't called grokdeclarator yet. In those
|
||||
cases we parse the postcondition as if the identifier was declared as
|
||||
'auto <identifier>'. We then instantiate the postcondition once the
|
||||
return type is known.
|
||||
|
||||
For member functions, contracts are in the complete-class context, so the
|
||||
parse is deferred. We also have the return type avaialable (unless it's
|
||||
deduced), so we don't need to parse the postcondition in terms of a
|
||||
placeholder. */
|
||||
|
||||
static tree
|
||||
cp_parser_contract_attribute_spec (cp_parser *parser, tree attribute)
|
||||
{
|
||||
gcc_assert (contract_attribute_p (attribute));
|
||||
cp_token *token = cp_lexer_consume_token (parser->lexer);
|
||||
location_t loc = token->location;
|
||||
|
||||
bool assertion_p = is_attribute_p ("assert", attribute);
|
||||
bool postcondition_p = is_attribute_p ("post", attribute);
|
||||
|
||||
/* Parse the optional mode. */
|
||||
tree mode = cp_parser_contract_mode_opt (parser, postcondition_p);
|
||||
|
||||
/* Check for postcondition identifiers. */
|
||||
cp_expr identifier;
|
||||
if (postcondition_p && cp_lexer_next_token_is (parser->lexer, CPP_NAME))
|
||||
identifier = cp_parser_identifier (parser);
|
||||
if (identifier == error_mark_node)
|
||||
return error_mark_node;
|
||||
|
||||
cp_parser_require (parser, CPP_COLON, RT_COLON);
|
||||
|
||||
/* Defer the parsing of pre/post contracts inside class definitions. */
|
||||
tree contract;
|
||||
if (!assertion_p &&
|
||||
current_class_type &&
|
||||
TYPE_BEING_DEFINED (current_class_type))
|
||||
{
|
||||
/* Skip until we reach an unenclose ']'. If we ran into an unnested ']'
|
||||
that doesn't close the attribute, return an error and let the attribute
|
||||
handling code emit an error for missing ']]'. */
|
||||
cp_token *first = cp_lexer_peek_token (parser->lexer);
|
||||
cp_parser_skip_to_closing_parenthesis_1 (parser,
|
||||
/*recovering=*/false,
|
||||
CPP_CLOSE_SQUARE,
|
||||
/*consume_paren=*/false);
|
||||
if (cp_lexer_peek_token (parser->lexer)->type != CPP_CLOSE_SQUARE
|
||||
|| cp_lexer_peek_nth_token (parser->lexer, 2)->type != CPP_CLOSE_SQUARE)
|
||||
return error_mark_node;
|
||||
cp_token *last = cp_lexer_peek_token (parser->lexer);
|
||||
|
||||
/* Build a deferred-parse node. */
|
||||
tree condition = make_node (DEFERRED_PARSE);
|
||||
DEFPARSE_TOKENS (condition) = cp_token_cache_new (first, last);
|
||||
DEFPARSE_INSTANTIATIONS (condition) = NULL;
|
||||
|
||||
/* And its corresponding contract. */
|
||||
contract = grok_contract (attribute, mode, identifier, condition, loc);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Enable location wrappers when parsing contracts. */
|
||||
auto suppression = make_temp_override (suppress_location_wrappers, 0);
|
||||
|
||||
/* Build a fake variable for the result identifier. */
|
||||
tree result = NULL_TREE;
|
||||
if (identifier)
|
||||
{
|
||||
begin_scope (sk_block, NULL_TREE);
|
||||
result = make_postcondition_variable (identifier);
|
||||
++processing_template_decl;
|
||||
}
|
||||
|
||||
/* Parse the condition, ensuring that parameters or the return variable
|
||||
aren't flagged for use outside the body of a function. */
|
||||
++processing_contract_condition;
|
||||
cp_expr condition = cp_parser_conditional_expression (parser);
|
||||
--processing_contract_condition;
|
||||
|
||||
/* Try to recover from errors by scanning up to the end of the
|
||||
attribute. Sometimes we get partially parsed expressions, so
|
||||
we need to search the condition for errors. */
|
||||
if (contains_error_p (condition))
|
||||
cp_parser_skip_up_to_closing_square_bracket (parser);
|
||||
|
||||
/* Build the contract. */
|
||||
contract = grok_contract (attribute, mode, result, condition, loc);
|
||||
|
||||
/* Leave our temporary scope for the postcondition result. */
|
||||
if (result)
|
||||
{
|
||||
--processing_template_decl;
|
||||
pop_bindings_and_leave_scope ();
|
||||
}
|
||||
}
|
||||
|
||||
if (!flag_contracts)
|
||||
{
|
||||
error_at (loc, "contracts are only available with %<-fcontracts%>");
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
return finish_contract_attribute (attribute, contract);
|
||||
}
|
||||
|
||||
/* Parse a contract condition for a deferred contract. */
|
||||
|
||||
void cp_parser_late_contract_condition (cp_parser *parser,
|
||||
tree fn,
|
||||
tree attribute)
|
||||
{
|
||||
tree contract = TREE_VALUE (TREE_VALUE (attribute));
|
||||
|
||||
/* Make sure we've gotten something that hasn't been parsed yet or that
|
||||
we're not parsing an invalid contract. */
|
||||
tree condition = CONTRACT_CONDITION (contract);
|
||||
if (TREE_CODE (condition) != DEFERRED_PARSE)
|
||||
return;
|
||||
|
||||
tree identifier = NULL_TREE;
|
||||
if (TREE_CODE (contract) == POSTCONDITION_STMT)
|
||||
identifier = POSTCONDITION_IDENTIFIER (contract);
|
||||
|
||||
/* Build a fake variable for the result identifier. */
|
||||
tree result = NULL_TREE;
|
||||
if (identifier)
|
||||
{
|
||||
/* TODO: Can we guarantee that the identifier has a location? */
|
||||
location_t loc = cp_expr_location (contract);
|
||||
tree type = TREE_TYPE (TREE_TYPE (fn));
|
||||
if (!check_postcondition_result (fn, type, loc))
|
||||
{
|
||||
invalidate_contract (contract);
|
||||
return;
|
||||
}
|
||||
|
||||
begin_scope (sk_block, NULL_TREE);
|
||||
result = make_postcondition_variable (identifier, type);
|
||||
++processing_template_decl;
|
||||
}
|
||||
|
||||
/* 'this' is not allowed in preconditions of constructors or in postconditions
|
||||
of destructors. Note that the previous value of this variable is
|
||||
established by the calling function, so we need to save it here. */
|
||||
tree saved_ccr = current_class_ref;
|
||||
tree saved_ccp = current_class_ptr;
|
||||
if ((DECL_CONSTRUCTOR_P (fn) && PRECONDITION_P (contract)) ||
|
||||
(DECL_DESTRUCTOR_P (fn) && POSTCONDITION_P (contract)))
|
||||
{
|
||||
current_class_ref = current_class_ptr = NULL_TREE;
|
||||
parser->local_variables_forbidden_p |= THIS_FORBIDDEN;
|
||||
}
|
||||
|
||||
push_unparsed_function_queues (parser);
|
||||
|
||||
/* Push the saved tokens onto the parser's lexer stack. */
|
||||
cp_token_cache *tokens = DEFPARSE_TOKENS (condition);
|
||||
cp_parser_push_lexer_for_tokens (parser, tokens);
|
||||
|
||||
/* Parse the condition, ensuring that parameters or the return variable
|
||||
aren't flagged for use outside the body of a function. */
|
||||
++processing_contract_condition;
|
||||
condition = cp_parser_conditional_expression (parser);
|
||||
--processing_contract_condition;
|
||||
|
||||
/* Revert to the main lexer. */
|
||||
cp_parser_pop_lexer (parser);
|
||||
|
||||
/* Restore the queue. */
|
||||
pop_unparsed_function_queues (parser);
|
||||
|
||||
current_class_ref = saved_ccr;
|
||||
current_class_ptr = saved_ccp;
|
||||
|
||||
/* Commit to changes. */
|
||||
update_late_contract (contract, result, condition);
|
||||
|
||||
/* Leave our temporary scope for the postcondition result. */
|
||||
if (result)
|
||||
{
|
||||
--processing_template_decl;
|
||||
pop_bindings_and_leave_scope ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse a standard C++-11 attribute specifier.
|
||||
|
||||
attribute-specifier:
|
||||
[ [ attribute-using-prefix [opt] attribute-list ] ]
|
||||
contract-attribute-specifier
|
||||
alignment-specifier
|
||||
|
||||
attribute-using-prefix:
|
||||
@@ -29328,7 +29794,15 @@ cp_parser_std_attribute_list (cp_parser *parser, tree attr_ns)
|
||||
|
||||
alignment-specifier:
|
||||
alignas ( type-id ... [opt] )
|
||||
alignas ( alignment-expression ... [opt] ). */
|
||||
alignas ( alignment-expression ... [opt] ).
|
||||
|
||||
Extensions for contracts:
|
||||
|
||||
contract-attribute-specifier:
|
||||
[ [ assert : contract-mode [opt] : conditional-expression ] ]
|
||||
[ [ pre : contract-mode [opt] : conditional-expression ] ]
|
||||
[ [ post : contract-mode [opt] identifier [opt] :
|
||||
conditional-expression ] ] */
|
||||
|
||||
static tree
|
||||
cp_parser_std_attribute_spec (cp_parser *parser)
|
||||
@@ -29340,10 +29814,27 @@ cp_parser_std_attribute_spec (cp_parser *parser)
|
||||
&& cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_OPEN_SQUARE)
|
||||
{
|
||||
tree attr_ns = NULL_TREE;
|
||||
tree attr_name = NULL_TREE;
|
||||
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
cp_lexer_consume_token (parser->lexer);
|
||||
|
||||
token = cp_lexer_peek_token (parser->lexer);
|
||||
if (token->type == CPP_NAME)
|
||||
{
|
||||
attr_name = token->u.value;
|
||||
attr_name = canonicalize_attr_name (attr_name);
|
||||
}
|
||||
|
||||
/* Handle contract-attribute-specs specially. */
|
||||
if (attr_name && contract_attribute_p (attr_name))
|
||||
{
|
||||
tree attrs = cp_parser_contract_attribute_spec (parser, attr_name);
|
||||
if (attrs != error_mark_node)
|
||||
attributes = attrs;
|
||||
goto finish_attrs;
|
||||
}
|
||||
|
||||
if (cp_lexer_next_token_is_keyword (parser->lexer, RID_USING))
|
||||
{
|
||||
token = cp_lexer_peek_nth_token (parser->lexer, 2);
|
||||
@@ -29372,6 +29863,7 @@ cp_parser_std_attribute_spec (cp_parser *parser)
|
||||
|
||||
attributes = cp_parser_std_attribute_list (parser, attr_ns);
|
||||
|
||||
finish_attrs:
|
||||
if (!cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE)
|
||||
|| !cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE))
|
||||
cp_parser_skip_to_end_of_statement (parser);
|
||||
@@ -32395,6 +32887,14 @@ cp_parser_save_default_args (cp_parser* parser, tree decl)
|
||||
tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
|
||||
if (UNPARSED_NOEXCEPT_SPEC_P (spec))
|
||||
vec_safe_push (unparsed_noexcepts, decl);
|
||||
|
||||
/* Contracts are deferred. */
|
||||
for (tree attr = DECL_ATTRIBUTES (decl); attr; attr = TREE_CHAIN (attr))
|
||||
if (cxx_contract_attribute_p (attr))
|
||||
{
|
||||
vec_safe_push (unparsed_contracts, decl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* DEFAULT_ARG contains the saved tokens for the initializer of DECL,
|
||||
|
||||
@@ -178,6 +178,9 @@ struct GTY(()) cp_unparsed_functions_entry {
|
||||
|
||||
/* Functions with noexcept-specifiers that require post-processing. */
|
||||
vec<tree, va_gc> *noexcepts;
|
||||
|
||||
/* Functions with contract attributes that require post-processing. */
|
||||
vec<tree, va_gc> *contracts;
|
||||
};
|
||||
|
||||
|
||||
|
||||
164
gcc/cp/pt.cc
164
gcc/cp/pt.cc
@@ -217,7 +217,6 @@ static tree get_underlying_template (tree);
|
||||
static tree tsubst_attributes (tree, tree, tsubst_flags_t, tree);
|
||||
static tree canonicalize_expr_argument (tree, tsubst_flags_t);
|
||||
static tree make_argument_pack (tree);
|
||||
static void register_parameter_specializations (tree, tree);
|
||||
static tree enclosing_instantiation_of (tree tctx);
|
||||
static void instantiate_body (tree pattern, tree args, tree d, bool nested);
|
||||
static tree maybe_dependent_member_ref (tree, tree, tsubst_flags_t, tree);
|
||||
@@ -1968,6 +1967,16 @@ register_local_specialization (tree spec, tree tmpl)
|
||||
local_specializations->put (tmpl, spec);
|
||||
}
|
||||
|
||||
/* Registers T as a specialization of itself. This is used to preserve
|
||||
the references to already-parsed parameters when instantiating
|
||||
postconditions. */
|
||||
|
||||
void
|
||||
register_local_identity (tree t)
|
||||
{
|
||||
local_specializations->put (t, t);
|
||||
}
|
||||
|
||||
/* TYPE is a class type. Returns true if TYPE is an explicitly
|
||||
specialized class. */
|
||||
|
||||
@@ -3161,8 +3170,10 @@ check_explicit_specialization (tree declarator,
|
||||
parm = DECL_CHAIN (parm))
|
||||
DECL_CONTEXT (parm) = result;
|
||||
}
|
||||
return register_specialization (tmpl, gen_tmpl, targs,
|
||||
decl = register_specialization (tmpl, gen_tmpl, targs,
|
||||
is_friend, 0);
|
||||
remove_contract_attributes (result);
|
||||
return decl;
|
||||
}
|
||||
|
||||
/* Set up the DECL_TEMPLATE_INFO for DECL. */
|
||||
@@ -3262,6 +3273,10 @@ check_explicit_specialization (tree declarator,
|
||||
is_friend, 0);
|
||||
}
|
||||
|
||||
/* If this is a specialization, splice any contracts that may have
|
||||
been inherited from the template, removing them. */
|
||||
if (decl != error_mark_node && DECL_TEMPLATE_SPECIALIZATION (decl))
|
||||
remove_contract_attributes (decl);
|
||||
|
||||
/* A 'structor should already have clones. */
|
||||
gcc_assert (decl == error_mark_node
|
||||
@@ -11576,6 +11591,113 @@ can_complete_type_without_circularity (tree type)
|
||||
static tree tsubst_omp_clauses (tree, enum c_omp_region_type, tree,
|
||||
tsubst_flags_t, tree);
|
||||
|
||||
/* Instantiate the contract statement. */
|
||||
|
||||
static tree
|
||||
tsubst_contract (tree decl, tree t, tree args, tsubst_flags_t complain,
|
||||
tree in_decl)
|
||||
{
|
||||
tree type = decl ? TREE_TYPE (TREE_TYPE (decl)) : NULL_TREE;
|
||||
bool auto_p = type_uses_auto (type);
|
||||
|
||||
tree r = copy_node (t);
|
||||
|
||||
/* Rebuild the result variable. */
|
||||
if (POSTCONDITION_P (t) && POSTCONDITION_IDENTIFIER (t))
|
||||
{
|
||||
tree oldvar = POSTCONDITION_IDENTIFIER (t);
|
||||
|
||||
tree newvar = copy_node (oldvar);
|
||||
TREE_TYPE (newvar) = type;
|
||||
DECL_CONTEXT (newvar) = decl;
|
||||
POSTCONDITION_IDENTIFIER (r) = newvar;
|
||||
|
||||
/* Make sure the postcondition is valid. */
|
||||
location_t loc = DECL_SOURCE_LOCATION (oldvar);
|
||||
if (!auto_p)
|
||||
if (!check_postcondition_result (decl, type, loc))
|
||||
return invalidate_contract (r);
|
||||
|
||||
/* Make the variable available for lookup. */
|
||||
register_local_specialization (newvar, oldvar);
|
||||
}
|
||||
|
||||
/* Instantiate the condition. If the return type is undeduced, process
|
||||
the expression as if inside a template to avoid spurious type errors. */
|
||||
if (auto_p)
|
||||
++processing_template_decl;
|
||||
++processing_contract_condition;
|
||||
CONTRACT_CONDITION (r)
|
||||
= tsubst_expr (CONTRACT_CONDITION (t), args, complain, in_decl, false);
|
||||
--processing_contract_condition;
|
||||
if (auto_p)
|
||||
--processing_template_decl;
|
||||
|
||||
/* And the comment. */
|
||||
CONTRACT_COMMENT (r)
|
||||
= tsubst_expr (CONTRACT_COMMENT (r), args, complain, in_decl, false);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Update T by instantiating its contract attribute. */
|
||||
|
||||
static void
|
||||
tsubst_contract_attribute (tree decl, tree t, tree args,
|
||||
tsubst_flags_t complain, tree in_decl)
|
||||
{
|
||||
/* For non-specializations, adjust the current declaration to the most general
|
||||
version of in_decl. Because we defer the instantiation of contracts as long
|
||||
as possible, they are still written in terms of the parameters (and return
|
||||
type) of the most general template. */
|
||||
tree tmpl = DECL_TI_TEMPLATE (in_decl);
|
||||
if (!DECL_TEMPLATE_SPECIALIZATION (tmpl))
|
||||
in_decl = DECL_TEMPLATE_RESULT (most_general_template (in_decl));
|
||||
local_specialization_stack specs (lss_copy);
|
||||
register_parameter_specializations (in_decl, decl);
|
||||
|
||||
/* Get the contract to be instantiated. */
|
||||
tree contract = CONTRACT_STATEMENT (t);
|
||||
|
||||
/* Use the complete set of template arguments for instantiation. The
|
||||
contract may not have been instantiated and still refer to outer levels
|
||||
of template parameters. */
|
||||
args = DECL_TI_ARGS (decl);
|
||||
|
||||
/* For member functions, make this available for semantic analysis. */
|
||||
tree save_ccp = current_class_ptr;
|
||||
tree save_ccr = current_class_ref;
|
||||
if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
|
||||
{
|
||||
tree arg_types = TYPE_ARG_TYPES (TREE_TYPE (decl));
|
||||
tree this_type = TREE_TYPE (TREE_VALUE (arg_types));
|
||||
inject_this_parameter (this_type, cp_type_quals (this_type));
|
||||
}
|
||||
|
||||
contract = tsubst_contract (decl, contract, args, complain, in_decl);
|
||||
|
||||
current_class_ptr = save_ccp;
|
||||
current_class_ref = save_ccr;
|
||||
|
||||
/* Rebuild the attribute. */
|
||||
TREE_VALUE (t) = build_tree_list (NULL_TREE, contract);
|
||||
}
|
||||
|
||||
/* Rebuild the attribute list for DECL, substituting into contracts
|
||||
as needed. */
|
||||
|
||||
void
|
||||
tsubst_contract_attributes (tree decl, tree args, tsubst_flags_t complain, tree in_decl)
|
||||
{
|
||||
tree list = copy_list (DECL_ATTRIBUTES (decl));
|
||||
for (tree attr = list; attr; attr = CONTRACT_CHAIN (attr))
|
||||
{
|
||||
if (cxx_contract_attribute_p (attr))
|
||||
tsubst_contract_attribute (decl, attr, args, complain, in_decl);
|
||||
}
|
||||
DECL_ATTRIBUTES (decl) = list;
|
||||
}
|
||||
|
||||
/* Instantiate a single dependent attribute T (a TREE_LIST), and return either
|
||||
T or a new TREE_LIST, possibly a chain in the case of a pack expansion. */
|
||||
|
||||
@@ -11585,6 +11707,10 @@ tsubst_attribute (tree t, tree *decl_p, tree args,
|
||||
{
|
||||
gcc_assert (ATTR_IS_DEPENDENT (t));
|
||||
|
||||
/* Note that contract attributes are never substituted from this function.
|
||||
Their instantiation is triggered by regenerate_from_template_decl when
|
||||
we instantiate the body of the function. */
|
||||
|
||||
tree val = TREE_VALUE (t);
|
||||
if (val == NULL_TREE)
|
||||
/* Nothing to do. */;
|
||||
@@ -17131,7 +17257,9 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
|
||||
= do_auto_deduction (TREE_TYPE (r), init, auto_node,
|
||||
complain, adc_variable_type);
|
||||
}
|
||||
gcc_assert (cp_unevaluated_operand || TREE_STATIC (r)
|
||||
gcc_assert (cp_unevaluated_operand
|
||||
|| processing_contract_condition
|
||||
|| TREE_STATIC (r)
|
||||
|| decl_constant_var_p (r)
|
||||
|| seen_error ());
|
||||
if (!processing_template_decl
|
||||
@@ -18649,6 +18777,19 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
|
||||
finish_using_directive (USING_STMT_NAMESPACE (t), /*attribs=*/NULL_TREE);
|
||||
break;
|
||||
|
||||
case PRECONDITION_STMT:
|
||||
case POSTCONDITION_STMT:
|
||||
gcc_unreachable ();
|
||||
|
||||
case ASSERTION_STMT:
|
||||
{
|
||||
r = tsubst_contract (NULL_TREE, t, args, complain, in_decl);
|
||||
if (r != error_mark_node)
|
||||
add_stmt (r);
|
||||
RETURN (r);
|
||||
}
|
||||
break;
|
||||
|
||||
case DECL_EXPR:
|
||||
{
|
||||
tree decl, pattern_decl;
|
||||
@@ -26199,6 +26340,21 @@ regenerate_decl_from_template (tree decl, tree tmpl, tree args)
|
||||
DECL_CONTEXT (t) = decl;
|
||||
}
|
||||
|
||||
if (DECL_CONTRACTS (decl))
|
||||
{
|
||||
/* If we're regenerating a specialization, the contracts will have
|
||||
been copied from the most general template. Replace those with
|
||||
the ones from the actual specialization. */
|
||||
tree tmpl = DECL_TI_TEMPLATE (decl);
|
||||
if (DECL_TEMPLATE_SPECIALIZATION (tmpl))
|
||||
{
|
||||
remove_contract_attributes (decl);
|
||||
copy_contract_attributes (decl, code_pattern);
|
||||
}
|
||||
|
||||
tsubst_contract_attributes (decl, args, tf_warning_or_error, code_pattern);
|
||||
}
|
||||
|
||||
/* Merge additional specifiers from the CODE_PATTERN. */
|
||||
if (DECL_DECLARED_INLINE_P (code_pattern)
|
||||
&& !DECL_DECLARED_INLINE_P (decl))
|
||||
@@ -26446,7 +26602,7 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain)
|
||||
/* We're starting to process the function INST, an instantiation of PATTERN;
|
||||
add their parameters to local_specializations. */
|
||||
|
||||
static void
|
||||
void
|
||||
register_parameter_specializations (tree pattern, tree inst)
|
||||
{
|
||||
tree tmpl_parm = DECL_ARGUMENTS (pattern);
|
||||
|
||||
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "spellcheck-tree.h"
|
||||
#include "stringpool.h"
|
||||
#include "attribs.h"
|
||||
#include "tree-inline.h"
|
||||
|
||||
static int is_subobject_of_p (tree, tree);
|
||||
static tree dfs_lookup_base (tree, void *);
|
||||
@@ -2082,6 +2083,33 @@ check_final_overrider (tree overrider, tree basefn)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!DECL_HAS_CONTRACTS_P (basefn) && DECL_HAS_CONTRACTS_P (overrider))
|
||||
{
|
||||
auto_diagnostic_group d;
|
||||
error ("function with contracts %q+D overriding contractless function",
|
||||
overrider);
|
||||
inform (DECL_SOURCE_LOCATION (basefn),
|
||||
"overridden function is %qD", basefn);
|
||||
return 0;
|
||||
}
|
||||
else if (DECL_HAS_CONTRACTS_P (basefn) && !DECL_HAS_CONTRACTS_P (overrider))
|
||||
{
|
||||
/* We're inheriting basefn's contracts; create a copy of them but
|
||||
replace references to their parms to our parms. */
|
||||
inherit_base_contracts (overrider, basefn);
|
||||
}
|
||||
else if (DECL_HAS_CONTRACTS_P (basefn) && DECL_HAS_CONTRACTS_P (overrider))
|
||||
{
|
||||
/* We're in the process of completing the overrider's class, which means
|
||||
our conditions definitely are not parsed so simply chain on the
|
||||
basefn for later checking.
|
||||
|
||||
Note that OVERRIDER's contracts will have been fully parsed at the
|
||||
point the deferred match is run. */
|
||||
defer_guarded_contract_match (overrider, basefn, DECL_CONTRACTS (basefn));
|
||||
}
|
||||
|
||||
if (DECL_FINAL_P (basefn))
|
||||
{
|
||||
auto_diagnostic_group d;
|
||||
|
||||
@@ -610,7 +610,8 @@ set_cleanup_locs (tree stmts, location_t loc)
|
||||
if (TREE_CODE (stmts) == CLEANUP_STMT)
|
||||
{
|
||||
tree t = CLEANUP_EXPR (stmts);
|
||||
protected_set_expr_location (t, loc);
|
||||
if (t && TREE_CODE (t) != POSTCONDITION_STMT)
|
||||
protected_set_expr_location (t, loc);
|
||||
/* Avoid locus differences for C++ cdtor calls depending on whether
|
||||
cdtor_returns_this: a conversion to void is added to discard the return
|
||||
value, and this conversion ends up carrying the location, and when it
|
||||
@@ -2169,7 +2170,8 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope,
|
||||
|
||||
/* DR 613/850: Can use non-static data members without an associated
|
||||
object in sizeof/decltype/alignof. */
|
||||
if (is_dummy_object (object) && cp_unevaluated_operand == 0
|
||||
if (is_dummy_object (object)
|
||||
&& !cp_unevaluated_operand
|
||||
&& (!processing_template_decl || !current_class_ref))
|
||||
{
|
||||
if (complain & tf_error)
|
||||
@@ -2177,6 +2179,14 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope,
|
||||
if (current_function_decl
|
||||
&& DECL_STATIC_FUNCTION_P (current_function_decl))
|
||||
error ("invalid use of member %qD in static member function", decl);
|
||||
else if (current_function_decl
|
||||
&& processing_contract_condition
|
||||
&& DECL_CONSTRUCTOR_P (current_function_decl))
|
||||
error ("invalid use of member %qD in constructor %<pre%> contract", decl);
|
||||
else if (current_function_decl
|
||||
&& processing_contract_condition
|
||||
&& DECL_DESTRUCTOR_P (current_function_decl))
|
||||
error ("invalid use of member %qD in destructor %<post%> contract", decl);
|
||||
else
|
||||
error ("invalid use of non-static data member %qD", decl);
|
||||
inform (DECL_SOURCE_LOCATION (decl), "declared here");
|
||||
@@ -3010,6 +3020,10 @@ finish_this_expr (void)
|
||||
tree fn = current_nonlambda_function ();
|
||||
if (fn && DECL_STATIC_FUNCTION_P (fn))
|
||||
error ("%<this%> is unavailable for static member functions");
|
||||
else if (fn && processing_contract_condition && DECL_CONSTRUCTOR_P (fn))
|
||||
error ("invalid use of %<this%> before it is valid");
|
||||
else if (fn && processing_contract_condition && DECL_DESTRUCTOR_P (fn))
|
||||
error ("invalid use of %<this%> after it is valid");
|
||||
else if (fn)
|
||||
error ("invalid use of %<this%> in non-member function");
|
||||
else
|
||||
@@ -3983,6 +3997,9 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use)
|
||||
}
|
||||
return error_mark_node;
|
||||
}
|
||||
else if (processing_contract_condition && (TREE_CODE (decl) == PARM_DECL))
|
||||
/* Use of a parameter in a contract condition is fine. */
|
||||
return decl;
|
||||
else
|
||||
{
|
||||
if (complain & tf_error)
|
||||
@@ -4115,7 +4132,8 @@ finish_id_expression_1 (tree id_expression,
|
||||
body, except inside an unevaluated context (i.e. decltype). */
|
||||
if (TREE_CODE (decl) == PARM_DECL
|
||||
&& DECL_CONTEXT (decl) == NULL_TREE
|
||||
&& !cp_unevaluated_operand)
|
||||
&& !cp_unevaluated_operand
|
||||
&& !processing_contract_condition)
|
||||
{
|
||||
*error_msg = G_("use of parameter outside function body");
|
||||
return error_mark_node;
|
||||
@@ -12286,6 +12304,10 @@ apply_deduced_return_type (tree fco, tree return_type)
|
||||
|
||||
TREE_TYPE (fco) = change_return_type (return_type, TREE_TYPE (fco));
|
||||
|
||||
maybe_update_postconditions (fco);
|
||||
|
||||
/* Apply the type to the result object. */
|
||||
|
||||
result = DECL_RESULT (fco);
|
||||
if (result == NULL_TREE)
|
||||
return;
|
||||
|
||||
@@ -46,6 +46,7 @@ static tree verify_stmt_tree_r (tree *, int *, void *);
|
||||
|
||||
static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
|
||||
static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
|
||||
static tree handle_contract_attribute (tree *, tree, tree, int, bool *);
|
||||
|
||||
/* If REF is an lvalue, returns the kind of lvalue that REF is.
|
||||
Otherwise, returns clk_none. */
|
||||
@@ -3885,6 +3886,50 @@ called_fns_equal (tree t1, tree t2)
|
||||
return cp_tree_equal (t1, t2);
|
||||
}
|
||||
|
||||
bool comparing_override_contracts;
|
||||
|
||||
/* In a component reference, return the innermost object of
|
||||
the postfix-expression. */
|
||||
|
||||
static tree
|
||||
get_innermost_component (tree t)
|
||||
{
|
||||
gcc_assert (TREE_CODE (t) == COMPONENT_REF);
|
||||
while (TREE_CODE (t) == COMPONENT_REF)
|
||||
t = TREE_OPERAND (t, 0);
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Returns true if T is a possibly converted 'this' or '*this' expression. */
|
||||
|
||||
static bool
|
||||
is_this_expression (tree t)
|
||||
{
|
||||
t = get_innermost_component (t);
|
||||
/* See through deferences and no-op conversions. */
|
||||
if (TREE_CODE (t) == INDIRECT_REF)
|
||||
t = TREE_OPERAND (t, 0);
|
||||
if (TREE_CODE (t) == NOP_EXPR)
|
||||
t = TREE_OPERAND (t, 0);
|
||||
return is_this_parameter (t);
|
||||
}
|
||||
|
||||
static bool
|
||||
comparing_this_references (tree t1, tree t2)
|
||||
{
|
||||
return is_this_expression (t1) && is_this_expression (t2);
|
||||
}
|
||||
|
||||
static bool
|
||||
equivalent_member_references (tree t1, tree t2)
|
||||
{
|
||||
if (!comparing_this_references (t1, t2))
|
||||
return false;
|
||||
t1 = TREE_OPERAND (t1, 1);
|
||||
t2 = TREE_OPERAND (t2, 1);
|
||||
return t1 == t2;
|
||||
}
|
||||
|
||||
/* Return truthvalue of whether T1 is the same tree structure as T2.
|
||||
Return 1 if they are the same. Return 0 if they are different. */
|
||||
|
||||
@@ -4219,6 +4264,13 @@ cp_tree_equal (tree t1, tree t2)
|
||||
return false;
|
||||
return true;
|
||||
|
||||
case COMPONENT_REF:
|
||||
/* If we're comparing contract conditions of overrides, member references
|
||||
compare equal if they designate the same member. */
|
||||
if (comparing_override_contracts)
|
||||
return equivalent_member_references (t1, t2);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -5034,6 +5086,8 @@ const struct attribute_spec std_attribute_table[] =
|
||||
handle_likeliness_attribute, attr_cold_hot_exclusions },
|
||||
{ "noreturn", 0, 0, true, false, false, false,
|
||||
handle_noreturn_attribute, attr_noreturn_exclusions },
|
||||
{ "pre", 0, -1, false, false, false, false, handle_contract_attribute, NULL },
|
||||
{ "post", 0, -1, false, false, false, false, handle_contract_attribute, NULL },
|
||||
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
|
||||
};
|
||||
|
||||
@@ -5282,6 +5336,17 @@ handle_abi_tag_attribute (tree* node, tree name, tree args,
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Perform checking for contract attributes. */
|
||||
|
||||
tree
|
||||
handle_contract_attribute (tree *ARG_UNUSED (node), tree ARG_UNUSED (name),
|
||||
tree ARG_UNUSED (args), int ARG_UNUSED (flags),
|
||||
bool *ARG_UNUSED (no_add_attrs))
|
||||
{
|
||||
/* TODO: Is there any checking we could do here? */
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Return a new PTRMEM_CST of the indicated TYPE. The MEMBER is the
|
||||
thing pointed to by the constant. */
|
||||
|
||||
|
||||
@@ -11260,11 +11260,22 @@ check_return_expr (tree retval, bool *no_warning)
|
||||
|
||||
/* Actually copy the value returned into the appropriate location. */
|
||||
if (retval && retval != result)
|
||||
retval = cp_build_init_expr (result, retval);
|
||||
{
|
||||
/* If there's a postcondition for a scalar return value, wrap
|
||||
retval in a call to the postcondition function. */
|
||||
if (tree post = apply_postcondition_to_return (retval))
|
||||
retval = post;
|
||||
retval = cp_build_init_expr (result, retval);
|
||||
}
|
||||
|
||||
if (tree set = maybe_set_retval_sentinel ())
|
||||
retval = build2 (COMPOUND_EXPR, void_type_node, retval, set);
|
||||
|
||||
/* If there's a postcondition for an aggregate return value, call the
|
||||
postcondition function after the return object is initialized. */
|
||||
if (tree post = apply_postcondition_to_return (result))
|
||||
retval = build2 (COMPOUND_EXPR, void_type_node, retval, post);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
@@ -3132,6 +3132,85 @@ of a loop too many expressions need to be evaluated, the resulting constexpr
|
||||
evaluation might take too long.
|
||||
The default is 33554432 (1<<25).
|
||||
|
||||
@item -fcontracts
|
||||
@opindex fcontracts
|
||||
Enable experimental support for the C++ Contracts feature, as briefly
|
||||
added to and then removed from the C++20 working paper (N4820). The
|
||||
implementation also includes proposed enhancements from papers P1290,
|
||||
P1332, and P1429. This functionality is intended mostly for those
|
||||
interested in experimentation towards refining the feature to get it
|
||||
into shape for a future C++ standard.
|
||||
|
||||
On violation of a checked contract, the violation handler is called.
|
||||
Users can replace the violation handler by defining
|
||||
@smallexample
|
||||
void handle_contract_violation (const std::experimental::contract_violation&);
|
||||
@end smallexample
|
||||
|
||||
There are different sets of additional flags that can be used together
|
||||
to specify which contracts will be checked and how, for N4820
|
||||
contracts, P1332 contracts, or P1429 contracts; these sets cannot be
|
||||
used together.
|
||||
|
||||
@table @gcctabopt
|
||||
@item -fcontract-mode=[on|off]
|
||||
@opindex fcontract-mode
|
||||
Control whether any contracts have any semantics at all. Defaults to on.
|
||||
|
||||
@item -fcontract-assumption-mode=[on|off]
|
||||
@opindex fcontract-assumption-mode
|
||||
[N4820] Control whether contracts with level @samp{axiom}
|
||||
should have the assume semantic. Defaults to on.
|
||||
|
||||
@item -fcontract-build-level=[off|default|audit]
|
||||
@opindex fcontract-build-level
|
||||
[N4820] Specify which level of contracts to generate checks
|
||||
for. Defaults to @samp{default}.
|
||||
|
||||
@item -fcontract-continuation-mode=[on|off]
|
||||
@opindex fcontract-continuation-mode
|
||||
[N4820] Control whether to allow the program to continue executing
|
||||
after a contract violation. That is, do checked contracts have the
|
||||
@samp{maybe} semantic described below rather than the @samp{never}
|
||||
semantic. Defaults to off.
|
||||
|
||||
@item -fcontract-role=<name>:<default>,<audit>,<axiom>
|
||||
@opindex fcontract-role
|
||||
[P1332] Specify the concrete semantics for each contract level
|
||||
of a particular contract role.
|
||||
|
||||
@item -fcontract-semantic=[default|audit|axiom]:<semantic>
|
||||
[P1429] Specify the concrete semantic for a particular
|
||||
contract level.
|
||||
|
||||
@item -fcontract-strict-declarations=[on|off]
|
||||
@opindex fcontract-strict-declarations
|
||||
Control whether to reject adding contracts to a function after its
|
||||
first declaration. Defaults to off.
|
||||
@end table
|
||||
|
||||
The possible concrete semantics for that can be specified with
|
||||
@samp{-fcontract-role} or @samp{-fcontract-semantic} are:
|
||||
|
||||
@table @code
|
||||
@item ignore
|
||||
This contract has no effect.
|
||||
|
||||
@item assume
|
||||
This contract is treated like C++23 @code{[[assume]]}.
|
||||
|
||||
@item check_never_continue
|
||||
@itemx never
|
||||
@itemx abort
|
||||
This contract is checked. If it fails, the violation handler is
|
||||
called. If the handler returns, @code{std::terminate} is called.
|
||||
|
||||
@item check_maybe_continue
|
||||
@itemx maybe
|
||||
This contract is checked. If it fails, the violation handler is
|
||||
called. If the handler returns, execution continues normally.
|
||||
@end table
|
||||
|
||||
@item -fcoroutines
|
||||
@opindex fcoroutines
|
||||
Enable support for the C++ coroutines extension (experimental).
|
||||
|
||||
92
gcc/input.cc
92
gcc/input.cc
@@ -949,6 +949,98 @@ location_get_source_line (const char *file_path, int line)
|
||||
return char_span (buffer, len);
|
||||
}
|
||||
|
||||
/* Return a NUL-terminated copy of the source text between two locations, or
|
||||
NULL if the arguments are invalid. The caller is responsible for freeing
|
||||
the return value. */
|
||||
|
||||
char *
|
||||
get_source_text_between (location_t start, location_t end)
|
||||
{
|
||||
expanded_location expstart =
|
||||
expand_location_to_spelling_point (start, LOCATION_ASPECT_START);
|
||||
expanded_location expend =
|
||||
expand_location_to_spelling_point (end, LOCATION_ASPECT_FINISH);
|
||||
|
||||
/* If the locations are in different files or the end comes before the
|
||||
start, give up and return nothing. */
|
||||
if (!expstart.file || !expend.file)
|
||||
return NULL;
|
||||
if (strcmp (expstart.file, expend.file) != 0)
|
||||
return NULL;
|
||||
if (expstart.line > expend.line)
|
||||
return NULL;
|
||||
if (expstart.line == expend.line
|
||||
&& expstart.column > expend.column)
|
||||
return NULL;
|
||||
/* These aren't real column numbers, give up. */
|
||||
if (expstart.column == 0 || expend.column == 0)
|
||||
return NULL;
|
||||
|
||||
/* For a single line we need to trim both edges. */
|
||||
if (expstart.line == expend.line)
|
||||
{
|
||||
char_span line = location_get_source_line (expstart.file, expstart.line);
|
||||
if (line.length () < 1)
|
||||
return NULL;
|
||||
int s = expstart.column - 1;
|
||||
int len = expend.column - s;
|
||||
if (line.length () < (size_t)expend.column)
|
||||
return NULL;
|
||||
return line.subspan (s, len).xstrdup ();
|
||||
}
|
||||
|
||||
struct obstack buf_obstack;
|
||||
obstack_init (&buf_obstack);
|
||||
|
||||
/* Loop through all lines in the range and append each to buf; may trim
|
||||
parts of the start and end lines off depending on column values. */
|
||||
for (int lnum = expstart.line; lnum <= expend.line; ++lnum)
|
||||
{
|
||||
char_span line = location_get_source_line (expstart.file, lnum);
|
||||
if (line.length () < 1 && (lnum != expstart.line && lnum != expend.line))
|
||||
continue;
|
||||
|
||||
/* For the first line in the range, only start at expstart.column */
|
||||
if (lnum == expstart.line)
|
||||
{
|
||||
unsigned off = expstart.column - 1;
|
||||
if (line.length () < off)
|
||||
return NULL;
|
||||
line = line.subspan (off, line.length() - off);
|
||||
}
|
||||
/* For the last line, don't go past expend.column */
|
||||
else if (lnum == expend.line)
|
||||
{
|
||||
if (line.length () < (size_t)expend.column)
|
||||
return NULL;
|
||||
line = line.subspan (0, expend.column);
|
||||
}
|
||||
|
||||
/* Combine spaces at the beginning of later lines. */
|
||||
if (lnum > expstart.line)
|
||||
{
|
||||
unsigned off;
|
||||
for (off = 0; off < line.length(); ++off)
|
||||
if (line[off] != ' ' && line[off] != '\t')
|
||||
break;
|
||||
if (off > 0)
|
||||
{
|
||||
obstack_1grow (&buf_obstack, ' ');
|
||||
line = line.subspan (off, line.length() - off);
|
||||
}
|
||||
}
|
||||
|
||||
/* This does not include any trailing newlines. */
|
||||
obstack_grow (&buf_obstack, line.get_buffer (), line.length ());
|
||||
}
|
||||
|
||||
/* NUL-terminate and finish the buf obstack. */
|
||||
obstack_1grow (&buf_obstack, 0);
|
||||
const char *buf = (const char *) obstack_finish (&buf_obstack);
|
||||
|
||||
return xstrdup (buf);
|
||||
}
|
||||
|
||||
/* Determine if FILE_PATH missing a trailing newline on its final line.
|
||||
Only valid to call once all of the file has been loaded, by
|
||||
requesting a line number beyond the end of the file. */
|
||||
|
||||
@@ -111,6 +111,7 @@ class char_span
|
||||
};
|
||||
|
||||
extern char_span location_get_source_line (const char *file_path, int line);
|
||||
extern char *get_source_text_between (location_t, location_t);
|
||||
|
||||
extern bool location_missing_trailing_newline (const char *file_path);
|
||||
|
||||
|
||||
13
gcc/testsuite/g++.dg/contracts/backtrace_handler/Makefile
Normal file
13
gcc/testsuite/g++.dg/contracts/backtrace_handler/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
CXXFLAGS=--std=c++17 -g
|
||||
|
||||
default: assert_fail libhandle_contract_violation.so
|
||||
|
||||
run: default
|
||||
LD_PRELOAD=./libhandle_contract_violation.so ./assert_fail
|
||||
|
||||
./libhandle_contract_violation.so: ./handle_contract_violation.cpp
|
||||
${CXX} ${CXXFLAGS} -shared -fPIC -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -fr ./libhandle_contract_violation.so ./assert_fail
|
||||
|
||||
12
gcc/testsuite/g++.dg/contracts/backtrace_handler/README
Normal file
12
gcc/testsuite/g++.dg/contracts/backtrace_handler/README
Normal file
@@ -0,0 +1,12 @@
|
||||
build and install gcc to $prefix, then to see the raw backtrace info run:
|
||||
LD_LIBRARY_PATH=$prefix/lib64 CXX=$prefix/bin/g++ make run
|
||||
|
||||
for a filtered view using addr2line, see ./prettytrace.sh:
|
||||
LD_LIBRARY_PATH=$prefix/lib64 CXX=$prefix/bin/g++ make run |& ./prettytrace.sh
|
||||
|
||||
prettytrace.sh relies on addr2line and c++filt to lookup and demangle names,
|
||||
and misc coreutils
|
||||
|
||||
example_out.txt has an example of the raw output while example_pretty.txt
|
||||
shows the corresponding prettified output
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
void fun1() {
|
||||
int x = 0;
|
||||
[[ assert: x < 0 ]];
|
||||
}
|
||||
namespace tns {
|
||||
void fun2() {
|
||||
fun1();
|
||||
}
|
||||
}
|
||||
template<typename T>
|
||||
void fun3(T a) {
|
||||
tns::fun2();
|
||||
}
|
||||
void fun4() {
|
||||
fun3(5);
|
||||
}
|
||||
int main(int, char**) {
|
||||
void (*fp)() = nullptr;
|
||||
fp = fun4;
|
||||
fp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
LD_PRELOAD=./libhandle_contract_violation.so ./assert_fail
|
||||
contract violation: assert_fail.cpp:3: fun1::x < 0 is false [with contract level=default]
|
||||
violation occurs here:
|
||||
./assert_fail[0x4011ad]
|
||||
./assert_fail[0x4011e0]
|
||||
./assert_fail[0x401230]
|
||||
./assert_fail[0x4011f1]
|
||||
./assert_fail[0x401219]
|
||||
/usr/lib/libc.so.6(__libc_start_main+0xf3)[0x7f26e4fa9223]
|
||||
./assert_fail[0x4010be]
|
||||
[0x0]
|
||||
end of violation
|
||||
@@ -0,0 +1,8 @@
|
||||
LD_PRELOAD=./libhandle_contract_violation.so ./assert_fail
|
||||
contract violation: assert_fail.cpp:3: fun1::x < 0 is false [with contract level=default]
|
||||
violation occurs here:
|
||||
assert_fail.cpp:fun1():4
|
||||
assert_fail.cpp:tns::fun2():8
|
||||
assert_fail.cpp:void fun3<int>(int):13
|
||||
assert_fail.cpp:fun4():16
|
||||
assert_fail.cpp:main:21
|
||||
@@ -0,0 +1,26 @@
|
||||
#include <iostream>
|
||||
#include <contract>
|
||||
#include <execinfo.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static constexpr int MAX_BACKTRACE_DEPTH = 128;
|
||||
|
||||
void handle_contract_violation(const std::contract_violation &violation) {
|
||||
size_t _backtraceSize{0};
|
||||
void *_backtrace[MAX_BACKTRACE_DEPTH]{};
|
||||
|
||||
_backtraceSize = backtrace(_backtrace, MAX_BACKTRACE_DEPTH);
|
||||
if(_backtraceSize == MAX_BACKTRACE_DEPTH)
|
||||
std::cerr << "warning: backtrace may have been truncated" << std::endl;
|
||||
|
||||
std::cerr << "contract violation: " << violation.file_name()
|
||||
<< ":" << violation.line_number()
|
||||
<< ": " << violation.comment() << " is false"
|
||||
<< " [with contract level=" << violation.assertion_level() << "]" << std::endl
|
||||
<< "violation occurs here:" << std::endl;
|
||||
// skip the stack frame of handle_contract_violation and
|
||||
// on_contract_violation to get to wherever the assert was
|
||||
backtrace_symbols_fd(_backtrace + 2, _backtraceSize - 1, STDERR_FILENO);
|
||||
std::cerr << "end of violation" << std::endl;
|
||||
}
|
||||
|
||||
30
gcc/testsuite/g++.dg/contracts/backtrace_handler/prettytrace.sh
Executable file
30
gcc/testsuite/g++.dg/contracts/backtrace_handler/prettytrace.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
inViolation="false"
|
||||
sed 's/^/:/' /dev/stdin | while read -r line; do
|
||||
line="$(echo "$line" | sed 's/^://')"
|
||||
|
||||
if [[ "${inViolation}" == "false" ]]; then
|
||||
echo "$line"
|
||||
if [[ -n "$(echo "$line" | grep 'violation occurs here:')" ]]; then
|
||||
inViolation="true"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ -n "$(echo "$line" | grep 'end of violation')" ]]; then
|
||||
inViolation="false"
|
||||
continue
|
||||
fi
|
||||
|
||||
addr="$(echo "$line" | sed -r 's/.*\[0x([a-f0-9]+)\]$/\1/')"
|
||||
bin="$(echo "$line" | sed -r 's/^([^([]*).*/\1/')"
|
||||
[[ -n "${bin}" ]] || continue
|
||||
t="$(addr2line -e "$bin" "$addr" -f)"
|
||||
file="$(echo "$t" | tail -1 | tr ':' '\n' | head -1)"
|
||||
file="$(echo "$file" | sed -r "s:^$(pwd)/?::")"
|
||||
line="$(echo "$t" | tail -1 | tr ':' '\n' | tail -1 | cut -d' ' -f1)"
|
||||
func="$(echo "$t" | head -1 | c++filt)"
|
||||
[[ $file != "??" ]] && echo " $file:$func:$line"
|
||||
done
|
||||
|
||||
128
gcc/testsuite/g++.dg/contracts/contracts-access1.C
Normal file
128
gcc/testsuite/g++.dg/contracts/contracts-access1.C
Normal file
@@ -0,0 +1,128 @@
|
||||
// ensure that that preconditions can access public, protected, and private
|
||||
// members of the current and base classes
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
|
||||
struct Base
|
||||
{
|
||||
int pub{-1};
|
||||
|
||||
virtual int b()
|
||||
[[ pre: pub > 0 ]]
|
||||
[[ pre: pro > 0 ]]
|
||||
[[ pre: pri > 0 ]]
|
||||
{
|
||||
return pub * pro * pri;
|
||||
}
|
||||
|
||||
protected:
|
||||
int pro{-1};
|
||||
int pri{-1};
|
||||
};
|
||||
|
||||
struct Child : Base
|
||||
{
|
||||
int fun()
|
||||
[[ pre: pub > 0 ]]
|
||||
[[ pre: pro > 0 ]]
|
||||
[[ pre: pri > 0 ]]
|
||||
{
|
||||
return pub * pro;
|
||||
}
|
||||
};
|
||||
|
||||
struct VChild : Base
|
||||
{
|
||||
int b()
|
||||
[[ pre: pub > 0 ]]
|
||||
[[ pre: pro > 0 ]]
|
||||
[[ pre: pri > 0 ]]
|
||||
{
|
||||
return pub * pro;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename B>
|
||||
struct TChild : B
|
||||
{
|
||||
int fun()
|
||||
[[ pre: B::pub > 0 ]]
|
||||
[[ pre: B::pro > 0 ]]
|
||||
[[ pre: B::pri > 0 ]]
|
||||
{
|
||||
return B::pub * B::pro;
|
||||
}
|
||||
};
|
||||
|
||||
struct PubBase
|
||||
{
|
||||
int pub{-1};
|
||||
int pro{-1};
|
||||
int pri{-1};
|
||||
};
|
||||
|
||||
struct PubChild : PubBase
|
||||
{
|
||||
int fun()
|
||||
[[ pre: pub > 0 ]]
|
||||
[[ pre: pro > 0 ]]
|
||||
[[ pre: pri > 0 ]]
|
||||
{
|
||||
return pub * pro;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename B>
|
||||
struct TPubChild : B
|
||||
{
|
||||
int fun()
|
||||
[[ pre: B::pub > 0 ]]
|
||||
[[ pre: B::pro > 0 ]]
|
||||
[[ pre: B::pri > 0 ]]
|
||||
{
|
||||
return B::pub * B::pro;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
Base base{};
|
||||
base.b();
|
||||
|
||||
Child child{};
|
||||
child.fun();
|
||||
|
||||
VChild vchild{};
|
||||
vchild.b();
|
||||
|
||||
TChild<Base> tchild{};
|
||||
tchild.fun();
|
||||
|
||||
PubChild pubchild{};
|
||||
pubchild.fun();
|
||||
|
||||
TPubChild<PubBase> tpubchild;
|
||||
tpubchild.fun();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 11 Base::b .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 12 Base::b .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 13 Base::b .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 26 Child::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 27 Child::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 28 Child::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 37 VChild::b .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 38 VChild::b .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 39 VChild::b .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 49 TChild<Base>::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 50 TChild<Base>::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 51 TChild<Base>::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 67 PubChild::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 68 PubChild::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 69 PubChild::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 79 TPubChild<PubBase>::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 80 TPubChild<PubBase>::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 81 TPubChild<PubBase>::fun .*(\n|\r\n|\r)*" }
|
||||
|
||||
30
gcc/testsuite/g++.dg/contracts/contracts-assume1.C
Normal file
30
gcc/testsuite/g++.dg/contracts/contracts-assume1.C
Normal file
@@ -0,0 +1,30 @@
|
||||
// test that assumed contracts do instatiate templates
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
template<typename T>
|
||||
int f(T t)
|
||||
{
|
||||
return -t;
|
||||
}
|
||||
|
||||
int dummy()
|
||||
{
|
||||
[[ assert assume: f(1.0) > 0 ]];
|
||||
return -1;
|
||||
}
|
||||
|
||||
template<>
|
||||
int f(double t) // { dg-error "specialization of.*after instantiation" }
|
||||
{
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
dummy();
|
||||
f(1);
|
||||
f(1.0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
34
gcc/testsuite/g++.dg/contracts/contracts-assume2.C
Normal file
34
gcc/testsuite/g++.dg/contracts/contracts-assume2.C
Normal file
@@ -0,0 +1,34 @@
|
||||
// ensure that assert contracts can be turned into compile time assumptions
|
||||
// and that they can be used for optimization.
|
||||
//
|
||||
// Even though x == -1, the assert contract tells the compiler that it is
|
||||
// safe to assume the x <= 0 branch is never taken fun can be transformed into
|
||||
// just
|
||||
// printf("%d: test x>0\n", x);
|
||||
// return 0;
|
||||
// we ensure this by matching on the output and expecting a 0 return code from
|
||||
// main -- unlike contracts-ignore2 which expects a failing return code
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-role=default:never,assume,ignore -O1" }
|
||||
#include <cstdio>
|
||||
|
||||
int fun(int x) {
|
||||
[[assert audit: x > 0]];
|
||||
if(x <= 0)
|
||||
{
|
||||
printf("%d: test x<=0 opt out\n", x);
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%d: test x>0\n", x);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
volatile int x = -1;
|
||||
return fun(x);
|
||||
}
|
||||
|
||||
// { dg-output "-1: test x>0(\n|\r\n|\r)*" }
|
||||
19
gcc/testsuite/g++.dg/contracts/contracts-assume3.C
Normal file
19
gcc/testsuite/g++.dg/contracts/contracts-assume3.C
Normal file
@@ -0,0 +1,19 @@
|
||||
// test that assumed contracts that reference undefined entities do not cause
|
||||
// a link failure
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
int f(int t);
|
||||
|
||||
int dummy()
|
||||
{
|
||||
[[ assert assume: f(1) > 0 ]];
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
dummy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
19
gcc/testsuite/g++.dg/contracts/contracts-assume4.C
Normal file
19
gcc/testsuite/g++.dg/contracts/contracts-assume4.C
Normal file
@@ -0,0 +1,19 @@
|
||||
// test that assumed constexpr contracts that reference undefined entities do
|
||||
// not cause constexpr eval failure
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
constexpr int f(int t); // { dg-warning "used but never defined" }
|
||||
|
||||
constexpr int dummy()
|
||||
{
|
||||
[[ assert assume: f(1) > 0 ]];
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
constexpr int n = dummy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
34
gcc/testsuite/g++.dg/contracts/contracts-assume5.C
Normal file
34
gcc/testsuite/g++.dg/contracts/contracts-assume5.C
Normal file
@@ -0,0 +1,34 @@
|
||||
// test that assumed constexpr contracts that reference defined entities, or
|
||||
// undefined entities in unevaluated context, cause constexpr eval failure when
|
||||
// the predicate is constexpr false
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
constexpr int f(int t)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
constexpr int dummy()
|
||||
{
|
||||
[[ assert assume: f(1) > 0 ]];
|
||||
return -1;
|
||||
}
|
||||
|
||||
constexpr int undef(int t);
|
||||
|
||||
constexpr int dummy2()
|
||||
{
|
||||
[[ assert assume: sizeof(decltype(undef(1))) < 0 ]];
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
constexpr int n = dummy(); // { dg-message "in .constexpr. expansion" }
|
||||
// { dg-error "contract predicate" "" { target *-*-* } 14 }
|
||||
constexpr int m = dummy2(); // { dg-message "in .constexpr. expansion" }
|
||||
// { dg-error "contract predicate" "" { target *-*-* } 22 }
|
||||
return 0;
|
||||
}
|
||||
|
||||
61
gcc/testsuite/g++.dg/contracts/contracts-assume6.C
Normal file
61
gcc/testsuite/g++.dg/contracts/contracts-assume6.C
Normal file
@@ -0,0 +1,61 @@
|
||||
// ensure that non-defined entities in assume contracts do not error
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
template<typename T>
|
||||
T id2(T n);
|
||||
|
||||
int fun(int n)
|
||||
[[ pre assume: id2(n) > 0 ]]
|
||||
[[ pre: n > 0 ]]
|
||||
{
|
||||
return -n;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T tfun(T n)
|
||||
[[ pre assume: id2(n) > 0 ]]
|
||||
[[ pre: n > 0 ]]
|
||||
{
|
||||
return -n;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T id(T n); // { dg-warning "used but never defined" }
|
||||
|
||||
template<typename T>
|
||||
constexpr T cfun(T n)
|
||||
[[ pre assume: id(n) > 0 ]]
|
||||
[[ pre: id(n) > 0 ]] // { dg-error "used before its definition" }
|
||||
{
|
||||
return -n;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T id3(T n)
|
||||
{
|
||||
return n;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T cfun2(T n)
|
||||
[[ pre assume: id3(n) > 0 ]] // { dg-error "contract predicate" }
|
||||
{
|
||||
return -n;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T cfun3(T n)
|
||||
[[ pre: id3(n) > 0 ]] // { dg-error "contract predicate" }
|
||||
{
|
||||
return -n;
|
||||
}
|
||||
|
||||
int main() {
|
||||
constexpr int n = cfun(-5);
|
||||
constexpr int n2 = cfun2(-5);
|
||||
constexpr int n3 = cfun3(-5);
|
||||
fun(-5);
|
||||
tfun(-5);
|
||||
}
|
||||
|
||||
19
gcc/testsuite/g++.dg/contracts/contracts-comdat1.C
Normal file
19
gcc/testsuite/g++.dg/contracts/contracts-comdat1.C
Normal file
@@ -0,0 +1,19 @@
|
||||
// Contract condition functions should be local symbols in a comdat group with
|
||||
// the guarded function.
|
||||
|
||||
// { dg-do compile { target { c++20 && comdat_group } } }
|
||||
// { dg-additional-options -fcontracts }
|
||||
// { dg-final { scan-assembler-not "_Z1fi.pre,comdat" } }
|
||||
// { dg-final { scan-assembler-not {(weak|globl)[^\n]*_Z1fi.pre} } }
|
||||
|
||||
inline int f(int i)
|
||||
[[ pre: i > 0 ]]
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
if (f(42) != 42)
|
||||
__builtin_abort ();
|
||||
}
|
||||
36
gcc/testsuite/g++.dg/contracts/contracts-config1.C
Normal file
36
gcc/testsuite/g++.dg/contracts/contracts-config1.C
Normal file
@@ -0,0 +1,36 @@
|
||||
// Small test to ensure that the level and role information printed by various
|
||||
// contract configurations is correct.
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-role=default:maybe,maybe,ignore" }
|
||||
|
||||
int fun(int n)
|
||||
[[ post default r: r > 0 ]]
|
||||
{
|
||||
return -n;
|
||||
}
|
||||
|
||||
int main(int, char **)
|
||||
{
|
||||
[[ assert default: false ]];
|
||||
[[ assert: false ]];
|
||||
[[ assert audit: false ]];
|
||||
[[ assert default %new_role: false ]];
|
||||
[[ assert %new_role: false ]];
|
||||
[[ assert audit %new_role: false ]];
|
||||
[[ assert check_maybe_continue: false ]];
|
||||
[[ assert %default: false ]];
|
||||
[[ assert audit %default: false ]];
|
||||
fun(5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*main false default default 1.*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*main false default default 1.*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*main false audit default 1.*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*main false default new_role 1.*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*main false default new_role 1.*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*main false audit new_role 1.*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*main false 1.*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*main false default default 1.*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*main false audit default 1.*(\n|\r\n|\r)*" }
|
||||
|
||||
74
gcc/testsuite/g++.dg/contracts/contracts-constexpr1.C
Normal file
74
gcc/testsuite/g++.dg/contracts/contracts-constexpr1.C
Normal file
@@ -0,0 +1,74 @@
|
||||
// ensure that passing pre/post do not affect constexpr functions
|
||||
// ensure that failing pre/post generate an error at runtime in constexpr funcs
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++20 -fcontracts -fcontract-continuation-mode=on" }
|
||||
|
||||
constexpr int wfun(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ post r: r > 0 ]]
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
constexpr int ffun(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ post r: r > 0 ]]
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int tfun(T a)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ post r: r > 0 ]]
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int wtfun(T a)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ post r: r > 0 ]]
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int ftfun(T a)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ post r: r > 0 ]]
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
constexpr int explicitfn(int a)
|
||||
[[ pre ignore: a > 0 ]]
|
||||
[[ pre check_maybe_continue: a > 0 ]]
|
||||
[[ post ignore r: r > 0 ]]
|
||||
[[ post check_maybe_continue r: r > 0 ]]
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
int main(int, char **) {
|
||||
constexpr int a = wfun(10);
|
||||
int b = ffun(-10);
|
||||
constexpr int c = wtfun(10);
|
||||
int d = ftfun(-10);
|
||||
|
||||
int e = explicitfn(-10);
|
||||
|
||||
int z = ftfun(-10.0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 14 ffun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 15 ffun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 38 ftfun<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 39 ftfun<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 46 explicitfn .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 48 explicitfn .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 38 ftfun<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 39 ftfun<double> .*(\n|\r\n|\r)*" }
|
||||
|
||||
58
gcc/testsuite/g++.dg/contracts/contracts-constexpr2.C
Normal file
58
gcc/testsuite/g++.dg/contracts/contracts-constexpr2.C
Normal file
@@ -0,0 +1,58 @@
|
||||
// ensure that failing pre/post can fail at constexpr time
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++20 -fcontracts -fcontract-continuation-mode=on" }
|
||||
|
||||
constexpr int ffun(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ post r: r > 10 ]]
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int ftfun(T a)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ post r: r > 10 ]]
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
constexpr int explicitfn(int a)
|
||||
[[ pre ignore: a > 0 ]]
|
||||
[[ pre check_maybe_continue: a > 0 ]]
|
||||
[[ post ignore r: r > 10 ]]
|
||||
[[ post check_maybe_continue r: r > 10 ]]
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int ftfun2(T a)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ post r: r > 10 ]]
|
||||
{
|
||||
return a;
|
||||
}
|
||||
|
||||
int main(int, char **) {
|
||||
constexpr int a = ffun(-10);
|
||||
// { dg-error "contract predicate" "" { target *-*-* } 6 }
|
||||
constexpr int b = ftfun(-10);
|
||||
// { dg-error "contract predicate" "" { target *-*-* } 14 }
|
||||
constexpr int c = explicitfn(-10);
|
||||
// { dg-error "contract predicate" "" { target *-*-* } 22 }
|
||||
constexpr int d = ftfun2(-10.0);
|
||||
// { dg-error "contract predicate" "" { target *-*-* } 31 }
|
||||
|
||||
constexpr int e = ffun(5);
|
||||
// { dg-error "contract predicate" "" { target *-*-* } 7 }
|
||||
constexpr int f = ftfun(5);
|
||||
// { dg-error "contract predicate" "" { target *-*-* } 15 }
|
||||
constexpr int g = explicitfn(5);
|
||||
// { dg-error "contract predicate" "" { target *-*-* } 24 }
|
||||
constexpr int h = ftfun2(5.5);
|
||||
// { dg-error "contract predicate" "" { target *-*-* } 32 }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
10
gcc/testsuite/g++.dg/contracts/contracts-constexpr3.C
Normal file
10
gcc/testsuite/g++.dg/contracts/contracts-constexpr3.C
Normal file
@@ -0,0 +1,10 @@
|
||||
// An assumed contract shouldn't break constant evaluation.
|
||||
|
||||
// { dg-do compile { target c++20 } }
|
||||
// { dg-additional-options -fcontracts }
|
||||
|
||||
bool b;
|
||||
|
||||
constexpr int f() [[ pre assume: b ]] { return 42; }
|
||||
|
||||
static_assert (f() > 0);
|
||||
19
gcc/testsuite/g++.dg/contracts/contracts-conversion1.C
Normal file
19
gcc/testsuite/g++.dg/contracts/contracts-conversion1.C
Normal file
@@ -0,0 +1,19 @@
|
||||
// Test to ensure that diagnostic location for condition conversion is in the
|
||||
// right place.
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
|
||||
template<typename T>
|
||||
void fn()
|
||||
[[ pre: T{} ]] // { dg-error "no match" }
|
||||
{
|
||||
}
|
||||
|
||||
struct Z { };
|
||||
|
||||
int main(int, char**) {
|
||||
fn<int>();
|
||||
fn<Z>();
|
||||
return 0;
|
||||
}
|
||||
177
gcc/testsuite/g++.dg/contracts/contracts-ctor-dtor1.C
Normal file
177
gcc/testsuite/g++.dg/contracts/contracts-ctor-dtor1.C
Normal file
@@ -0,0 +1,177 @@
|
||||
// Tests to ensure that contracts are properly emitted for constructors,
|
||||
// destructors, and their intersection with templates.
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
bool pre_{false}, post_{false};
|
||||
bool delegate_{false};
|
||||
|
||||
struct S
|
||||
{
|
||||
S() [[ pre: pre_ ]] [[ post: post_ ]];
|
||||
~S() [[ pre: pre_ ]] [[ post: post_ ]];
|
||||
};
|
||||
|
||||
S::S() { return; }
|
||||
S::~S() { return; }
|
||||
|
||||
struct SInline
|
||||
{
|
||||
SInline() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
|
||||
~SInline() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
|
||||
};
|
||||
|
||||
struct SDelegate0
|
||||
{
|
||||
SDelegate0(int) [[ pre: pre_ ]] [[ post: post_ ]] { return; }
|
||||
SDelegate0() : SDelegate0(0) { return; }
|
||||
~SDelegate0() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
|
||||
};
|
||||
|
||||
struct SDelegate1
|
||||
{
|
||||
SDelegate1(int) { return; }
|
||||
SDelegate1() [[ pre: pre_ ]] [[ post: post_ ]] : SDelegate1(0) { return; }
|
||||
~SDelegate1() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
|
||||
};
|
||||
|
||||
struct SDelegate2
|
||||
{
|
||||
SDelegate2(int) [[ pre: pre_ ]] [[ post: post_ ]] { return; }
|
||||
SDelegate2() [[ pre: pre_ && delegate_ ]] [[ post: post_ && delegate_ ]] : SDelegate2(0) { return; }
|
||||
~SDelegate2() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
|
||||
};
|
||||
|
||||
struct SDelegate3
|
||||
{
|
||||
SDelegate3(int) [[ pre: pre_ ]] [[ post: post_ ]] { return; }
|
||||
SDelegate3() [[ pre: pre_ && delegate_ ]] [[ post: post_ && delegate_ ]] : SDelegate3(0) { return; }
|
||||
~SDelegate3() [[ pre: pre_ ]] [[ post: post_ ]] { return; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct S1
|
||||
{
|
||||
S1() [[ pre: pre_ ]] [[ post: post_ ]] { }
|
||||
};
|
||||
|
||||
struct S2
|
||||
{
|
||||
template<typename T>
|
||||
S2(T) [[ pre: pre_ ]] [[ post: post_ ]] { }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct S3
|
||||
{
|
||||
template<typename S>
|
||||
S3(S) [[ pre: pre_ ]] [[ post: post_ ]] { }
|
||||
};
|
||||
|
||||
struct G0
|
||||
{
|
||||
G0() [[ post: x > 0 ]] {}
|
||||
~G0() [[ pre: x > 0 ]] {}
|
||||
int x{-1};
|
||||
};
|
||||
|
||||
struct G1
|
||||
{
|
||||
G1() [[ post: this->x > 0 ]] {}
|
||||
~G1() [[ pre: this->x > 0 ]] {}
|
||||
int x{-1};
|
||||
};
|
||||
|
||||
int x{-1};
|
||||
|
||||
struct G2
|
||||
{
|
||||
G2() [[ pre: ::x > 0 ]] {}
|
||||
~G2() [[ post: ::x > 0 ]] {}
|
||||
int x{1};
|
||||
};
|
||||
|
||||
void test0()
|
||||
{
|
||||
S s;
|
||||
SInline si;
|
||||
SDelegate0 sd0;
|
||||
SDelegate1 sd1;
|
||||
SDelegate2 sd2;
|
||||
SDelegate3 sd3;
|
||||
S1<int> s1_i;
|
||||
S1<double> s1_d;
|
||||
S2 s2_i{1};
|
||||
S2 s2_d{.1};
|
||||
S3<int> s3_i_i{1};
|
||||
S3<int> s3_i_d{.1};
|
||||
S3<double> s3_d_i{1};
|
||||
S3<double> s3_d_d{.1};
|
||||
}
|
||||
|
||||
void test1()
|
||||
{
|
||||
G0 g0;
|
||||
G1 g1;
|
||||
G2 g2;
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
test0();
|
||||
test1();
|
||||
return 0;
|
||||
};
|
||||
|
||||
// test0
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 11 S::S .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 11 S::S .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 20 SInline::SInline .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 20 SInline::SInline .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 26 SDelegate0::SDelegate0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 26 SDelegate0::SDelegate0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 34 SDelegate1::SDelegate1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 34 SDelegate1::SDelegate1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 41 SDelegate2::SDelegate2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 40 SDelegate2::SDelegate2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 40 SDelegate2::SDelegate2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 41 SDelegate2::SDelegate2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 48 SDelegate3::SDelegate3 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 47 SDelegate3::SDelegate3 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 47 SDelegate3::SDelegate3 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 48 SDelegate3::SDelegate3 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 55 S1<int>::S1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 55 S1<int>::S1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 55 S1<double>::S1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 55 S1<double>::S1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 61 S2::S2<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 61 S2::S2<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 61 S2::S2<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 61 S2::S2<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<int>::S3<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<int>::S3<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<int>::S3<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<int>::S3<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<double>::S3<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<double>::S3<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<double>::S3<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 68 S3<double>::S3<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 49 SDelegate3::~SDelegate3 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 49 SDelegate3::~SDelegate3 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 42 SDelegate2::~SDelegate2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 42 SDelegate2::~SDelegate2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 35 SDelegate1::~SDelegate1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 35 SDelegate1::~SDelegate1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 28 SDelegate0::~SDelegate0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 28 SDelegate0::~SDelegate0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 21 SInline::~SInline .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 21 SInline::~SInline .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 12 S::~S .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 12 S::~S .*(\n|\r\n|\r)*" }
|
||||
|
||||
// test1
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 73 G0::G0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 80 G1::G1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 81 G1::~G1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 74 G0::~G0 .*(\n|\r\n|\r)*" }
|
||||
|
||||
35
gcc/testsuite/g++.dg/contracts/contracts-ctor-dtor2.C
Normal file
35
gcc/testsuite/g++.dg/contracts/contracts-ctor-dtor2.C
Normal file
@@ -0,0 +1,35 @@
|
||||
// Tests to ensure that an invalid this parm cannot be used in pre on ctors or
|
||||
// in post on dtors.
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
struct F0
|
||||
{
|
||||
F0() [[ pre: x > 0 ]]; // { dg-error "invalid use of member" }
|
||||
~F0() [[ post: x > 0 ]]; // { dg-error "invalid use of member" }
|
||||
int x{-1};
|
||||
};
|
||||
|
||||
struct F1
|
||||
{
|
||||
F1() [[ pre: this->x > 0 ]]; // { dg-error "may not be used" }
|
||||
~F1() [[ post: this->x > 0 ]]; // { dg-error "may not be used" }
|
||||
int x{-1};
|
||||
};
|
||||
|
||||
struct F2
|
||||
{
|
||||
F2()
|
||||
[[ post ret: false ]] // { dg-error "does not return a value" }
|
||||
{
|
||||
}
|
||||
~F2()
|
||||
[[ post r: false ]] // { dg-error "does not return a value" }
|
||||
{
|
||||
}
|
||||
void f()
|
||||
[[ post r: false ]] // { dg-error "does not return a value" }
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
37
gcc/testsuite/g++.dg/contracts/contracts-cv1.C
Normal file
37
gcc/testsuite/g++.dg/contracts/contracts-cv1.C
Normal file
@@ -0,0 +1,37 @@
|
||||
// Tests to ensure that contracts have a properly cv qualified this
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
struct S
|
||||
{
|
||||
int g() const { return x_; }
|
||||
int f() { return x_; }
|
||||
|
||||
void mem_c() const
|
||||
[[ pre: f() ]] // { dg-error "discards qualifiers" }
|
||||
{
|
||||
}
|
||||
void mem_nc()
|
||||
[[ pre: f() ]]
|
||||
{
|
||||
}
|
||||
|
||||
void memc_c() const
|
||||
[[ pre: g() ]]
|
||||
{
|
||||
}
|
||||
void memc_nc()
|
||||
[[ pre: g() ]]
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
int x_{-10};
|
||||
};
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
S s;
|
||||
return 0;
|
||||
};
|
||||
|
||||
108
gcc/testsuite/g++.dg/contracts/contracts-deduced1.C
Normal file
108
gcc/testsuite/g++.dg/contracts/contracts-deduced1.C
Normal file
@@ -0,0 +1,108 @@
|
||||
// Tests to ensure that deduced return types work with postconditions using
|
||||
// the return value on defining declarations.
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts --param ggc-min-heapsize=0 --param ggc-min-expand=0" }
|
||||
|
||||
auto undeduced(int z)
|
||||
{
|
||||
if (!(sizeof(decltype(undeduced(5))) > 4)) // { dg-error "before deduction" }
|
||||
return 5;
|
||||
return 4;
|
||||
}
|
||||
|
||||
// defining declaration, fine
|
||||
auto g0(int a) [[ pre: a < 0 ]] [[ post r: r > 0 ]]
|
||||
{
|
||||
return -a * 1.2;
|
||||
}
|
||||
|
||||
// non defining post using nondeduced identifier, fine
|
||||
int g1(int m) [[ post r: r == m ]];
|
||||
|
||||
int g1(int n) [[ post s: s == n ]]
|
||||
{
|
||||
return -n;
|
||||
}
|
||||
|
||||
int g2(int z)
|
||||
[[ pre: sizeof(decltype(g2(5))) > 4 ]]; // { dg-error "not declared" }
|
||||
|
||||
int g3(int z)
|
||||
[[ pre: sizeof(decltype(g2(5))) > 4 ]]
|
||||
{
|
||||
return -z;
|
||||
}
|
||||
|
||||
// deduced that doesn't use return, good
|
||||
auto g4(int m) [[ post: m ]];
|
||||
|
||||
auto g4(int m) [[ post: m ]]
|
||||
{
|
||||
return -m;
|
||||
}
|
||||
|
||||
auto g5(int m) [[ pre: m ]];
|
||||
|
||||
auto g5(int m) [[ pre: m ]]
|
||||
{
|
||||
return -m;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto g6(T t) [[ post r: r == t ]];
|
||||
|
||||
template<typename S>
|
||||
auto g6(S s) [[ post q: q == s ]]
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T g7(T t) [[ post r: r == t ]];
|
||||
|
||||
template<typename S>
|
||||
S g7(S s) [[ post q: q == s ]]
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto g8(T t) [[ post r: r == t && sizeof(decltype(::g8(t))) > 2 ]]; // { dg-error "not been declared" }
|
||||
|
||||
// This failure is related to the fact that we've invalidated the previous
|
||||
// contract.
|
||||
template<typename S>
|
||||
auto g8(S s) [[ post q: q == s && sizeof(decltype(::g8(s))) > 2 ]] // { dg-error "mismatched" }
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
// non defining pre, bad
|
||||
auto f0(int z)
|
||||
[[ pre: sizeof(decltype(f0(5))) > 4 ]]; // { dg-error "not declared" }
|
||||
|
||||
// defining pre, still ill formed
|
||||
auto f1(int z)
|
||||
[[ pre: sizeof(decltype(f1(5))) > 4 ]] // { dg-error "not declared" }
|
||||
{
|
||||
return '5';
|
||||
}
|
||||
|
||||
// undeduced using postcon, OK
|
||||
auto f2(int m) [[ post r: r == m ]];
|
||||
|
||||
auto f2(int n) [[ post s: s == n ]]
|
||||
{
|
||||
return n;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void f3(T t) [[ post r: false ]] // { dg-error "function does not return a value" }
|
||||
{
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
84
gcc/testsuite/g++.dg/contracts/contracts-deduced2.C
Normal file
84
gcc/testsuite/g++.dg/contracts/contracts-deduced2.C
Normal file
@@ -0,0 +1,84 @@
|
||||
// check that contracts work around deduced return types
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
|
||||
auto g0(int a) [[ pre: a < 0 ]] [[ post r: r > 0 ]]
|
||||
{
|
||||
return -a * 1.2;
|
||||
}
|
||||
|
||||
int g1(int m) [[ post r: r == m ]];
|
||||
|
||||
int g1(int n) [[ post s: s == n ]]
|
||||
{
|
||||
return -n;
|
||||
}
|
||||
|
||||
int g2(int z)
|
||||
{
|
||||
return -z;
|
||||
}
|
||||
|
||||
int g3(int z)
|
||||
[[ pre: sizeof(decltype(g2(5))) > 4 ]]
|
||||
{
|
||||
return -z;
|
||||
}
|
||||
|
||||
auto g4(int m) [[ post: m ]];
|
||||
|
||||
auto g4(int m) [[ post: m ]]
|
||||
{
|
||||
return -m;
|
||||
}
|
||||
|
||||
auto g5(int m) [[ pre: m ]];
|
||||
|
||||
auto g5(int m) [[ pre: m ]]
|
||||
{
|
||||
return -m;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto g6(T t) [[ post r: r == t ]];
|
||||
|
||||
template<typename S>
|
||||
auto g6(S s) [[ post q: q == s ]]
|
||||
{
|
||||
return -s;
|
||||
}
|
||||
|
||||
// template<typename T>
|
||||
// T g7(T t) [[ post r: r == t ]];
|
||||
|
||||
template<typename S>
|
||||
S g7(S s) [[ post q: q == s ]]
|
||||
{
|
||||
return -s;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
g0(5);
|
||||
g1(6);
|
||||
g2(1);
|
||||
g3(1);
|
||||
g4(0);
|
||||
g5(0);
|
||||
g6(5);
|
||||
g6(5.5);
|
||||
g7(5);
|
||||
g7(6.6);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 5 g0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 5 g0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 12 g1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 23 g3 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 30 g4 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 37 g5 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 46 g6<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 46 g6<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 55 g7<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 55 g7<double> .*(\n|\r\n|\r)*" }
|
||||
|
||||
40
gcc/testsuite/g++.dg/contracts/contracts-friend1.C
Normal file
40
gcc/testsuite/g++.dg/contracts/contracts-friend1.C
Normal file
@@ -0,0 +1,40 @@
|
||||
// ensure contracts on friend declarations are a complete class context
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
|
||||
struct X {
|
||||
friend void fn0(X x) [[ pre: x.a > 0 ]] { }
|
||||
|
||||
friend void fn2(X x);
|
||||
static void fns0(X x) [[ pre: x.a > 0 ]] { }
|
||||
static void fns1(X x) [[ pre: x.a > 0 ]];
|
||||
static void fns2(X x);
|
||||
|
||||
friend void fn(X &x) { x.a = -5; }
|
||||
|
||||
private:
|
||||
int a{10};
|
||||
};
|
||||
|
||||
void fn2(X x) [[ pre: x.a > 0 ]] { }
|
||||
void X::fns1(X x) { }
|
||||
void X::fns2(X x) [[ pre: x.a > 0 ]] { }
|
||||
|
||||
int main(int, char**) {
|
||||
X x;
|
||||
fn(x); // no contract
|
||||
|
||||
fn0(x);
|
||||
fn2(x);
|
||||
|
||||
X::fns0(x);
|
||||
X::fns1(x);
|
||||
X::fns2(x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 6 fn0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 19 fn2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 9 X::fns0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 X::fns1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 21 X::fns2 .*(\n|\r\n|\r)*" }
|
||||
14
gcc/testsuite/g++.dg/contracts/contracts-ft1.C
Normal file
14
gcc/testsuite/g++.dg/contracts/contracts-ft1.C
Normal file
@@ -0,0 +1,14 @@
|
||||
// { dg-do compile }
|
||||
|
||||
#ifdef __cpp_contracts
|
||||
static_assert (false);
|
||||
#endif
|
||||
|
||||
#ifdef __cpp_contracts_literal_semantics
|
||||
static_assert (false);
|
||||
#endif
|
||||
|
||||
#ifdef __cpp_contracts_roles
|
||||
static_assert (false);
|
||||
#endif
|
||||
|
||||
30
gcc/testsuite/g++.dg/contracts/contracts-ignore1.C
Normal file
30
gcc/testsuite/g++.dg/contracts/contracts-ignore1.C
Normal file
@@ -0,0 +1,30 @@
|
||||
// test that ignored contracts do instatiate templates
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
template<typename T>
|
||||
int f(T t)
|
||||
{
|
||||
return -t;
|
||||
}
|
||||
|
||||
int dummy()
|
||||
{
|
||||
[[ assert ignore: f(1.0) > 0 ]];
|
||||
return -1;
|
||||
}
|
||||
|
||||
template<>
|
||||
int f(double t) // { dg-error "specialization of.*after instantiation" }
|
||||
{
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
dummy();
|
||||
f(1);
|
||||
f(1.0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
26
gcc/testsuite/g++.dg/contracts/contracts-ignore2.C
Normal file
26
gcc/testsuite/g++.dg/contracts/contracts-ignore2.C
Normal file
@@ -0,0 +1,26 @@
|
||||
// baseline for testing assert contracts being turned into compile time
|
||||
// assumptions; see contracts-assume2 for the assumed case
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
#include <cstdio>
|
||||
|
||||
int fun(int x) {
|
||||
[[assert audit: x > 0]];
|
||||
if(x <= 0)
|
||||
{
|
||||
printf("%d: test x<=0 opt out\n", x);
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%d: test x>0\n", x);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
volatile int x = -1;
|
||||
return fun(x);
|
||||
}
|
||||
|
||||
// { dg-shouldfail "" }
|
||||
15
gcc/testsuite/g++.dg/contracts/contracts-large-return.C
Normal file
15
gcc/testsuite/g++.dg/contracts/contracts-large-return.C
Normal file
@@ -0,0 +1,15 @@
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
struct Foo
|
||||
{
|
||||
int x;
|
||||
bool y;
|
||||
long z[4];
|
||||
};
|
||||
|
||||
Foo foo() [[ pre: true ]]
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
19
gcc/testsuite/g++.dg/contracts/contracts-multiline1.C
Normal file
19
gcc/testsuite/g++.dg/contracts/contracts-multiline1.C
Normal file
@@ -0,0 +1,19 @@
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
|
||||
int main(int, char **)
|
||||
{
|
||||
int x = 5;
|
||||
int y = 10;
|
||||
[[ assert:
|
||||
x
|
||||
<
|
||||
10
|
||||
&&
|
||||
y
|
||||
>
|
||||
123
|
||||
]];
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 8 main x < 10 && y > 123.*(\n|\r\n|\r)*" }
|
||||
@@ -0,0 +1,15 @@
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
|
||||
struct BaseA {
|
||||
virtual int fun(int n) [[ pre: n > 0 ]] { return -n; }
|
||||
};
|
||||
|
||||
struct BaseB {
|
||||
virtual int fun(int n) [[ pre: n > 0 ]] { return -n; }
|
||||
};
|
||||
|
||||
struct Child : public BaseA, BaseB {
|
||||
int fun(int n) [[ pre: n > 0 ]] { return -n; }
|
||||
};
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
|
||||
struct BaseA {
|
||||
virtual int fun(int n) [[ pre: n > 0 ]] { return -n; }
|
||||
};
|
||||
|
||||
struct BaseB {
|
||||
virtual int fun(int n) [[ pre: n < 0 ]] { return -n; }
|
||||
};
|
||||
|
||||
struct Child1 : public BaseA, BaseB {
|
||||
int fun(int n) [[ pre: n > 0 ]] { return -n; } // { dg-error "mismatched" }
|
||||
};
|
||||
|
||||
struct Child2 : public BaseA, BaseB {
|
||||
int fun(int n) [[ pre: n < 0 ]] { return -n; } // { dg-error "mismatched" }
|
||||
};
|
||||
|
||||
struct Child3 : public BaseA, BaseB {
|
||||
int fun(int n) { return -n; }
|
||||
};
|
||||
|
||||
struct Child4 : public BaseA {
|
||||
int fun(int n);
|
||||
};
|
||||
|
||||
int Child4::fun(int n)
|
||||
[[ pre: n != 0 ]] // { dg-error "mismatched" }
|
||||
{
|
||||
return -n;
|
||||
}
|
||||
|
||||
24
gcc/testsuite/g++.dg/contracts/contracts-nested-class1.C
Normal file
24
gcc/testsuite/g++.dg/contracts/contracts-nested-class1.C
Normal file
@@ -0,0 +1,24 @@
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
void gfn3(int n) [[ pre: n > 0 ]];
|
||||
|
||||
struct Outer {
|
||||
struct Inner {
|
||||
void fn(int n) [[ pre: n > 0 && bob > 1 ]];
|
||||
void fn2(int n) [[ pre: n > 0 && bob > 1 ]];
|
||||
};
|
||||
|
||||
void fn(int m) [[ pre: m > 1 ]];
|
||||
friend void Inner::fn(int n) [[ pre: n > 0 && bob > 1 ]]; // { dg-error "not declared" }
|
||||
|
||||
friend void gfn(int p) [[ pre: p > 0 ]];
|
||||
friend void gfn(int q) [[ pre: q > 1 ]]; // { dg-error "'q' was not declared" }
|
||||
|
||||
// This should be okay.
|
||||
friend void gfn2(int q);
|
||||
friend void gfn2(int p) [[ pre: p > 0 ]] { }
|
||||
|
||||
static int bob;
|
||||
};
|
||||
int Outer::bob{-1};
|
||||
40
gcc/testsuite/g++.dg/contracts/contracts-nested-class2.C
Normal file
40
gcc/testsuite/g++.dg/contracts/contracts-nested-class2.C
Normal file
@@ -0,0 +1,40 @@
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
|
||||
void gfn3(int n) [[ pre: n > 0 ]];
|
||||
|
||||
struct Outer {
|
||||
struct Inner {
|
||||
void fn(int n) [[ pre: n > 0 && bob > 1 ]];
|
||||
};
|
||||
|
||||
void fn(int m) [[ pre: m > 1 ]];
|
||||
|
||||
friend void gfn1(int q);
|
||||
friend void gfn1(int p) [[ pre: p > 0 ]] { }
|
||||
|
||||
friend void gfn2(int q, Outer *);
|
||||
friend void gfn2(int p, Outer *) [[ pre: p > 0 ]] { }
|
||||
|
||||
friend void gfn3(int n);
|
||||
|
||||
static int bob;
|
||||
};
|
||||
int Outer::bob{-1};
|
||||
|
||||
void Outer::Inner::fn(int x) { }
|
||||
void Outer::fn(int y) { }
|
||||
|
||||
void gfn3(int n) { }
|
||||
void gfn1(int q);
|
||||
|
||||
int main(int, char **) {
|
||||
Outer::Inner in;
|
||||
in.fn(-5);
|
||||
Outer out;
|
||||
out.fn(-6);
|
||||
gfn1(-7);
|
||||
gfn2(-8, &out);
|
||||
gfn3(-9);
|
||||
}
|
||||
|
||||
24
gcc/testsuite/g++.dg/contracts/contracts-nocopy1.C
Normal file
24
gcc/testsuite/g++.dg/contracts/contracts-nocopy1.C
Normal file
@@ -0,0 +1,24 @@
|
||||
// Contracts shouldn't introduce more copies.
|
||||
|
||||
// { dg-do compile { target c++20 } }
|
||||
// { dg-additional-options -fcontracts }
|
||||
|
||||
struct A
|
||||
{
|
||||
int i;
|
||||
A(int i): i(i) { }
|
||||
A(const A&) = delete;
|
||||
};
|
||||
|
||||
A f(A a)
|
||||
[[ pre: a.i > 0 ]]
|
||||
[[ post r: r.i > 0 ]]
|
||||
{
|
||||
return {a.i};
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
if (f({42}).i != 42)
|
||||
__builtin_abort ();
|
||||
}
|
||||
43
gcc/testsuite/g++.dg/contracts/contracts-override.C
Normal file
43
gcc/testsuite/g++.dg/contracts/contracts-override.C
Normal file
@@ -0,0 +1,43 @@
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
struct Foo {
|
||||
virtual int f0(int n) [[ pre: false ]] { return n; }
|
||||
virtual int f1(int n) [[ pre: false ]] { return n; }
|
||||
virtual int f2(int n) [[ pre: false ]] { return n; }
|
||||
virtual int f3(int n) [[ pre: false ]] { return n; }
|
||||
virtual int f4(int n) [[ pre: false ]] { return n; }
|
||||
virtual int f5(int n) [[ pre: false ]] { return n; }
|
||||
virtual int f6(int n) [[ pre: false ]] { return n; }
|
||||
virtual int f7(int n) [[ pre: false ]] { return n; }
|
||||
virtual int f8(int n) [[ pre: false ]] { return n; }
|
||||
virtual int f9(int n) [[ pre: false ]] { return n; }
|
||||
virtual int f10(int n) [[ pre: false ]] { return n; }
|
||||
virtual int f11(int n) [[ pre: n > 0 ]] [[ pre: n > 1 ]] { return n; }
|
||||
virtual int f12(int n) [[ pre: n > 0 ]] [[ pre: n > 1 ]] { return n; }
|
||||
};
|
||||
|
||||
struct Bar : Foo {
|
||||
[[ pre: n > -1 ]] int f0(int n = 0) override { return -n; } // { dg-error "contracts must appertain" }
|
||||
int [[ pre: n > -2 ]] f1(int n = 0) override { return -n; } // { dg-error "contracts must appertain" }
|
||||
int f2 [[ pre: n > -3 ]] (int n = 0) override { return -n; } // { dg-error "contracts must appertain" }
|
||||
int f4([[ pre: n > -4 ]] int n = 0) override { return -n; } // { dg-error "contracts must appertain" }
|
||||
int f5(int [[ pre: n > -5 ]] n = 0) override { return -n; } // { dg-error "contracts must appertain" }
|
||||
int f6(int n [[ pre: n > -6 ]] = 0) override { return -n; } // { dg-error "contracts must appertain" }
|
||||
int f7(int n = [[ pre: n > -7 ]] 0) override { return -n; }
|
||||
// { dg-error "expected identifier" "" { target *-*-* } .-1 }
|
||||
// { dg-error "expected .\{. before numeric" "" { target *-*-* } .-2 }
|
||||
// { dg-error "invalid user-defined conversion" "" { target *-*-* } .-3 }
|
||||
// { dg-error "expected .,." "" { target *-*-* } .-4 }
|
||||
int f8(int n = 0 [[ pre: n > -8 ]]) override { return -n; }
|
||||
// { dg-error "shall only introduce an attribute" "" { target *-*-* } .-1 }
|
||||
int f9(int n = 0) [[ pre: n > -9 ]] override { return -n; } // { dg-error "mismatched contract" }
|
||||
|
||||
// The grammar doesn't appear to permit contracts after the virt-specifiers
|
||||
// but the parser will happily add these to an attribute list that is not
|
||||
// owned by the function declarator.
|
||||
int f10(int n = 0) override [[ pre: n > -10 ]] { return -n; } // { dg-error "contracts must appertain" }
|
||||
int f11(int n) [[ pre: n > 1 ]] override [[ pre: n > 0 ]] { return -n; } // { dg-error "contracts must appertain" }
|
||||
int f12(int n) [[ pre: n > 0 ]] override [[ pre: n > 1 ]] { return -n; } // { dg-error "contracts must appertain" }
|
||||
};
|
||||
|
||||
74
gcc/testsuite/g++.dg/contracts/contracts-post1.C
Normal file
74
gcc/testsuite/g++.dg/contracts/contracts-post1.C
Normal file
@@ -0,0 +1,74 @@
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
int f1(int n)
|
||||
[[pre: n >= 0]]
|
||||
[[post r: r >= 0]]
|
||||
[[post r: !(r < 0)]]
|
||||
{
|
||||
return n;
|
||||
}
|
||||
|
||||
int f2(int n)
|
||||
[[post: true]]
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int f3(int n)
|
||||
[[post r: r >= n]]
|
||||
{
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
int f4(int n)
|
||||
[[post: x > 0]] // { dg-error "not declared" }
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void f5()
|
||||
[[post: true]]
|
||||
{ }
|
||||
|
||||
void f6()
|
||||
[[post r: true]] // { dg-error "function does not return a value" }
|
||||
{ }
|
||||
|
||||
int f7(int n)
|
||||
[[post: n > 0]]
|
||||
{
|
||||
return x; // { dg-error "not declared" }
|
||||
}
|
||||
|
||||
void f8(int n)
|
||||
[[post: n > 0]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void f9(int n)
|
||||
[[post: n > 0]]
|
||||
{
|
||||
return n; // { dg-error "return-statement with a value" }
|
||||
}
|
||||
|
||||
int f10(int n)
|
||||
[[post: n > 0]]
|
||||
{
|
||||
return; // { dg-error "return-statement with no value" }
|
||||
}
|
||||
|
||||
void f11()
|
||||
[[post: true]]
|
||||
{
|
||||
constexpr int n = 0;
|
||||
return n; // { dg-error "return-statement with a value" }
|
||||
}
|
||||
|
||||
int f12()
|
||||
[[post: true]]
|
||||
{
|
||||
return; // { dg-error "return-statement with no value" }
|
||||
}
|
||||
|
||||
13
gcc/testsuite/g++.dg/contracts/contracts-post2.C
Normal file
13
gcc/testsuite/g++.dg/contracts/contracts-post2.C
Normal file
@@ -0,0 +1,13 @@
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
int f1(int n)
|
||||
[[post r: r == n]]
|
||||
{
|
||||
return n;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
f1(0);
|
||||
}
|
||||
15
gcc/testsuite/g++.dg/contracts/contracts-post3.C
Normal file
15
gcc/testsuite/g++.dg/contracts/contracts-post3.C
Normal file
@@ -0,0 +1,15 @@
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
// { dg-shouldfail "assert violation" }
|
||||
// { dg-output "default std::handle_contract_violation called" }
|
||||
|
||||
int f1(int n)
|
||||
[[post r: r > n]]
|
||||
{
|
||||
return n;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
f1(0);
|
||||
}
|
||||
36
gcc/testsuite/g++.dg/contracts/contracts-post4.C
Normal file
36
gcc/testsuite/g++.dg/contracts/contracts-post4.C
Normal file
@@ -0,0 +1,36 @@
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
struct S
|
||||
{
|
||||
S() [[post: n == 0]]
|
||||
: n(0)
|
||||
{ }
|
||||
|
||||
~S() [[post: true]]
|
||||
{ }
|
||||
|
||||
int f1()
|
||||
[[post r: n == r]]
|
||||
{
|
||||
return n;
|
||||
}
|
||||
|
||||
int f2()
|
||||
[[post r: r == x]] // { dg-error "not declared" }
|
||||
{
|
||||
return n;
|
||||
}
|
||||
|
||||
void f3()
|
||||
[[post r: n]] // { dg-error "does not return a value" }
|
||||
{
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
// f1(0);
|
||||
}
|
||||
19
gcc/testsuite/g++.dg/contracts/contracts-post5.C
Normal file
19
gcc/testsuite/g++.dg/contracts/contracts-post5.C
Normal file
@@ -0,0 +1,19 @@
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
// Tests for function templates
|
||||
|
||||
template<typename T>
|
||||
T f1(T n)
|
||||
[[pre: n >= 0]]
|
||||
[[post r: r >= 0]]
|
||||
[[post r: !(r < 0)]]
|
||||
{
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
void driver()
|
||||
{
|
||||
f1(0);
|
||||
}
|
||||
30
gcc/testsuite/g++.dg/contracts/contracts-post6.C
Normal file
30
gcc/testsuite/g++.dg/contracts/contracts-post6.C
Normal file
@@ -0,0 +1,30 @@
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
// Test for class members
|
||||
|
||||
template<typename T>
|
||||
struct S
|
||||
{
|
||||
S(T n)
|
||||
[[post: true]]
|
||||
: n(n)
|
||||
{ }
|
||||
|
||||
T f1(T n)
|
||||
[[pre: n >= 0]]
|
||||
[[post r: r >= 0]]
|
||||
[[post r: !(r < 0)]]
|
||||
{
|
||||
return n;
|
||||
}
|
||||
|
||||
T n;
|
||||
};
|
||||
|
||||
|
||||
void driver()
|
||||
{
|
||||
S<int> s1(0);
|
||||
s1.f1(2);
|
||||
}
|
||||
36
gcc/testsuite/g++.dg/contracts/contracts-pre1.C
Normal file
36
gcc/testsuite/g++.dg/contracts/contracts-pre1.C
Normal file
@@ -0,0 +1,36 @@
|
||||
// generic pre contract parsing checks
|
||||
// check omitted, 'default', 'audit', and 'axiom' contract levels parse
|
||||
// ensure that an invalid contrcat level 'off' errors
|
||||
// ensure that a predicate referencing an undefined variable errors
|
||||
// ensure that a missing colon after contract level errors
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
void f1(int x) [[ pre: x >= 0 ]] { }
|
||||
void f2(int x) [[ pre default: x >= 0 ]] { }
|
||||
void f3(int x) [[ pre audit: x >= 0 ]] { }
|
||||
void f4(int x) [[ pre axiom: x >= 0 ]] { }
|
||||
|
||||
void finvalid(int x) [[ pre invalid: x >= 0 ]] { } // { dg-error "expected contract level" }
|
||||
void fundeclared() [[ pre: x >= 0 ]] { } // { dg-error ".x. was not declared in this scope" }
|
||||
void fmissingcolon(int x) [[ pre default x == 0]] { } // { dg-error "expected .:. before .x." }
|
||||
|
||||
int Z;
|
||||
void (*fp1)(int x) [[ pre: Z > 0 ]]; // { dg-error "contracts must appertain" }
|
||||
void (*fp2 [[ pre: Z > 0 ]])(int x); // { dg-error "contracts must appertain" }
|
||||
typedef void (*fp3)(int x) [[ pre: Z > 0 ]]; // { dg-error "contracts must appertain" }
|
||||
typedef void (*fp4 [[ pre: Z > 0 ]])(int x); // { dg-error "contracts must appertain" }
|
||||
fp3 fn5(int a) [[ pre: a > 0 ]]; // { dg-bogus "contracts must appertain" }
|
||||
|
||||
int xyz;
|
||||
[[ pre: xyz ]] struct Bar; // { dg-error "contracts must appertain" }
|
||||
// { dg-warning "attribute ignored" "" { target *-*-* } .-1 }
|
||||
struct [[ pre: xyz ]] Bar; // { dg-error "contracts must appertain" }
|
||||
struct Bar [[ pre: xyz ]]; // { dg-error "contracts must appertain" }
|
||||
struct Zoo {} x [[ pre: xyz ]]; // { dg-error "contracts must appertain" }
|
||||
|
||||
void f6(int x) [[ pre: x > 0 ; // { dg-error "expected .]." }
|
||||
void f7(int x) [[ pre: x > 0 ]; // { dg-error "expected .]." }
|
||||
void f8(int x) [[ pre: x > 0 { }; // { dg-error "expected .]." }
|
||||
void f9(int x) [[ pre: x > 0 ] { }; // { dg-error "expected .]." }
|
||||
|
||||
190
gcc/testsuite/g++.dg/contracts/contracts-pre10.C
Normal file
190
gcc/testsuite/g++.dg/contracts/contracts-pre10.C
Normal file
@@ -0,0 +1,190 @@
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
|
||||
struct M
|
||||
{
|
||||
template<typename T>
|
||||
int f(int a) [[ pre: a > 0 ]];
|
||||
|
||||
template<typename T>
|
||||
int g(int a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int f_arg(T a) [[ pre: a > 0 ]];
|
||||
|
||||
template<typename T>
|
||||
int g_arg(T a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return (int)-a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T f_ret(int a) [[ pre: a > 0 ]];
|
||||
|
||||
template<typename T>
|
||||
T g_ret(int a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a * 1.5;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
int M::f(int a)
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int M::f_arg(T a)
|
||||
{
|
||||
return (int)-a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T M::f_ret(int a)
|
||||
{
|
||||
return -a * (T)1.5;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct S
|
||||
{
|
||||
template<typename Q>
|
||||
int f(int a) [[ pre: a > 0 ]];
|
||||
|
||||
template<typename Q>
|
||||
int g(int a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
template<typename Q>
|
||||
int S<T>::f(int a)
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
#include <cstdio>
|
||||
int main(int, char**)
|
||||
{
|
||||
{
|
||||
M m;
|
||||
printf ("=================================\n");
|
||||
printf ("m.f<int>(-10): %d\n", m.f<int>(-10));
|
||||
printf ("m.f<double>(-11.5): %d\n", m.f<double>(-11.5));
|
||||
printf ("m.f<int>(10): %d\n", m.f<int>(10));
|
||||
printf ("m.f<double>(11.5): %d\n", m.f<double>(11.5));
|
||||
|
||||
printf ("=================================\n");
|
||||
printf ("m.g<int>(-10): %d\n", m.g<int>(-10));
|
||||
printf ("m.g<double>(-11.5): %d\n", m.g<double>(-11.5));
|
||||
printf ("m.g<int>(10): %d\n", m.g<int>(10));
|
||||
printf ("m.g<double>(11.5): %d\n", m.g<double>(11.5));
|
||||
|
||||
printf ("=================================\n");
|
||||
printf ("m.f_arg(-10): %d\n", m.f_arg(-10));
|
||||
printf ("m.f_arg(-11.5): %d\n", m.f_arg(-11.5));
|
||||
printf ("m.f_arg(10): %d\n", m.f_arg(10));
|
||||
printf ("m.f_arg(11.5): %d\n", m.f_arg(11.5));
|
||||
|
||||
printf ("=================================\n");
|
||||
printf ("m.g_arg(-10): %d\n", m.g_arg(-10));
|
||||
printf ("m.g_arg(-11.5): %d\n", m.g_arg(-11.5));
|
||||
printf ("m.g_arg(10): %d\n", m.g_arg(10));
|
||||
printf ("m.g_arg(11.5): %d\n", m.g_arg(11.5));
|
||||
|
||||
printf ("=================================\n");
|
||||
printf ("m.f_ret<int>(-10): %d\n", m.f_ret<int>(-10));
|
||||
printf ("m.f_ret<double>(-11.5): %f\n", m.f_ret<double>(-11.5));
|
||||
printf ("m.f_ret<int>(10): %d\n", m.f_ret<int>(10));
|
||||
printf ("m.f_ret<double>(11.5): %f\n", m.f_ret<double>(11.5));
|
||||
|
||||
printf ("=================================\n");
|
||||
printf ("m.g_ret<int>(-10): %d\n", m.g_ret<int>(-10));
|
||||
printf ("m.g_ret<double>(-11.5): %f\n", m.g_ret<double>(-11.5));
|
||||
printf ("m.g_ret<int>(10): %d\n", m.g_ret<int>(10));
|
||||
printf ("m.g_ret<double>(11.5): %f\n", m.g_ret<double>(11.5));
|
||||
}
|
||||
|
||||
{
|
||||
S<int> s;
|
||||
printf ("=================================\n");
|
||||
s.f<int>(-10);
|
||||
|
||||
s.f<double>(-10);
|
||||
|
||||
printf ("=================================\n");
|
||||
s.g<int>(-10);
|
||||
|
||||
s.g<double>(-10);
|
||||
}
|
||||
|
||||
{
|
||||
S<double> s;
|
||||
printf ("=================================\n");
|
||||
s.f<int>(-10);
|
||||
|
||||
s.f<double>(-10);
|
||||
|
||||
printf ("=================================\n");
|
||||
s.g<int>(-10);
|
||||
|
||||
s.g<double>(-10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "=================================(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 7 M::f<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.f<int>.-10.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 7 M::f<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.f<double>.-11.5.: 11(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.f<int>.10.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.f<double>.11.5.: -11(\n|\r\n|\r)*" }
|
||||
// { dg-output "=================================(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 M::g<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.g<int>.-10.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 M::g<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.g<double>.-11.5.: 11(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.g<int>.10.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.g<double>.11.5.: -11(\n|\r\n|\r)*" }
|
||||
// { dg-output "=================================(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 16 M::f_arg<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.f_arg.-10.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 16 M::f_arg<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.f_arg.-11.5.: 11(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.f_arg.10.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.f_arg.11.5.: -11(\n|\r\n|\r)*" }
|
||||
// { dg-output "=================================(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 19 M::g_arg<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.g_arg.-10.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 19 M::g_arg<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.g_arg.-11.5.: 11(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.g_arg.10.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.g_arg.11.5.: -11(\n|\r\n|\r)*" }
|
||||
// { dg-output "=================================(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 25 M::f_ret<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.f_ret<int>.-10.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 25 M::f_ret<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.f_ret<double>.-11.5.: 16.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.f_ret<int>.10.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.f_ret<double>.11.5.: -16.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "=================================(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 28 M::g_ret<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.g_ret<int>.-10.: 15(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 28 M::g_ret<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.g_ret<double>.-11.5.: 16.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.g_ret<int>.10.: -15(\n|\r\n|\r)*" }
|
||||
// { dg-output "m.g_ret<double>.11.5.: -16.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "=================================(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 59 S<int>::g<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 59 S<int>::g<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "=================================(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 59 S<double>::g<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 59 S<double>::g<double> .*(\n|\r\n|\r)*" }
|
||||
212
gcc/testsuite/g++.dg/contracts/contracts-pre2.C
Normal file
212
gcc/testsuite/g++.dg/contracts/contracts-pre2.C
Normal file
@@ -0,0 +1,212 @@
|
||||
// basic test to ensure pre contracts work for free functions
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
#include <cstdio>
|
||||
|
||||
namespace nullary
|
||||
{
|
||||
int x = 10;
|
||||
int y = 10;
|
||||
|
||||
void fun()
|
||||
[[ pre: x < 0 ]]
|
||||
{
|
||||
printf("fun::x: %d\n", x);
|
||||
}
|
||||
|
||||
void fun2()
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: y < 0 ]]
|
||||
{
|
||||
printf("fun2::x: %d fun2::y: %d\n", x, y);
|
||||
}
|
||||
|
||||
void funend()
|
||||
[[ pre: x < 0 ]];
|
||||
}
|
||||
|
||||
namespace nonvoid
|
||||
{
|
||||
int x = 10;
|
||||
double y = 10.5;
|
||||
|
||||
struct S
|
||||
{
|
||||
bool z;
|
||||
};
|
||||
|
||||
void vfun()
|
||||
[[ pre: x < 0 ]]
|
||||
{
|
||||
printf("vfun::x: %d\n", x);
|
||||
}
|
||||
|
||||
int fun()
|
||||
[[ pre: x < 0 ]]
|
||||
{
|
||||
printf("fun::x: %d\n", x);
|
||||
return x;
|
||||
}
|
||||
|
||||
double fun2()
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: y < 0 ]]
|
||||
{
|
||||
printf("fun2::x: %d fun2::y: %f\n", x, y);
|
||||
return y;
|
||||
}
|
||||
|
||||
S funend()
|
||||
[[ pre: x < 0 ]];
|
||||
}
|
||||
|
||||
namespace nonnullary
|
||||
{
|
||||
int x = 10;
|
||||
double y = 10.5;
|
||||
|
||||
struct S
|
||||
{
|
||||
bool z;
|
||||
};
|
||||
|
||||
void vfun(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
{
|
||||
printf("vfun::x: %d\n", x);
|
||||
}
|
||||
|
||||
int fun(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
{
|
||||
printf("fun::x: %d\n", x);
|
||||
return x;
|
||||
}
|
||||
|
||||
double fun2(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: y < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
[[ pre: n < 0 ]]
|
||||
{
|
||||
printf("fun2::x: %d fun2::y: %f\n", x, y);
|
||||
return y;
|
||||
}
|
||||
|
||||
S funend(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]];
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
// nullary void
|
||||
{
|
||||
nullary::fun();
|
||||
nullary::fun2();
|
||||
nullary::funend();
|
||||
}
|
||||
|
||||
// nullary non void
|
||||
{
|
||||
nonvoid::vfun();
|
||||
|
||||
int f = 13;
|
||||
f = nonvoid::fun();
|
||||
printf("main::f: %d\n", f);
|
||||
double d = 13.37;
|
||||
d = nonvoid::fun2();
|
||||
printf("main::d: %f\n", d);
|
||||
nonvoid::S s = nonvoid::funend();
|
||||
printf("main::s.z: %d\n", s.z ? 1 : 0);
|
||||
}
|
||||
|
||||
// non-nullary non-void
|
||||
{
|
||||
int x = 11;
|
||||
double y = 11.5;
|
||||
|
||||
nonnullary::vfun(x, y);
|
||||
|
||||
int f = 13;
|
||||
f = nonnullary::fun(x, y);
|
||||
printf("main::f: %d\n", f);
|
||||
double d = 13.37;
|
||||
d = nonnullary::fun2(x, y);
|
||||
printf("main::d: %f\n", d);
|
||||
nonnullary::S s = nonnullary::funend(x, y);
|
||||
printf("main::s.z: %d\n", s.z ? 1 : 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace nullary
|
||||
{
|
||||
void funend()
|
||||
[[ pre: x < 0 ]]
|
||||
{
|
||||
printf("funend::x: %d\n", x);
|
||||
}
|
||||
}
|
||||
|
||||
namespace nonvoid
|
||||
{
|
||||
S funend()
|
||||
[[ pre: x < 0 ]]
|
||||
{
|
||||
printf("funend::x: %d\n", x);
|
||||
S s;
|
||||
s.z = true;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
namespace nonnullary
|
||||
{
|
||||
S funend(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
{
|
||||
printf("funend::x: %d\n", x);
|
||||
S s;
|
||||
s.z = true;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 12 nullary::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 18 nullary::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 19 nullary::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun2::x: 10 fun2::y: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 146 nullary::funend .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 39 nonvoid::vfun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 45 nonvoid::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::f: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 52 nonvoid::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 53 nonvoid::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 155 nonvoid::funend .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 74 nonnullary::vfun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 75 nonnullary::vfun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 81 nonnullary::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::f: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 88 nonnullary::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 89 nonnullary::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 90 nonnullary::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 91 nonnullary::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 167 nonnullary::funend .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 168 nonnullary::funend .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
|
||||
|
||||
33
gcc/testsuite/g++.dg/contracts/contracts-pre2a1.C
Normal file
33
gcc/testsuite/g++.dg/contracts/contracts-pre2a1.C
Normal file
@@ -0,0 +1,33 @@
|
||||
// ensure the feature test macros are defined pre c++20 while we still support
|
||||
// -fcontracts independent of std version
|
||||
// { dg-do compile { target c++11 } }
|
||||
// { dg-additional-options "-fcontracts" }
|
||||
|
||||
static_assert (__cpp_contracts >= 201906, "__cpp_contracts");
|
||||
static_assert (__cpp_contracts_literal_semantics >= 201906, "__cpp_contracts_literal_semantics");
|
||||
static_assert (__cpp_contracts_roles >= 201906, "__cpp_contracts_roles");
|
||||
|
||||
int main()
|
||||
{
|
||||
int x;
|
||||
|
||||
[[assert: x >= 0]];
|
||||
[[assert default: x < 0]];
|
||||
[[assert audit: x == 0]];
|
||||
[[assert axiom: x == 1]];
|
||||
|
||||
[[assert: x > 0 ? true : false]];
|
||||
[[assert: x < 0 ? true : false]];
|
||||
|
||||
[[assert ignore: x >= 0]];
|
||||
[[assert assume: x >= 0]];
|
||||
[[assert check_never_continue: x >= 0]];
|
||||
[[assert check_maybe_continue: x >= 0]];
|
||||
|
||||
[[assert %default: x >= 0]];
|
||||
[[assert default %default: x < 0]];
|
||||
[[assert audit %default: x == 0]];
|
||||
[[assert axiom %default: x == 1]];
|
||||
return 0;
|
||||
}
|
||||
|
||||
22
gcc/testsuite/g++.dg/contracts/contracts-pre2a2.C
Normal file
22
gcc/testsuite/g++.dg/contracts/contracts-pre2a2.C
Normal file
@@ -0,0 +1,22 @@
|
||||
// basic test to ensure contracts work pre-c++2a
|
||||
// { dg-do run { target c++11 } }
|
||||
// { dg-additional-options "-fcontracts -fcontract-continuation-mode=on" }
|
||||
|
||||
int f(int n)
|
||||
[[ pre: n > 0 ]]
|
||||
[[ post r: r < 0 ]]
|
||||
{
|
||||
[[ assert: n > 0 ]];
|
||||
return -n;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
f(-5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 6 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 9 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 7 .*(\n|\r\n|\r)*" }
|
||||
|
||||
525
gcc/testsuite/g++.dg/contracts/contracts-pre3.C
Normal file
525
gcc/testsuite/g++.dg/contracts/contracts-pre3.C
Normal file
@@ -0,0 +1,525 @@
|
||||
// tests to ensure pre contracts work on member functions
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
#include <cstdio>
|
||||
|
||||
namespace member
|
||||
{
|
||||
int x = 10;
|
||||
double y = 10.5;
|
||||
|
||||
struct S
|
||||
{
|
||||
bool z;
|
||||
};
|
||||
|
||||
struct T1
|
||||
{
|
||||
void vfun(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]];
|
||||
|
||||
int fun(int m, double n)
|
||||
[[ pre: x < 0 ]];
|
||||
|
||||
double fun2(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: y < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
[[ pre: n < 0 ]];
|
||||
|
||||
S funend(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]];
|
||||
};
|
||||
|
||||
void T1::vfun(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
{
|
||||
printf("vfun::x: %d\n", x);
|
||||
}
|
||||
|
||||
|
||||
int T1::fun(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
{
|
||||
printf("fun::x: %d\n", x);
|
||||
return x;
|
||||
}
|
||||
|
||||
double T1::fun2(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: y < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
[[ pre: n < 0 ]]
|
||||
{
|
||||
printf("fun2::x: %d fun2::y: %f\n", x, y);
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
namespace special
|
||||
{
|
||||
struct T1
|
||||
{
|
||||
T1(int m, int n)
|
||||
[[ pre: m < 0 ]]
|
||||
[[ pre: n < 0 ]];
|
||||
|
||||
int operator+(int m)
|
||||
[[ pre: m > 0 ]]
|
||||
[[ pre: v > 0 ]];
|
||||
|
||||
int operator-(int m)
|
||||
[[ pre: m > 0 ]]
|
||||
[[ pre: v > 0 ]];
|
||||
|
||||
~T1()
|
||||
[[ pre: v > 0 ]];
|
||||
|
||||
int v{-10};
|
||||
};
|
||||
|
||||
T1::T1(int m, int n)
|
||||
[[ pre: m < 0 ]]
|
||||
[[ pre: n < 0 ]]
|
||||
: v{-m * n}
|
||||
{
|
||||
}
|
||||
|
||||
int T1::operator+(int m)
|
||||
[[ pre: m > 0 ]]
|
||||
[[ pre: v > 0 ]]
|
||||
{
|
||||
return v + m;
|
||||
}
|
||||
|
||||
int T1::operator-(int m)
|
||||
[[ pre: m > 0 ]]
|
||||
[[ pre: v > 0 ]]
|
||||
{
|
||||
return v - m;
|
||||
}
|
||||
|
||||
T1::~T1()
|
||||
[[ pre: v > 0 ]]
|
||||
{
|
||||
}
|
||||
|
||||
struct T2
|
||||
{
|
||||
T2(int m, int n)
|
||||
[[ pre: m < 0 ]]
|
||||
[[ pre: n < 0 ]]
|
||||
: v{-m * n}
|
||||
{
|
||||
}
|
||||
|
||||
int operator+(int m)
|
||||
[[ pre: m > 0 ]]
|
||||
[[ pre: v > 0 ]]
|
||||
{
|
||||
return v + m;
|
||||
}
|
||||
|
||||
int operator-(int m)
|
||||
[[ pre: m > 0 ]]
|
||||
[[ pre: v > 0 ]]
|
||||
{
|
||||
return v - m;
|
||||
}
|
||||
|
||||
~T2()
|
||||
[[ pre: v > 0 ]]
|
||||
{
|
||||
}
|
||||
|
||||
int v{-10};
|
||||
};
|
||||
|
||||
struct TC : T1
|
||||
{
|
||||
TC(int m, int n)
|
||||
[[ pre: m < -1 ]]
|
||||
[[ pre: n < -1 ]]
|
||||
: T1{m, n}
|
||||
{
|
||||
}
|
||||
|
||||
~TC()
|
||||
[[ pre: vc < 0 ]]
|
||||
{
|
||||
}
|
||||
|
||||
TC(int a)
|
||||
[[ pre: a < 0 ]]
|
||||
: TC{a, a}
|
||||
{
|
||||
}
|
||||
|
||||
int vc{10};
|
||||
};
|
||||
|
||||
void test()
|
||||
{
|
||||
T1 t1{10, 20};
|
||||
int a = t1 - -5;
|
||||
int b = t1 + -5;
|
||||
printf("==========\n");
|
||||
|
||||
T2 t2{10, 20};
|
||||
int k = t2 - -5;
|
||||
int j = t2 + -5;
|
||||
printf("==========\n");
|
||||
|
||||
TC tc{10, 20};
|
||||
printf("==========\n");
|
||||
|
||||
TC tc2{10};
|
||||
printf("==========\n");
|
||||
}
|
||||
}
|
||||
|
||||
namespace virt
|
||||
{
|
||||
struct T1
|
||||
{
|
||||
virtual int fun(int m, int n)
|
||||
[[ pre: m > 0 ]]
|
||||
[[ pre: n > 0 ]]
|
||||
[[ pre: v > 0 ]];
|
||||
int v{-10};
|
||||
};
|
||||
|
||||
int T1::fun(int m, int n)
|
||||
[[ pre: m > 0 ]]
|
||||
[[ pre: n > 0 ]]
|
||||
[[ pre: v > 0 ]]
|
||||
{
|
||||
printf("T1::fun::m: %d, T1::fun::n: %d, T1::v: %d\n", m, n, v);
|
||||
return m * n * v;
|
||||
}
|
||||
|
||||
struct T2 : T1
|
||||
{
|
||||
};
|
||||
|
||||
struct T3 : T2
|
||||
{
|
||||
virtual int fun(int m, int n)
|
||||
[[ pre: m > 0 ]]
|
||||
[[ pre: n > 0 ]]
|
||||
[[ pre: v > 0 ]]
|
||||
override
|
||||
{
|
||||
printf("T3::fun::m: %d, T3::fun::n: %d, T3::v: %d\n", m, n, v);
|
||||
return m * n * v;
|
||||
}
|
||||
};
|
||||
|
||||
struct T3b : T2
|
||||
{
|
||||
virtual int fun(int m, int n)
|
||||
[[ pre: m > 0 ]]
|
||||
[[ pre: n > 0 ]]
|
||||
[[ pre: v > 0 ]]
|
||||
override;
|
||||
|
||||
int p(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ pre: v > 0 ]];
|
||||
|
||||
int u(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ pre: z > 0 ]];
|
||||
|
||||
int n(int a)
|
||||
[[ pre: a > 0 ]];
|
||||
|
||||
static int Sn(int a)
|
||||
[[ pre: a > 0 ]];
|
||||
|
||||
int z{-10};
|
||||
};
|
||||
|
||||
int T3b::fun(int m, int n)
|
||||
[[ pre: m > 0 ]]
|
||||
[[ pre: n > 0 ]]
|
||||
[[ pre: v > 0 ]]
|
||||
{
|
||||
printf("T3b::fun::m: %d, T3b::fun::n: %d, T3b::v: %d\n", m, n, v);
|
||||
return m * n * v;
|
||||
}
|
||||
|
||||
int T3b::p(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ pre: v > 0 ]]
|
||||
{
|
||||
printf("T3b::p: a: %d, v: %d\n", a, v);
|
||||
return a * v;
|
||||
}
|
||||
|
||||
int T3b::u(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ pre: z > 0 ]]
|
||||
{
|
||||
printf("T3b::u: a: %d, z: %d\n", a, z);
|
||||
return a * z;
|
||||
}
|
||||
|
||||
int T3b::n(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
printf("T3b::n: a: %d\n", a);
|
||||
return a;
|
||||
}
|
||||
|
||||
int T3b::Sn(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
printf("T3b::Sn: a: %d\n", a);
|
||||
return a;
|
||||
}
|
||||
|
||||
struct T3c : T2
|
||||
{
|
||||
int fun(int m, int n)
|
||||
[[ pre: m > 0 ]]
|
||||
[[ pre: n > 0 ]]
|
||||
[[ pre: v > 0 ]]
|
||||
{
|
||||
printf("T3c::fun::m: %d, T3c::fun::n: %d, T3c::v: %d\n", m, n, v);
|
||||
return m * n * v;
|
||||
}
|
||||
|
||||
int p(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ pre: v > 0 ]]
|
||||
{
|
||||
printf("T3c::p: a: %d, v: %d\n", a, v);
|
||||
return a * v;
|
||||
}
|
||||
|
||||
int u(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ pre: z > 0 ]]
|
||||
{
|
||||
printf("T3c::u: a: %d, z: %d\n", a, z);
|
||||
return a * z;
|
||||
}
|
||||
|
||||
int n(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
printf("T3c::n: a: %d\n", a);
|
||||
return a;
|
||||
}
|
||||
|
||||
static int Sn(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
printf("T3c::Sn: a: %d\n", a);
|
||||
return a;
|
||||
}
|
||||
|
||||
int z{-10};
|
||||
};
|
||||
|
||||
void t(const char *kind, T1 *t)
|
||||
{
|
||||
printf("=================\n%s:\n", kind);
|
||||
t->fun(-1, -2);
|
||||
}
|
||||
|
||||
void test()
|
||||
{
|
||||
T1 t1;
|
||||
t1.fun(-10, -20);
|
||||
|
||||
T2 t2;
|
||||
t2.fun(-10, -20);
|
||||
|
||||
T3 t3;
|
||||
t3.fun(-10, -20);
|
||||
|
||||
T3b t3b;
|
||||
t3b.fun(-10, -20);
|
||||
|
||||
T3c t3c;
|
||||
t3c.fun(-10, -20);
|
||||
|
||||
t("T1", &t1);
|
||||
t("T2", &t2);
|
||||
t("T3", &t3);
|
||||
t("T3b", &t3b);
|
||||
t("T3c", &t3c);
|
||||
|
||||
printf("=============\n");
|
||||
t3b.p(-3);
|
||||
t3b.u(-3);
|
||||
t3b.n(-3);
|
||||
T3b::Sn(-3);
|
||||
|
||||
printf("=============\n");
|
||||
t3c.p(-3);
|
||||
t3c.u(-3);
|
||||
t3c.n(-3);
|
||||
T3c::Sn(-3);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
// ordinary member functions
|
||||
{
|
||||
int x = 11;
|
||||
double y = 11.5;
|
||||
member::T1 t1;
|
||||
t1.vfun(x, y);
|
||||
|
||||
int f = 13;
|
||||
f = t1.fun(x, y);
|
||||
printf("main::f: %d\n", f);
|
||||
double d = 13.37;
|
||||
d = t1.fun2(x, y);
|
||||
printf("main::d: %f\n", d);
|
||||
member::S s = t1.funend(x, y);
|
||||
printf("main::s.z: %d\n", s.z ? 1 : 0);
|
||||
}
|
||||
|
||||
special::test();
|
||||
virt::test();
|
||||
return 0;
|
||||
}
|
||||
|
||||
member::S member::T1::funend(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
{
|
||||
printf("funend::x: %d\n", x);
|
||||
S s;
|
||||
s.z = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 37 member::T1::vfun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 38 member::T1::vfun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 45 member::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::f: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 52 member::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 53 member::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 54 member::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 55 member::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 397 member::T1::funend .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 398 member::T1::funend .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 85 special::T1::T1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 86 special::T1::T1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 99 special::T1::operator- .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 100 special::T1::operator- .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 92 special::T1::operator. .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 93 special::T1::operator. .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "==========(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 113 special::T2::T2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 114 special::T2::T2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 127 special::T2::operator- .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 128 special::T2::operator- .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 120 special::T2::operator. .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 121 special::T2::operator. .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "==========(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 144 special::TC::TC .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 145 special::TC::TC .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 85 special::T1::T1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 86 special::T1::T1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "==========(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 156 special::TC::TC .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 144 special::TC::TC .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 145 special::TC::TC .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 85 special::T1::T1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 86 special::T1::T1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "==========(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 151 special::TC::~TC .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 106 special::T1::~T1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 151 special::TC::~TC .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 106 special::T1::~T1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 134 special::T2::~T2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 106 special::T1::~T1 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 196 virt::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 197 virt::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 198 virt::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T1::fun::m: -10, T1::fun::n: -20, T1::v: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 196 virt::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 197 virt::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 198 virt::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T1::fun::m: -10, T1::fun::n: -20, T1::v: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 211 virt::T3::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 212 virt::T3::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 213 virt::T3::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3::fun::m: -10, T3::fun::n: -20, T3::v: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 247 virt::T3b::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 248 virt::T3b::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 249 virt::T3b::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3b::fun::m: -10, T3b::fun::n: -20, T3b::v: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 288 virt::T3c::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 289 virt::T3c::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 290 virt::T3c::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3c::fun::m: -10, T3c::fun::n: -20, T3c::v: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "=================(\n|\r\n|\r)*" }
|
||||
// { dg-output "T1:(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 196 virt::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 197 virt::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 198 virt::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T1::fun::m: -1, T1::fun::n: -2, T1::v: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "=================(\n|\r\n|\r)*" }
|
||||
// { dg-output "T2:(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 196 virt::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 197 virt::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 198 virt::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T1::fun::m: -1, T1::fun::n: -2, T1::v: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "=================(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3:(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 211 virt::T3::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 212 virt::T3::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 213 virt::T3::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3::fun::m: -1, T3::fun::n: -2, T3::v: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "=================(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3b:(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 247 virt::T3b::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 248 virt::T3b::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 249 virt::T3b::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3b::fun::m: -1, T3b::fun::n: -2, T3b::v: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "=================(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3c:(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 288 virt::T3c::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 289 virt::T3c::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 290 virt::T3c::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3c::fun::m: -1, T3c::fun::n: -2, T3c::v: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "=============(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 256 virt::T3b::p .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 257 virt::T3b::p .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3b::p: a: -3, v: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 264 virt::T3b::u .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 265 virt::T3b::u .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3b::u: a: -3, z: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 272 virt::T3b::n .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3b::n: a: -3(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 279 virt::T3b::Sn .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3b::Sn: a: -3(\n|\r\n|\r)*" }
|
||||
// { dg-output "=============(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 297 virt::T3c::p .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 298 virt::T3c::p .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3c::p: a: -3, v: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 305 virt::T3c::u .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 306 virt::T3c::u .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3c::u: a: -3, z: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 313 virt::T3c::n .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3c::n: a: -3(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 320 virt::T3c::Sn .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "T3c::Sn: a: -3(\n|\r\n|\r)*" }
|
||||
|
||||
92
gcc/testsuite/g++.dg/contracts/contracts-pre4.C
Normal file
92
gcc/testsuite/g++.dg/contracts/contracts-pre4.C
Normal file
@@ -0,0 +1,92 @@
|
||||
// test that contracts on overriding functions are found correctly
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual int f(int a) [[ pre: a > 0 ]];
|
||||
};
|
||||
|
||||
int Base::f(int a)
|
||||
{
|
||||
return a + 10;
|
||||
}
|
||||
|
||||
// inherits original
|
||||
struct Child0 : Base
|
||||
{
|
||||
};
|
||||
|
||||
// defined out of line, explicit override
|
||||
struct Child1 : Base
|
||||
{
|
||||
virtual int f(int a) override;
|
||||
};
|
||||
|
||||
int Child1::f(int a)
|
||||
{
|
||||
return a + 20;
|
||||
}
|
||||
|
||||
// defined out of line
|
||||
struct Child2 : Base
|
||||
{
|
||||
int f(int a);
|
||||
};
|
||||
|
||||
int Child2::f(int a)
|
||||
{
|
||||
return a + 30;
|
||||
}
|
||||
|
||||
// defined inline, explicitly override
|
||||
struct Child3 : Base
|
||||
{
|
||||
virtual int f(int a) override
|
||||
{
|
||||
return a + 40;
|
||||
}
|
||||
};
|
||||
|
||||
// defined inline
|
||||
struct Child4 : Base
|
||||
{
|
||||
int f(int a)
|
||||
{
|
||||
return a + 50;
|
||||
}
|
||||
};
|
||||
|
||||
#include <cstdio>
|
||||
int main(int, char**)
|
||||
{
|
||||
Base b;
|
||||
Child0 c0;
|
||||
Child1 c1;
|
||||
Child2 c2;
|
||||
Child3 c3;
|
||||
Child4 c4;
|
||||
|
||||
printf("Base: %d\n", b.f(-10));
|
||||
printf("Child0: %d\n", c0.f(-10));
|
||||
printf("Child1: %d\n", c1.f(-10));
|
||||
printf("Child2: %d\n", c2.f(-10));
|
||||
printf("Child3: %d\n", c3.f(-10));
|
||||
printf("Child4: %d\n", c4.f(-10));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 7 Base::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "Base: 0(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 7 Base::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "Child0: 0(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 7 Child1::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "Child1: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 7 Child2::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "Child2: 20(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 7 Child3::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "Child3: 30(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 7 Child4::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "Child4: 40(\n|\r\n|\r)*" }
|
||||
|
||||
81
gcc/testsuite/g++.dg/contracts/contracts-pre5.C
Normal file
81
gcc/testsuite/g++.dg/contracts/contracts-pre5.C
Normal file
@@ -0,0 +1,81 @@
|
||||
// basic test to ensure pre contracts work for free templates
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
#include <cstdio>
|
||||
|
||||
template<typename T>
|
||||
int body(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
T t = a * 2.5;
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int none(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int arg0(T t)
|
||||
[[ pre: t > 0 ]]
|
||||
{
|
||||
return -t - 10;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int arg1(int a, T t)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ pre: t > 0 ]]
|
||||
{
|
||||
return -t * a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ret(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
printf("%d\n", body<int>(-1));
|
||||
printf("%d\n", body<double>(-2));
|
||||
printf("%d\n", none<int>(-1));
|
||||
printf("%d\n", none<double>(-2));
|
||||
printf("%d\n", arg0(-1));
|
||||
printf("%d\n", arg0(-2.9));
|
||||
printf("%d\n", arg1(-3, -1));
|
||||
printf("%d\n", arg1(-4, -2.9));
|
||||
printf("%d\n", (int)ret<int>(-3));
|
||||
printf("%d\n", (int)ret<double>(-4.9));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 8 body<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-2(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 8 body<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-5(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 16 none<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "1(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 16 none<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "2(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 23 arg0<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-9(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 23 arg0<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-7(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 30 arg1<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 31 arg1<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-3(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 30 arg1<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 31 arg1<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-11(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 38 ret<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "3(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 38 ret<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "4(\n|\r\n|\r)*" }
|
||||
|
||||
74
gcc/testsuite/g++.dg/contracts/contracts-pre6.C
Normal file
74
gcc/testsuite/g++.dg/contracts/contracts-pre6.C
Normal file
@@ -0,0 +1,74 @@
|
||||
// ensure no errors are thrown when we have to insert a decl for the internal
|
||||
// unchecked function after leaving a (possibly nested) namespace
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
namespace ns0
|
||||
{
|
||||
int f(int a) [[ pre: a > 0 ]];
|
||||
}
|
||||
|
||||
int ns0::f(int a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
namespace ns0
|
||||
{
|
||||
namespace ns1
|
||||
{
|
||||
int f(int a) [[ pre: a > 0 ]];
|
||||
}
|
||||
}
|
||||
|
||||
int ns0::ns1::f(int a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
namespace ns0
|
||||
{
|
||||
namespace ns1
|
||||
{
|
||||
int f2(int a) [[ pre: a > 0 ]];
|
||||
namespace ns2
|
||||
{
|
||||
int f(int a) [[ pre: a > 0 ]];
|
||||
}
|
||||
}
|
||||
int ns1::f2(int a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
}
|
||||
|
||||
int ns0::ns1::ns2::f(int a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
namespace ns0
|
||||
{
|
||||
struct S
|
||||
{
|
||||
int f(int a) [[ pre: a > 0 ]];
|
||||
};
|
||||
namespace ns1
|
||||
{
|
||||
struct S2
|
||||
{
|
||||
int f(int a) [[ pre: a > 0 ]];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
int ns0::S::f(int a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
int ns0::ns1::S2::f(int a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
134
gcc/testsuite/g++.dg/contracts/contracts-pre7.C
Normal file
134
gcc/testsuite/g++.dg/contracts/contracts-pre7.C
Normal file
@@ -0,0 +1,134 @@
|
||||
// ensure no errors are thrown when we have to insert a decl for the internal
|
||||
// unchecked function after leaving a (possibly nested) namespace
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
|
||||
namespace ns0
|
||||
{
|
||||
template<typename T>
|
||||
int f(T a) [[ pre: a > 0 ]];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int ns0::f(T a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return (int)-a;
|
||||
}
|
||||
|
||||
namespace ns0
|
||||
{
|
||||
namespace ns1
|
||||
{
|
||||
template<typename T>
|
||||
int f(T a) [[ pre: a > 0 ]];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int ns0::ns1::f(T a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
namespace ns0
|
||||
{
|
||||
namespace ns1
|
||||
{
|
||||
template<typename T>
|
||||
int f2(T a) [[ pre: a > 0 ]];
|
||||
namespace ns2
|
||||
{
|
||||
template<typename T>
|
||||
int f(T a) [[ pre: a > 0 ]];
|
||||
}
|
||||
}
|
||||
template<typename T>
|
||||
int ns1::f2(T a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int ns0::ns1::ns2::f(T a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
namespace ns0
|
||||
{
|
||||
template<typename T>
|
||||
struct S
|
||||
{
|
||||
int f(T a) [[ pre: a > 0 ]];
|
||||
};
|
||||
namespace ns1
|
||||
{
|
||||
template<typename T>
|
||||
struct S2
|
||||
{
|
||||
int f(T a) [[ pre: a > 0 ]];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int ns0::S<T>::f(T a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int ns0::ns1::S2<T>::f(T a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
#include <cstdio>
|
||||
int main(int, char**)
|
||||
{
|
||||
printf ("%d\n", ns0::f(-1));
|
||||
printf ("%d\n", ns0::ns1::f(-2));
|
||||
printf ("%d\n", ns0::ns1::f2(-3));
|
||||
printf ("%d\n", ns0::ns1::ns2::f(-4));
|
||||
ns0::S<int> ns0_s;
|
||||
printf ("%d\n", ns0_s.f(-5));
|
||||
ns0::ns1::S2<int> ns0_ns1_s2;
|
||||
printf ("%d\n", ns0_ns1_s2.f(-6));
|
||||
|
||||
printf ("%d\n", ns0::f(-7.5));
|
||||
printf ("%d\n", ns0::ns1::f(-8.5));
|
||||
printf ("%d\n", ns0::ns1::f2(-9.5));
|
||||
printf ("%d\n", ns0::ns1::ns2::f(-10.5));
|
||||
ns0::S<double> ns0_sd;
|
||||
printf ("%d\n", ns0_sd.f(-11.5));
|
||||
ns0::ns1::S2<double> ns0_ns1_s2d;
|
||||
printf ("%d\n", ns0_ns1_s2d.f(-12.5));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 13 ns0::f<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "1(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 28 ns0::ns1::f<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "2(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 46 ns0::ns1::f2<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "3(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 53 ns0::ns1::ns2::f<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "4(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 76 ns0::S<int>::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "5(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 82 ns0::ns1::S2<int>::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "6(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 13 ns0::f<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "7(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 28 ns0::ns1::f<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "8(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 46 ns0::ns1::f2<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "9(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 53 ns0::ns1::ns2::f<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 76 ns0::S<double>::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "11(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 82 ns0::ns1::S2<double>::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "12(\n|\r\n|\r)*" }
|
||||
146
gcc/testsuite/g++.dg/contracts/contracts-pre9.C
Normal file
146
gcc/testsuite/g++.dg/contracts/contracts-pre9.C
Normal file
@@ -0,0 +1,146 @@
|
||||
// ensure no errors are thrown for various combinations of class templates
|
||||
// with guarded members
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
|
||||
template<typename T>
|
||||
struct S
|
||||
{
|
||||
int f(int a) [[ pre: a > 0 ]];
|
||||
int g(int a) [[ pre: a > 0 ]];
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
int S<T>::f(int a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int S<T>::g(int a) // Contract is inherited (error from line 10).
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct S_arg
|
||||
{
|
||||
int f(T a) [[ pre: a > 0 ]];
|
||||
int g(T a) [[ pre: a > 0 ]];
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
int S_arg<T>::f(T a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int S_arg<T>::g(T a) // Contract is inherited (error from line 29).
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct S_ret
|
||||
{
|
||||
T f(int a) [[ pre: a > 0 ]];
|
||||
T g(int a) [[ pre: a > 0 ]];
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T S_ret<T>::f(int a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T S_ret<T>::g(int a) // Contract is inherited (error from line 48).
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
#include <cstdio>
|
||||
int main(int, char**)
|
||||
{
|
||||
{
|
||||
S<int> s_int;
|
||||
printf ("s_int.f(-10): %d\n", s_int.f(-10));
|
||||
printf ("s_int.g(-10): %d\n", s_int.g(-10));
|
||||
printf ("s_int.f(10): %d\n", s_int.f(10));
|
||||
printf ("s_int.g(10): %d\n", s_int.g(10));
|
||||
|
||||
S<double> s_double;
|
||||
printf ("s_double.f(-10.5): %d\n", s_double.f(-10.5));
|
||||
printf ("s_double.g(-10.5): %d\n", s_double.g(-10.5));
|
||||
printf ("s_double.f(10.5): %d\n", s_double.f(10.5));
|
||||
printf ("s_double.g(10.5): %d\n", s_double.g(10.5));
|
||||
}
|
||||
|
||||
{
|
||||
S_arg<int> s_arg_int;
|
||||
printf ("s_arg_int.f(-10): %d\n", s_arg_int.f(-10));
|
||||
printf ("s_arg_int.g(-10): %d\n", s_arg_int.g(-10));
|
||||
printf ("s_arg_int.f(10): %d\n", s_arg_int.f(10));
|
||||
printf ("s_arg_int.g(10): %d\n", s_arg_int.g(10));
|
||||
|
||||
S_arg<double> s_arg_double;
|
||||
printf ("s_arg_double.f(-10): %d\n", s_arg_double.f(-10));
|
||||
printf ("s_arg_double.g(-10): %d\n", s_arg_double.g(-10));
|
||||
printf ("s_arg_double.f(10): %d\n", s_arg_double.f(10));
|
||||
printf ("s_arg_double.g(10): %d\n", s_arg_double.g(10));
|
||||
}
|
||||
|
||||
{
|
||||
S_ret<int> s_ret_int;
|
||||
printf ("s_ret_int.f(-10): %d\n", s_ret_int.f(-10));
|
||||
printf ("s_ret_int.g(-10): %d\n", s_ret_int.g(-10));
|
||||
printf ("s_ret_int.f(10): %d\n", s_ret_int.f(10));
|
||||
printf ("s_ret_int.g(10): %d\n", s_ret_int.g(10));
|
||||
|
||||
S_ret<double> s_ret_double;
|
||||
printf ("s_ret_double.f(-10): %f\n", s_ret_double.f(-10));
|
||||
printf ("s_ret_double.g(-10): %f\n", s_ret_double.g(-10));
|
||||
printf ("s_ret_double.f(10): %f\n", s_ret_double.f(10));
|
||||
printf ("s_ret_double.g(10): %f\n", s_ret_double.g(10));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 14 S<int>::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_int.f.-10.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 S<int>::g .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_int.g.-10.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_int.f.10.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_int.g.10.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 14 S<double>::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_double.f.-10.5.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 S<double>::g .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_double.g.-10.5.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_double.f.10.5.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_double.g.10.5.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 33 S_arg<int>::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_arg_int.f.-10.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 29 S_arg<int>::g .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_arg_int.g.-10.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_arg_int.f.10.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_arg_int.g.10.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 33 S_arg<double>::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_arg_double.f.-10.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 29 S_arg<double>::g .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_arg_double.g.-10.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_arg_double.f.10.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_arg_double.g.10.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 52 S_ret<int>::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_ret_int.f.-10.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 48 S_ret<int>::g .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_ret_int.g.-10.: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_ret_int.f.10.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_ret_int.g.10.: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 52 S_ret<double>::f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_ret_double.f.-10.: 10.000000(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 48 S_ret<double>::g .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_ret_double.g.-10.: 10.000000(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_ret_double.f.10.: -10.000000(\n|\r\n|\r)*" }
|
||||
// { dg-output "s_ret_double.g.10.: -10.000000(\n|\r\n|\r)*" }
|
||||
149
gcc/testsuite/g++.dg/contracts/contracts-redecl1.C
Normal file
149
gcc/testsuite/g++.dg/contracts/contracts-redecl1.C
Normal file
@@ -0,0 +1,149 @@
|
||||
// generic error tests for contract redecls with generalized redecl
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
// OK if equivalent -- even through renames.
|
||||
int g0(int a) [[ pre: a > 0 ]];
|
||||
int g0(int a) [[ pre: a > 0 ]];
|
||||
|
||||
int g0b(int a) [[ pre: a > 0 ]];
|
||||
int g0b(int b) [[ pre: b > 0 ]];
|
||||
int g0b(int c) [[ pre: c > 0 ]]
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// OK if specified before.
|
||||
int g1(int a) [[ pre: a > 0 ]];
|
||||
int g1(int a);
|
||||
|
||||
// OK if specified after.
|
||||
int g2(int a);
|
||||
int g2(int a) [[ pre: a > 0 ]];
|
||||
|
||||
int g2b(int a);
|
||||
int g2b(int b) [[ pre: b > 0 ]];
|
||||
|
||||
// can add to non-virtual methods
|
||||
struct G0
|
||||
{
|
||||
int f(int a);
|
||||
};
|
||||
|
||||
// OK to add contracts at the point of definition.
|
||||
int G0::f(int a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
struct G1
|
||||
{
|
||||
int f1(int a);
|
||||
};
|
||||
|
||||
// OK to redeclare functions and add constraints...
|
||||
int G1::f1(int a) [[ pre: a > 0 ]];
|
||||
|
||||
// ...and leave them off later.
|
||||
int G1::f1(int a)
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
int f0(int a) [[ pre: a > 0 ]];
|
||||
int f0(int a) [[ pre: a > 0 ]] [[ pre: a > 10 ]]; // { dg-error "different number of contracts" }
|
||||
|
||||
int f1(int a) [[ pre: a > 0 ]] [[ pre: a > 10 ]];
|
||||
int f1(int a) [[ pre: a > 0 ]]; // { dg-error "different number of contracts" }
|
||||
|
||||
int f2(int a) [[ pre: a > 0 ]];
|
||||
int f2(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
|
||||
|
||||
int f3(int a) { return a; }
|
||||
int f3(int a) [[ pre: a < 0 ]]; // { dg-error "cannot add contracts" }
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual int f(int a) [[ pre: a > 0 ]];
|
||||
};
|
||||
|
||||
struct Child : Base
|
||||
{
|
||||
int f(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
|
||||
};
|
||||
|
||||
struct S1
|
||||
{
|
||||
virtual int f(int a); // contracts are inherited at the point of declarations
|
||||
};
|
||||
|
||||
int S1::f(int a) [[ pre: a > 0 ]] // { dg-error "cannot add" }
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
struct S2
|
||||
{
|
||||
int f() { return 0; }
|
||||
};
|
||||
|
||||
int S2::f(); // OK?
|
||||
|
||||
|
||||
struct S3
|
||||
{
|
||||
int f() { return 0; }
|
||||
};
|
||||
|
||||
int S3::f() [[pre: true]]; // { dg-error "cannot add contracts" }
|
||||
|
||||
|
||||
// The initial decl of a guarded member must appear inside the class.
|
||||
struct S4
|
||||
{
|
||||
int f(int a);
|
||||
};
|
||||
|
||||
int S4::g(int a) [[ pre: a > 0 ]]; // { dg-error "no declaration matches" }
|
||||
|
||||
|
||||
struct S5
|
||||
{
|
||||
template<typename T>
|
||||
S5(T a);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
S5::S5(T a) [[ pre: a > 0 ]]
|
||||
{
|
||||
}
|
||||
|
||||
struct S6
|
||||
{
|
||||
template<typename T>
|
||||
S6(T a);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
S6::S6(T a) [[ pre: a > 0 ]];
|
||||
|
||||
template<typename T>
|
||||
S6::S6(T a)
|
||||
{
|
||||
}
|
||||
|
||||
int p0(int n)
|
||||
[[ post r: r > 0 && r == n ]]
|
||||
[[ post r: r > 1 && r == n ]]
|
||||
[[ post r: r > 2 && r == n ]]
|
||||
[[ post r: r > 3 && r == n ]];
|
||||
|
||||
int p0(int z)
|
||||
[[ post r: r > 0 && r == z ]]
|
||||
[[ post r1: r1 > 1 && r1 == z ]]
|
||||
[[ post r2: r2 > 2 && r2 == z ]]
|
||||
[[ post r3: r3 > 3 && r3 == z ]]
|
||||
{
|
||||
return z;
|
||||
}
|
||||
|
||||
149
gcc/testsuite/g++.dg/contracts/contracts-redecl2.C
Normal file
149
gcc/testsuite/g++.dg/contracts/contracts-redecl2.C
Normal file
@@ -0,0 +1,149 @@
|
||||
// generic error tests for generalized contract redecls
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
// allowed to repeat contracts or omit them
|
||||
int g0(int a) [[ pre: a > 0 ]];
|
||||
int g0(int a) [[ pre: a > 0 ]];
|
||||
|
||||
int g1(int a) [[ pre: a > 0 ]];
|
||||
int g1(int a);
|
||||
|
||||
// allowed to add from none if generalized redecl is on (by default)
|
||||
int g2(int a);
|
||||
int g2(int a) [[ pre: a > 0 ]];
|
||||
|
||||
// can add to non-virtual methods
|
||||
struct G0
|
||||
{
|
||||
int f(int a);
|
||||
};
|
||||
|
||||
int G0::f(int a) [[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
struct G1
|
||||
{
|
||||
int f(int a);
|
||||
};
|
||||
|
||||
int G1::f(int a) [[ pre: a > 0 ]];
|
||||
|
||||
int G1::f(int a)
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
// allowed to redeclare even without contracts
|
||||
struct G2
|
||||
{
|
||||
int f(int a);
|
||||
};
|
||||
|
||||
int G2::f(int a);
|
||||
|
||||
|
||||
int f0(int a) [[ pre: a > 0 ]];
|
||||
int f0(int a) [[ pre: a > 0 ]] [[ pre: a > 10 ]]; // { dg-error "different number of contracts" }
|
||||
|
||||
int f1(int a) [[ pre: a > 0 ]];
|
||||
int f1(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
|
||||
|
||||
int f2(int a) { return a; }
|
||||
int f2(int a) [[ pre: a < 0 ]]; // { dg-error "cannot add contracts after definition" }
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual int f(int a) [[ pre: a > 0 ]];
|
||||
};
|
||||
|
||||
struct Child : Base
|
||||
{
|
||||
int f(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
|
||||
};
|
||||
|
||||
// the initial decl of a guarded member must appear inside the class
|
||||
struct F2
|
||||
{
|
||||
int f(int a);
|
||||
};
|
||||
|
||||
int F2::g(int a) [[ pre: a > 0 ]]; // { dg-error "no declaration matches" }
|
||||
// FIXME if we move F2 down then a different error makes F2 undeclared
|
||||
|
||||
struct F0
|
||||
{
|
||||
virtual int f(int a);
|
||||
};
|
||||
|
||||
int F0::f(int a); // { dg-error "declaration.*is not definition" }
|
||||
|
||||
struct F1
|
||||
{
|
||||
virtual int f(int a);
|
||||
};
|
||||
|
||||
int F1::f(int a) [[ pre: a > 0 ]] // { dg-error "cannot add" }
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
// cannot "re"declare members of a forward declared class
|
||||
struct F2;
|
||||
int F2::test(); // { dg-error "no declaration matches" }
|
||||
int F2::test2() [[ pre: true ]]; // { dg-error "no declaration matches" }
|
||||
|
||||
// can only redeclare member functions
|
||||
struct F3
|
||||
{
|
||||
int x;
|
||||
typedef int my_int;
|
||||
|
||||
struct Inner0;
|
||||
struct Inner1;
|
||||
enum my_enum0; // { dg-error "use of enum.*without previous decl" }
|
||||
enum my_enum1 { E1, E2 };
|
||||
|
||||
int test0();
|
||||
int test1();
|
||||
int test2();
|
||||
};
|
||||
|
||||
int F3::x{-1}; // { dg-error "is not a static data member" }
|
||||
typedef double F3::my_int; // { dg-error "typedef name may not be a nested-name-specifier" }
|
||||
struct F3::Inner0; // { dg-warning "declaration.*does not declare anything" }
|
||||
|
||||
struct F3::Inner1 { };
|
||||
|
||||
enum F3::my_enum1 { E0, E1, END }; // { dg-error "multiple definition" }
|
||||
|
||||
struct F4
|
||||
{
|
||||
int test0();
|
||||
|
||||
int F3::test0() [[ pre: true ]]; // { dg-error "cannot declare member function" }
|
||||
friend int F3::test1();
|
||||
friend int F3::test2();
|
||||
};
|
||||
int F3::test2() [[ pre: true ]] { return -1; }
|
||||
|
||||
void dummy0()
|
||||
{
|
||||
int F4::test0() [[ pre: true ]]; // { dg-error "qualified-id in declaration" }
|
||||
}
|
||||
|
||||
namespace ns0
|
||||
{
|
||||
typedef int value;
|
||||
struct X
|
||||
{
|
||||
int test1(value);
|
||||
typedef double value;
|
||||
int test2(value);
|
||||
};
|
||||
int X::test1(value); // { dg-error "no declaration matches" }
|
||||
int X::test2(value);
|
||||
}
|
||||
|
||||
195
gcc/testsuite/g++.dg/contracts/contracts-redecl3.C
Normal file
195
gcc/testsuite/g++.dg/contracts/contracts-redecl3.C
Normal file
@@ -0,0 +1,195 @@
|
||||
// basic test to ensure contracts generalized redecl works
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
#include <cstdio>
|
||||
|
||||
namespace defining
|
||||
{
|
||||
int x = 10;
|
||||
double y = 10.5;
|
||||
|
||||
struct S
|
||||
{
|
||||
bool z;
|
||||
};
|
||||
|
||||
struct T1
|
||||
{
|
||||
void vfun(int m, double n);
|
||||
int fun(int m, double n);
|
||||
double fun2(int m, double n);
|
||||
S funend(int m, double n);
|
||||
};
|
||||
|
||||
void T1::vfun(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
{
|
||||
printf("vfun::x: %d\n", x);
|
||||
}
|
||||
|
||||
int T1::fun(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
{
|
||||
printf("fun::x: %d\n", x);
|
||||
return x;
|
||||
}
|
||||
|
||||
double T1::fun2(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: y < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
[[ pre: n < 0 ]]
|
||||
{
|
||||
printf("fun2::x: %d fun2::y: %f\n", x, y);
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
namespace nondefining
|
||||
{
|
||||
int x = 10;
|
||||
double y = 10.5;
|
||||
|
||||
struct S
|
||||
{
|
||||
bool z;
|
||||
};
|
||||
|
||||
struct T1
|
||||
{
|
||||
void vfun(int m, double n);
|
||||
int fun(int m, double n);
|
||||
double fun2(int m, double n);
|
||||
S funend(int m, double n);
|
||||
};
|
||||
|
||||
void T1::vfun(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]];
|
||||
int T1::fun(int m, double n)
|
||||
[[ pre: x < 0 ]];
|
||||
double T1::fun2(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: y < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
[[ pre: n < 0 ]];
|
||||
|
||||
void T1::vfun(int m, double n)
|
||||
{
|
||||
printf("vfun::x: %d\n", x);
|
||||
}
|
||||
|
||||
int T1::fun(int m, double n)
|
||||
{
|
||||
printf("fun::x: %d\n", x);
|
||||
return x;
|
||||
}
|
||||
|
||||
double T1::fun2(int m, double n)
|
||||
{
|
||||
printf("fun2::x: %d fun2::y: %f\n", x, y);
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
// defining redecl
|
||||
{
|
||||
int x = 11;
|
||||
double y = 11.5;
|
||||
|
||||
defining::T1 t1;
|
||||
t1.vfun(x, y);
|
||||
|
||||
int f = 13;
|
||||
f = t1.fun(x, y);
|
||||
printf("main::f: %d\n", f);
|
||||
double d = 13.37;
|
||||
d = t1.fun2(x, y);
|
||||
printf("main::d: %f\n", d);
|
||||
defining::S s = t1.funend(x, y);
|
||||
printf("main::s.z: %d\n", s.z ? 1 : 0);
|
||||
}
|
||||
|
||||
// nondefining redecl
|
||||
{
|
||||
int x = 12;
|
||||
double y = 12.5;
|
||||
|
||||
nondefining::T1 t1;
|
||||
t1.vfun(x, y);
|
||||
|
||||
int f = 13;
|
||||
f = t1.fun(x, y);
|
||||
printf("main::f: %d\n", f);
|
||||
double d = 13.37;
|
||||
d = t1.fun2(x, y);
|
||||
printf("main::d: %f\n", d);
|
||||
nondefining::S s = t1.funend(x, y);
|
||||
printf("main::s.z: %d\n", s.z ? 1 : 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace defining
|
||||
{
|
||||
S T1::funend(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
{
|
||||
printf("funend::x: %d\n", x);
|
||||
S s;
|
||||
s.z = true;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
namespace nondefining
|
||||
{
|
||||
S T1::funend(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]];
|
||||
|
||||
S T1::funend(int m, double n)
|
||||
{
|
||||
printf("funend::x: %d\n", x);
|
||||
S s;
|
||||
s.z = true;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 25 defining::T1::vfun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 26 defining::T1::vfun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 32 defining::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::f: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 39 defining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 40 defining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 41 defining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 42 defining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 138 defining::T1::funend .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 139 defining::T1::funend .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 68 nondefining::T1::vfun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 69 nondefining::T1::vfun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 71 nondefining::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::f: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 73 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 74 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 75 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 76 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 151 nondefining::T1::funend .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 152 nondefining::T1::funend .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
|
||||
|
||||
56
gcc/testsuite/g++.dg/contracts/contracts-redecl4.C
Normal file
56
gcc/testsuite/g++.dg/contracts/contracts-redecl4.C
Normal file
@@ -0,0 +1,56 @@
|
||||
// test that free functions can be redeclared with contracts without affecting
|
||||
// normal default parm handling
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
#include <cstdio>
|
||||
|
||||
int f(int a, int, int c = 10);
|
||||
int f(int a, int b = 11, int);
|
||||
int f(int a, int b, int c)
|
||||
[[ pre: a < 0 ]]
|
||||
[[ pre: b < 0 ]]
|
||||
[[ pre: c < 0 ]];
|
||||
|
||||
int f(int, int, int);
|
||||
|
||||
int f(int a, int b, int c)
|
||||
{
|
||||
printf("f: a: %d, b: %d, c: %d\n", a, b, c);
|
||||
return a * b - c;
|
||||
}
|
||||
|
||||
int f(int a = 12, int, int);
|
||||
|
||||
int main(int, char **)
|
||||
{
|
||||
f(1,1,1);
|
||||
printf("=====\n");
|
||||
f(1,1);
|
||||
printf("=====\n");
|
||||
f(1);
|
||||
printf("=====\n");
|
||||
f();
|
||||
printf("=====\n");
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 11 f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 12 f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "f: a: 1, b: 1, c: 1(\n|\r\n|\r)*" }
|
||||
// { dg-output "=====(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 11 f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 12 f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "f: a: 1, b: 1, c: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "=====(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 11 f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 12 f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "f: a: 1, b: 11, c: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "=====(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 11 f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 12 f .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "f: a: 12, b: 11, c: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "=====(\n|\r\n|\r)*" }
|
||||
|
||||
101
gcc/testsuite/g++.dg/contracts/contracts-redecl5.C
Normal file
101
gcc/testsuite/g++.dg/contracts/contracts-redecl5.C
Normal file
@@ -0,0 +1,101 @@
|
||||
// generic error tests for generalized contract redecls
|
||||
// we also test for the warning diagnostic for strict redecl
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-strict-declarations=on" }
|
||||
|
||||
// allowed to repeat contracts or omit them
|
||||
int g0(int a) [[ pre: a > 0 ]];
|
||||
int g0(int a) [[ pre: a > 0 ]];
|
||||
|
||||
int g1(int a) [[ pre: a > 0 ]];
|
||||
int g1(int a);
|
||||
|
||||
// allowed to add from none if generalized redecl is on (by default)
|
||||
int g2(int a);
|
||||
int g2(int a) [[ pre: a > 0 ]]; // { dg-warning "adds contracts" }
|
||||
int g2(int a) [[ pre: a > 0 ]]; // { dg-bogus "adds contracts" }
|
||||
|
||||
// can add to non-virtual methods
|
||||
struct G0
|
||||
{
|
||||
int f(int a);
|
||||
};
|
||||
|
||||
int G0::f(int a) [[ pre: a > 0 ]] // { dg-warning "adds contracts" }
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
struct G1
|
||||
{
|
||||
int f(int a);
|
||||
};
|
||||
|
||||
int G1::f(int a) [[ pre: a > 0 ]]; // { dg-warning "adds contracts" }
|
||||
// { dg-warning "outside of class is not definition" "" { target *-*-* } .-1 }
|
||||
|
||||
int G1::f(int a);
|
||||
// { dg-warning "outside of class is not definition" "" { target *-*-* } .-1 }
|
||||
|
||||
int G1::f(int a) [[ pre: a > 0 ]];
|
||||
// { dg-warning "outside of class is not definition" "" { target *-*-* } .-1 }
|
||||
|
||||
int G1::f(int a)
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
// allowed to redeclare even without contracts
|
||||
struct G2
|
||||
{
|
||||
int f(int a);
|
||||
};
|
||||
|
||||
int G2::f(int a); // { dg-warning "outside of class is not definition" }
|
||||
|
||||
|
||||
int f0(int a) [[ pre: a > 0 ]];
|
||||
int f0(int a) [[ pre: a > 0 ]] [[ pre: a > 10 ]]; // { dg-error "different number of contracts" }
|
||||
|
||||
int f1(int a) [[ pre: a > 0 ]];
|
||||
int f1(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
|
||||
|
||||
int f2(int a) { return a; }
|
||||
int f2(int a) [[ pre: a < 0 ]]; // { dg-error "cannot add contracts after definition" }
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual int f(int a) [[ pre: a > 0 ]];
|
||||
};
|
||||
|
||||
struct Child : Base
|
||||
{
|
||||
int f(int a) [[ pre: a < 0 ]]; // { dg-error "mismatched contract" }
|
||||
};
|
||||
|
||||
// the initial decl of a guarded member must appear inside the class
|
||||
struct F2
|
||||
{
|
||||
int f(int a);
|
||||
};
|
||||
|
||||
int F2::g(int a) [[ pre: a > 0 ]]; // { dg-error "no declaration matches" }
|
||||
// FIXME if we move F2 down then a different error makes F2 undeclared
|
||||
|
||||
struct F0
|
||||
{
|
||||
virtual int f(int a);
|
||||
};
|
||||
|
||||
int F0::f(int a); // { dg-error "declaration.*is not definition" }
|
||||
|
||||
struct F1
|
||||
{
|
||||
virtual int f(int a);
|
||||
};
|
||||
|
||||
int F1::f(int a) [[ pre: a > 0 ]] // { dg-error "cannot add" }
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
195
gcc/testsuite/g++.dg/contracts/contracts-redecl6.C
Normal file
195
gcc/testsuite/g++.dg/contracts/contracts-redecl6.C
Normal file
@@ -0,0 +1,195 @@
|
||||
// basic test to ensure contracts generalized redecl works
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
#include <cstdio>
|
||||
|
||||
namespace defining
|
||||
{
|
||||
int x = 10;
|
||||
double y = 10.5;
|
||||
|
||||
struct S
|
||||
{
|
||||
bool z;
|
||||
};
|
||||
|
||||
struct T1
|
||||
{
|
||||
void vfun(int m, double n) const;
|
||||
int fun(int m, double n) volatile;
|
||||
double fun2(int m, double n) const volatile;
|
||||
static S funend(int m, double n);
|
||||
};
|
||||
|
||||
void T1::vfun(int m, double n) const
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
{
|
||||
printf("vfun::x: %d\n", x);
|
||||
}
|
||||
|
||||
int T1::fun(int m, double n) volatile
|
||||
[[ pre: x < 0 ]]
|
||||
{
|
||||
printf("fun::x: %d\n", x);
|
||||
return x;
|
||||
}
|
||||
|
||||
double T1::fun2(int m, double n) const volatile
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: y < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
[[ pre: n < 0 ]]
|
||||
{
|
||||
printf("fun2::x: %d fun2::y: %f\n", x, y);
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
namespace nondefining
|
||||
{
|
||||
int x = 10;
|
||||
double y = 10.5;
|
||||
|
||||
struct S
|
||||
{
|
||||
bool z;
|
||||
};
|
||||
|
||||
struct T1
|
||||
{
|
||||
void vfun(int m, double n) const;
|
||||
int fun(int m, double n) volatile;
|
||||
double fun2(int m, double n) const volatile;
|
||||
static S funend(int m, double n);
|
||||
};
|
||||
|
||||
void T1::vfun(int m, double n) const
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]];
|
||||
int T1::fun(int m, double n) volatile
|
||||
[[ pre: x < 0 ]];
|
||||
double T1::fun2(int m, double n) const volatile
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: y < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
[[ pre: n < 0 ]];
|
||||
|
||||
void T1::vfun(int m, double n) const
|
||||
{
|
||||
printf("vfun::x: %d\n", x);
|
||||
}
|
||||
|
||||
int T1::fun(int m, double n) volatile
|
||||
{
|
||||
printf("fun::x: %d\n", x);
|
||||
return x;
|
||||
}
|
||||
|
||||
double T1::fun2(int m, double n) const volatile
|
||||
{
|
||||
printf("fun2::x: %d fun2::y: %f\n", x, y);
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
// defining redecl
|
||||
{
|
||||
int x = 11;
|
||||
double y = 11.5;
|
||||
|
||||
defining::T1 t1;
|
||||
t1.vfun(x, y);
|
||||
|
||||
int f = 13;
|
||||
f = t1.fun(x, y);
|
||||
printf("main::f: %d\n", f);
|
||||
double d = 13.37;
|
||||
d = t1.fun2(x, y);
|
||||
printf("main::d: %f\n", d);
|
||||
defining::S s = defining::T1::funend(x, y);
|
||||
printf("main::s.z: %d\n", s.z ? 1 : 0);
|
||||
}
|
||||
|
||||
// nondefining redecl
|
||||
{
|
||||
int x = 12;
|
||||
double y = 12.5;
|
||||
|
||||
nondefining::T1 t1;
|
||||
t1.vfun(x, y);
|
||||
|
||||
int f = 13;
|
||||
f = t1.fun(x, y);
|
||||
printf("main::f: %d\n", f);
|
||||
double d = 13.37;
|
||||
d = t1.fun2(x, y);
|
||||
printf("main::d: %f\n", d);
|
||||
nondefining::S s = nondefining::T1::funend(x, y);
|
||||
printf("main::s.z: %d\n", s.z ? 1 : 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace defining
|
||||
{
|
||||
S T1::funend(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]]
|
||||
{
|
||||
printf("funend::x: %d\n", x);
|
||||
S s;
|
||||
s.z = true;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
namespace nondefining
|
||||
{
|
||||
S T1::funend(int m, double n)
|
||||
[[ pre: x < 0 ]]
|
||||
[[ pre: m < 0 ]];
|
||||
|
||||
S T1::funend(int m, double n)
|
||||
{
|
||||
printf("funend::x: %d\n", x);
|
||||
S s;
|
||||
s.z = true;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 25 defining::T1::vfun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 26 defining::T1::vfun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 32 defining::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::f: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 39 defining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 40 defining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 41 defining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 42 defining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 138 defining::T1::funend .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 139 defining::T1::funend .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 68 nondefining::T1::vfun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 69 nondefining::T1::vfun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "vfun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 71 nondefining::T1::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::f: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 73 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 74 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 75 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 76 nondefining::T1::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "fun2::x: 10 fun2::y: 10.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::d: 10.500000(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 151 nondefining::T1::funend .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 152 nondefining::T1::funend .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "funend::x: 10(\n|\r\n|\r)*" }
|
||||
// { dg-output "main::s.z: 1(\n|\r\n|\r)*" }
|
||||
|
||||
95
gcc/testsuite/g++.dg/contracts/contracts-redecl7.C
Normal file
95
gcc/testsuite/g++.dg/contracts/contracts-redecl7.C
Normal file
@@ -0,0 +1,95 @@
|
||||
// test that contracts can be added during (defining) friend declarations
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
#include <cstdio>
|
||||
struct T;
|
||||
|
||||
struct S
|
||||
{
|
||||
int now(int a, T *t) [[ pre: a > 0 ]] [[ pre: x < 0 ]];
|
||||
|
||||
|
||||
int x{-1};
|
||||
};
|
||||
|
||||
int now(int a, T *t) [[ pre: a > 0 ]];
|
||||
int later(int a, T *t);
|
||||
int both(int a, T *t) [[ pre: a > 0 ]];
|
||||
|
||||
struct T
|
||||
{
|
||||
friend int now(int a, T *t);
|
||||
friend int later(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]]
|
||||
{
|
||||
printf("later: a: %d, t->pri: %d\n", a, t->pri);
|
||||
return -a * t->pri;
|
||||
}
|
||||
friend int both(int a, T *t) [[ pre: a > 0 ]]
|
||||
{
|
||||
printf("both: a: %d, t->pri: %d\n", a, t->pri);
|
||||
return -a * t->pri;
|
||||
}
|
||||
|
||||
|
||||
friend int S::now(int a, T *t);
|
||||
|
||||
friend int hidden(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]]
|
||||
{
|
||||
printf("hidden: a: %d, t->pri: %d\n", a, t->pri);
|
||||
return -a * t->pri;
|
||||
}
|
||||
friend int hidden2(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]]
|
||||
{
|
||||
printf("hidden2: a: %d, t->pri: %d\n", a, t->pri);
|
||||
return -a * t->pri;
|
||||
}
|
||||
|
||||
int x{1};
|
||||
private:
|
||||
int pri{-10};
|
||||
};
|
||||
|
||||
int hidden2(int a, T *t) [[ pre: a > 0 ]] [[ pre: t->pri > 0 ]];
|
||||
|
||||
int S::now(int a, T *t)
|
||||
{
|
||||
printf("S::now: a: %d, t->pri: %d\n", a, t->pri);
|
||||
return -a * t->pri;
|
||||
}
|
||||
|
||||
int now(int a, T *t)
|
||||
{
|
||||
printf("now: a: %d, t->pri: %d\n", a, t->pri);
|
||||
return -a * t->pri;
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
T t;
|
||||
S s;
|
||||
s.now(-10, &t);
|
||||
|
||||
now(-20, &t);
|
||||
later(-21, &t);
|
||||
both(-22, &t);
|
||||
hidden(-23, &t);
|
||||
hidden2(-24, &t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 9 S::now .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "S::now: a: -10, t->pri: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 15 now .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "now: a: -20, t->pri: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 22 later .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 22 later .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "later: a: -21, t->pri: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 27 both .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "both: a: -22, t->pri: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 36 hidden .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 36 hidden .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "hidden: a: -23, t->pri: -10(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 41 hidden2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 41 hidden2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "hidden2: a: -24, t->pri: -10(\n|\r\n|\r)*" }
|
||||
|
||||
64
gcc/testsuite/g++.dg/contracts/contracts-redecl8.C
Normal file
64
gcc/testsuite/g++.dg/contracts/contracts-redecl8.C
Normal file
@@ -0,0 +1,64 @@
|
||||
// test that contracts are matched on friend decls when the type is complete
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
struct T;
|
||||
|
||||
int both(int x, T *t) [[ pre: x > 0 ]] { return 0; }
|
||||
int both2(int x, T *t) [[ pre: x > 0 ]];
|
||||
|
||||
template<typename Z>
|
||||
int fn(int x, Z *z) [[ pre: x > 0 ]];
|
||||
|
||||
template<typename Z>
|
||||
int fn2(int x, Z *z);
|
||||
|
||||
template<typename Z>
|
||||
int fn3(int x, Z *z) [[ pre: x > 0 ]];
|
||||
|
||||
template<>
|
||||
int fn3<T>(int x, T *z) [[ pre: x > 1 ]];
|
||||
|
||||
struct T
|
||||
{
|
||||
friend int both2(int x, T *t) [[ pre: x > 1 ]] // { dg-error "mismatched" }
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
friend int hidden(int x, T *t)
|
||||
[[ pre: x > 1 ]] [[ pre: t->pri > 0 ]]
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
/* cannot define friend spec, so we never get to matching contracts
|
||||
friend int fn<T>(int x, T *t)
|
||||
[[ pre: t->pri > 0 ]] { return 0; } // error defining explicit spec friend
|
||||
*/
|
||||
|
||||
// bad, general contracts must match general
|
||||
template<typename Z>
|
||||
friend int fn(int x, Z *z)
|
||||
[[ pre: z->pri > 1 ]] { return 0; } // { dg-error "mismatched" }
|
||||
|
||||
// fine, can add contracts
|
||||
template<typename Z>
|
||||
friend int fn2(int x, Z *z)
|
||||
[[ pre: z->pri > 1 ]] { return 0; } // { dg-bogus "mismatched" }
|
||||
|
||||
/* cannot declare without definition, so dup friend can't occur:
|
||||
friend int dup(int x, T *t)
|
||||
[[ pre: t->pri > 0 ]]; // error non-defining friend with contracts
|
||||
friend int dup(int x, T *t)
|
||||
[[ pre: t->pri > 1 ]]; // error non-defining friend with contracts
|
||||
*/
|
||||
|
||||
int x{1};
|
||||
private:
|
||||
int pri{-10};
|
||||
};
|
||||
|
||||
int hidden(int x, T *t)
|
||||
[[ pre: x > 0 ]] [[ pre: t->pri > 1 ]]; // { dg-error "mismatched" }
|
||||
|
||||
19
gcc/testsuite/g++.dg/contracts/contracts-tmpl-attr1.C
Normal file
19
gcc/testsuite/g++.dg/contracts/contracts-tmpl-attr1.C
Normal file
@@ -0,0 +1,19 @@
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
template<typename T>
|
||||
[[z]]
|
||||
[[nodiscard]]
|
||||
T fun(T n)
|
||||
[[ pre: n > 0 ]]
|
||||
[[ post r: r > 0 ]] // { dg-warning ".z. attribute.*ignored" }
|
||||
{
|
||||
return n;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
fun(-5); // { dg-warning "ignoring return value" }
|
||||
fun(-5.3); // { dg-warning "ignoring return value" }
|
||||
return 0;
|
||||
}
|
||||
|
||||
121
gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec1.C
Normal file
121
gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec1.C
Normal file
@@ -0,0 +1,121 @@
|
||||
// basic test to ensure pre contracts work for free template specializations
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
#include <cstdio>
|
||||
|
||||
template<typename T>
|
||||
int body(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
T t = a * 2.5;
|
||||
return t;
|
||||
}
|
||||
|
||||
template<>
|
||||
int body<double>(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
double t = a * 3.3;
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int none(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
template<>
|
||||
int none<double>(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
return a - 100;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int arg0(T t)
|
||||
[[ pre: t > 0 ]]
|
||||
{
|
||||
return -t - 10;
|
||||
}
|
||||
|
||||
template<>
|
||||
int arg0<double>(double t)
|
||||
[[ pre: t > 0 ]]
|
||||
{
|
||||
return -t + 10;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int arg1(int a, T t)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ pre: t > 0 ]]
|
||||
{
|
||||
return -t * a;
|
||||
}
|
||||
|
||||
template<>
|
||||
int arg1<double>(int a, double t)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ pre: t > 0 ]]
|
||||
{
|
||||
return -t * a + 17;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ret(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
template<>
|
||||
double ret<double>(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
return -a * 3.3;
|
||||
}
|
||||
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
printf("%d\n", body<int>(-1));
|
||||
printf("%d\n", body<double>(-1));
|
||||
printf("%d\n", none<int>(-1));
|
||||
printf("%d\n", none<double>(-1));
|
||||
printf("%d\n", arg0(-1));
|
||||
printf("%d\n", arg0(-1.0));
|
||||
printf("%d\n", arg1(-3, -1));
|
||||
printf("%d\n", arg1(-3, -1.0));
|
||||
printf("%d\n", (int)ret<int>(-1));
|
||||
printf("%d\n", (int)ret<double>(-1));
|
||||
printf("%f\n", ret<double>(-1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 8 body<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-2(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 16 body<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-3(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 24 none<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "1(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 31 none<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-101(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 38 arg0<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-9(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 45 arg0<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "11(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 52 arg1<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 53 arg1<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-3(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 60 arg1<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 61 arg1<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "14(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 68 ret<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "1(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 75 ret<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "3(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 75 ret<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "3.300000(\n|\r\n|\r)*" }
|
||||
|
||||
395
gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec2.C
Normal file
395
gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec2.C
Normal file
@@ -0,0 +1,395 @@
|
||||
// basic test to ensure contracts work for class and member specializations
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
#include <cstdio>
|
||||
|
||||
// template specializations can have differing contracts
|
||||
template<typename T>
|
||||
int body(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
T t = a * 2.5;
|
||||
return t;
|
||||
}
|
||||
|
||||
template<>
|
||||
int body<double>(int a)
|
||||
[[ pre: a > 1 ]]
|
||||
{
|
||||
double t = a * 3.3;
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int none(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
template<>
|
||||
int none<double>(int a)
|
||||
[[ pre: a > 1 ]]
|
||||
{
|
||||
return a - 100;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int arg0(T t)
|
||||
[[ pre: t > 0 ]]
|
||||
{
|
||||
return -t - 10;
|
||||
}
|
||||
|
||||
template<>
|
||||
int arg0<double>(double t)
|
||||
[[ pre: t > 1 ]]
|
||||
{
|
||||
return -t + 10;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int arg1(int a, T t)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ pre: t > 0 ]]
|
||||
{
|
||||
return -t * a;
|
||||
}
|
||||
|
||||
template<>
|
||||
int arg1<double>(int a, double t)
|
||||
[[ pre: a > 1 ]]
|
||||
[[ pre: t > 1 ]]
|
||||
{
|
||||
return -t * a + 17;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T ret(int a)
|
||||
[[ pre: a > 0 ]]
|
||||
{
|
||||
return -a;
|
||||
}
|
||||
|
||||
template<>
|
||||
double ret<double>(int a)
|
||||
[[ pre: a > 1 ]]
|
||||
{
|
||||
return -a * 3.3;
|
||||
}
|
||||
|
||||
// template specializations can have no contracts
|
||||
template<typename T>
|
||||
int g1(T t) [[ pre: t > 0 ]]
|
||||
{
|
||||
return (int)t;
|
||||
}
|
||||
|
||||
template<>
|
||||
int g1<double>(double t)
|
||||
{
|
||||
return (int)t;
|
||||
}
|
||||
|
||||
// template specializations can have no contracts in the first decl but add
|
||||
// them later
|
||||
template<typename T>
|
||||
int g2(T t) [[ pre: t > 0 ]]
|
||||
{
|
||||
return (int)t;
|
||||
}
|
||||
|
||||
template<>
|
||||
int g2<double>(double t);
|
||||
|
||||
template<>
|
||||
int g2<double>(double t)
|
||||
[[ pre: t < 0 ]]
|
||||
{
|
||||
return (int)t;
|
||||
}
|
||||
|
||||
template<>
|
||||
int g2<char>(char t)
|
||||
[[ pre: t < 'c' ]]
|
||||
{
|
||||
return (int)t;
|
||||
}
|
||||
|
||||
// contracts can be different on the general template, partial and full specs
|
||||
template<typename T, typename S>
|
||||
struct G3
|
||||
{
|
||||
void f(T t, S s)
|
||||
[[ pre: t > 0 ]]
|
||||
[[ pre: s > 0 ]]
|
||||
{
|
||||
printf ("G3 general T S\n");
|
||||
}
|
||||
};
|
||||
|
||||
template<typename S>
|
||||
struct G3<int, S>
|
||||
{
|
||||
void f(int t, S s);
|
||||
};
|
||||
|
||||
template<typename S>
|
||||
void G3<int, S>::f(int t, S s)
|
||||
[[ pre: t > 1 ]]
|
||||
[[ pre: s > 1 ]]
|
||||
{
|
||||
printf ("G3 partial int S\n");
|
||||
}
|
||||
|
||||
template<>
|
||||
void G3<int, double>::f(int t, double s)
|
||||
[[ pre: t > 2 ]]
|
||||
[[ pre: s > 2 ]]
|
||||
{
|
||||
printf ("G3 full int double\n");
|
||||
}
|
||||
|
||||
struct C
|
||||
{
|
||||
bool operator>(int rhs) { return false; }
|
||||
};
|
||||
|
||||
// deletes contracts
|
||||
template<>
|
||||
void G3<int, C>::f(int t, C s);
|
||||
|
||||
template<>
|
||||
void G3<int, C>::f(int t, C s)
|
||||
{
|
||||
printf ("G3 full int C\n");
|
||||
};
|
||||
|
||||
// specialized ctors
|
||||
template<typename T, typename S>
|
||||
struct G4
|
||||
{
|
||||
G4(T t, S s)
|
||||
[[ pre: t > 0 ]]
|
||||
[[ pre: s > 0 ]]
|
||||
[[ post: x > 0 ]]
|
||||
{
|
||||
printf ("G4 general T S\n");
|
||||
return;
|
||||
}
|
||||
int x{-1};
|
||||
};
|
||||
|
||||
template<typename S>
|
||||
struct G4<char, S>
|
||||
{
|
||||
G4(char t, S s)
|
||||
[[ pre: t > 'c' ]]
|
||||
[[ pre: s > 3 ]]
|
||||
[[ post: x2 > 3 ]]
|
||||
{
|
||||
printf ("G4 partial char S\n");
|
||||
return;
|
||||
}
|
||||
int x2{-1};
|
||||
};
|
||||
|
||||
template<>
|
||||
G4<double, double>::G4(double, double)
|
||||
{
|
||||
printf ("G4 full double double\n");
|
||||
return;
|
||||
}
|
||||
|
||||
template<>
|
||||
G4<double, char>::G4(double a, char b)
|
||||
[[ pre: a > 0 ]]
|
||||
[[ pre: b > 'b' ]]
|
||||
[[ post: x > 1 ]]
|
||||
{
|
||||
printf ("G4 full double char\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// crossover of template classes and template members ok
|
||||
template<typename T, typename S>
|
||||
struct G5
|
||||
{
|
||||
template<typename P>
|
||||
void f(T t, S s, P r)
|
||||
[[ pre: t > 0 ]]
|
||||
[[ pre: s > 0 ]]
|
||||
[[ pre: r > 0 ]]
|
||||
{
|
||||
printf ("G5 gen T S, f gen R\n");
|
||||
}
|
||||
};
|
||||
|
||||
template<typename S>
|
||||
struct G5<char, S>
|
||||
{
|
||||
template<typename R>
|
||||
void f(char x, S y, R z)
|
||||
[[ pre: x > 'z' ]]
|
||||
[[ pre: y > 1 ]]
|
||||
[[ pre: z > 1 ]]
|
||||
{
|
||||
printf ("G5 partial char S, f gen R\n");
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
template<typename Q>
|
||||
void G5<double, double>::f(double a, double b, Q c)
|
||||
[[ pre: a > 2 ]]
|
||||
[[ pre: b > 2 ]]
|
||||
[[ pre: c > 2 ]]
|
||||
{
|
||||
printf ("G5 full double double, f gen R\n");
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
printf("%d\n", body<int>(-1));
|
||||
printf("%d\n", body<double>(-1));
|
||||
printf("%d\n", none<int>(-1));
|
||||
printf("%d\n", none<double>(-1));
|
||||
printf("%d\n", arg0(-1));
|
||||
printf("%d\n", arg0(-1.0));
|
||||
printf("%d\n", arg1(-3, -1));
|
||||
printf("%d\n", arg1(-3, -1.0));
|
||||
printf("%d\n", (int)ret<int>(-1));
|
||||
printf("%d\n", (int)ret<double>(-1));
|
||||
printf("%f\n", ret<double>(-1));
|
||||
|
||||
printf("%d\n", g1(-1));
|
||||
printf("%d\n", g1(-1.0));
|
||||
|
||||
printf("%d\n", g2(-1));
|
||||
printf("%d\n", g2(1.0));
|
||||
printf("%d\n", g2('d'));
|
||||
|
||||
G3<double, double> g3_gen;
|
||||
G3<int, int> g3_partial;
|
||||
G3<int, double> g3_full;
|
||||
g3_gen.f(-1.0, -1.0); // general
|
||||
g3_partial.f(-2, -2); // partial spec
|
||||
g3_full.f(-3, -3.0); // full spec
|
||||
|
||||
G3<char, char> g3_gen2;
|
||||
G3<int, char> g3_partial2;
|
||||
g3_gen2.f((char)-1, (char)-1);
|
||||
g3_partial2.f(-1, (char)-1);
|
||||
|
||||
G3<int, C> g3_full2;
|
||||
g3_full2.f(5, C{});
|
||||
g3_full2.f(-5, C{});
|
||||
|
||||
G4 g4_gen{-1, -1};
|
||||
G4 g4_full1{-1.0, -1.0};
|
||||
G4 g4_full2{-1.0, (char)'b'};
|
||||
G4 g4_partial{(char)'c', -5};
|
||||
|
||||
G5<int, int> g5_gen;
|
||||
g5_gen.f(-1, -1, -2);
|
||||
g5_gen.f(-1, -1, -2.0);
|
||||
|
||||
G5<char, int> g5_part;
|
||||
g5_part.f('a', -1, -2);
|
||||
g5_part.f('a', -1, -2.1);
|
||||
|
||||
G5<double, double> g5_full;
|
||||
g5_full.f(-1.0, -1.0, -2);
|
||||
g5_full.f(-1.0, -1.0, -2.1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 9 body<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-2(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 17 body<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-3(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 25 none<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "1(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 32 none<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-101(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 39 arg0<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-9(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 46 arg0<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "11(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 53 arg1<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 54 arg1<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-3(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 61 arg1<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 62 arg1<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "14(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 69 ret<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "1(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 76 ret<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "3(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 76 ret<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "3.300000(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 83 g1<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-1(\n|\r\n|\r)*" }
|
||||
// { dg-output "-1(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 97 g2<int> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "-1(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 107 g2<double> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "1(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 114 g2<char> .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "100(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 124 G3<double, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 125 G3<double, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G3 general T S(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 139 G3<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 140 G3<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G3 partial int S(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 147 G3<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 148 G3<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G3 full int double(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 124 G3<char, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 125 G3<char, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G3 general T S(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 139 G3<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 140 G3<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G3 partial int S(\n|\r\n|\r)*" }
|
||||
// { dg-output "G3 full int C(\n|\r\n|\r)*" }
|
||||
// { dg-output "G3 full int C(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 173 G4<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 174 G4<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G4 general T S(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 175 G4<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G4 full double double(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 206 G4<double, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 207 G4<double, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G4 full double char(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 208 G4<double, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 187 G4<char, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 188 G4<char, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G4 partial char S(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 189 G4<char, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 220 G5<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 221 G5<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 222 G5<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G5 gen T S, f gen R(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 220 G5<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 221 G5<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 222 G5<int, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G5 gen T S, f gen R(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 233 G5<char, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 234 G5<char, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 235 G5<char, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G5 partial char S, f gen R(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 233 G5<char, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 234 G5<char, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 235 G5<char, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G5 partial char S, f gen R(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 244 G5<double, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 245 G5<double, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 246 G5<double, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G5 full double double, f gen R(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 244 G5<double, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 245 G5<double, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 246 G5<double, .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G5 full double double, f gen R(\n|\r\n|\r)*" }
|
||||
|
||||
45
gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec3.C
Normal file
45
gcc/testsuite/g++.dg/contracts/contracts-tmpl-spec3.C
Normal file
@@ -0,0 +1,45 @@
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
#include <cstdio>
|
||||
|
||||
template<typename T, typename S>
|
||||
struct G5
|
||||
{
|
||||
template<typename R>
|
||||
void f(T t, S s, R r)
|
||||
[[ pre: t > 0 ]] [[ pre: s > 0 ]] [[ pre: r > 0 ]]
|
||||
{
|
||||
printf ("G5 gen T S, f gen R\n");
|
||||
}
|
||||
};
|
||||
|
||||
// specializations can remove contracts
|
||||
template<>
|
||||
template<typename R>
|
||||
void G5<double, double>::f(double a, double b, R c)
|
||||
{
|
||||
printf ("G5 full double double, f gen R\n");
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
G5<double, double> g5_full;
|
||||
g5_full.f(-1.0, -1.0, -2);
|
||||
g5_full.f(-1.0, -1.0, -2.1);
|
||||
|
||||
G5<int, double> g5_gen;
|
||||
g5_gen.f(-1, -1.0, -2);
|
||||
g5_gen.f(-1, -1.0, -2.1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "G5 full double double, f gen R(\n|\r\n|\r)*" }
|
||||
// { dg-output "G5 full double double, f gen R(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* t > 0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* s > 0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* r > 0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G5 gen T S, f gen R(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* t > 0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* s > 0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 10 G5<int, .* r > 0 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "G5 gen T S, f gen R(\n|\r\n|\r)*" }
|
||||
|
||||
49
gcc/testsuite/g++.dg/contracts/contracts1.C
Normal file
49
gcc/testsuite/g++.dg/contracts/contracts1.C
Normal file
@@ -0,0 +1,49 @@
|
||||
// generic assert contract parsing checks
|
||||
// check omitted, 'default', 'audit', and 'axiom' contract levels parse
|
||||
// check that all concrete semantics parse
|
||||
// check omitted, '%default' contract roles parse
|
||||
// ensure that an invalid contract level 'invalid' errors
|
||||
// ensure that a predicate referencing an undefined variable errors
|
||||
// ensure that a missing colon after contract level errors
|
||||
// ensure that an invalid contract role 'invalid' errors
|
||||
// ensure that a missing colon after contract role errors
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
static_assert (__cpp_contracts >= 201906);
|
||||
static_assert (__cpp_contracts_literal_semantics >= 201906);
|
||||
static_assert (__cpp_contracts_roles >= 201906);
|
||||
|
||||
int main()
|
||||
{
|
||||
int x;
|
||||
|
||||
[[assert: x >= 0]];
|
||||
[[assert default: x < 0]];
|
||||
[[assert audit: x == 0]];
|
||||
[[assert axiom: x == 1]];
|
||||
|
||||
[[assert: x > 0 ? true : false]];
|
||||
[[assert: x < 0 ? true : false]];
|
||||
|
||||
[[assert: x = 0]]; // { dg-error "expected .]. before .=. token" }
|
||||
|
||||
[[assert ignore: x >= 0]];
|
||||
[[assert assume: x >= 0]];
|
||||
[[assert check_never_continue: x >= 0]];
|
||||
[[assert check_maybe_continue: x >= 0]];
|
||||
|
||||
[[assert %default: x >= 0]];
|
||||
[[assert default %default: x < 0]];
|
||||
[[assert audit %default: x == 0]];
|
||||
[[assert axiom %default: x == 1]];
|
||||
|
||||
[[assert check_always_continue: x >= 0]]; // { dg-error "expected contract level" }
|
||||
[[assert invalid: x == 0]]; // { dg-error "expected contract level" }
|
||||
[[assert: y == 0]]; // { dg-error ".y. was not declared in this scope" }
|
||||
[[assert default x == 0]]; // { dg-error "expected .:. before .x." }
|
||||
[[assert %default x >= 0]]; // { dg-error "expected .:. before .x." }
|
||||
|
||||
[[assert %invalid: x >= 0]]; // TODO: optional warning?
|
||||
return 0;
|
||||
}
|
||||
73
gcc/testsuite/g++.dg/contracts/contracts10.C
Normal file
73
gcc/testsuite/g++.dg/contracts/contracts10.C
Normal file
@@ -0,0 +1,73 @@
|
||||
// general checks to ensure that contract violations are generated during
|
||||
// runtime when appropriate
|
||||
// each check also validates the expected file name, line number, function,
|
||||
// predicate, and contract level are included in the violation_info object
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-build-level=audit -fcontract-continuation-mode=on" }
|
||||
|
||||
namespace tns
|
||||
{
|
||||
int fun()
|
||||
{
|
||||
int x = 1;
|
||||
[[ assert: x < 0 ]];
|
||||
return 0;
|
||||
}
|
||||
int fun2();
|
||||
|
||||
struct TestType
|
||||
{
|
||||
static int fun();
|
||||
static int fun2()
|
||||
{
|
||||
int x = 1;
|
||||
[[ assert: x < 0 ]];
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int tns::fun2()
|
||||
{
|
||||
int x = 1;
|
||||
[[ assert: x < 0 ]];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tns::TestType::fun()
|
||||
{
|
||||
int x = 1;
|
||||
[[ assert: x < 0 ]];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int x = 100;
|
||||
[[assert: x < 0]];
|
||||
[[assert default: x < 1]];
|
||||
[[assert audit: x < 2]];
|
||||
// contract_violation.line_number() may eventually come from
|
||||
// std::source_location which *is* affected by the #line macro; our current
|
||||
// implementation conforms to this so we've included it as a check
|
||||
#line 100
|
||||
[[assert: x < 3]];
|
||||
[[assert axiom: x < 4]];
|
||||
|
||||
tns::fun();
|
||||
tns::fun2();
|
||||
|
||||
tns::TestType::fun();
|
||||
tns::TestType::fun2();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 47 main .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 48 main .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 49 main .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 100 main .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 13 tns::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 33 tns::fun2 .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 40 tns::TestType::fun .*(\n|\r\n|\r)*" }
|
||||
// { dg-output "default std::handle_contract_violation called: .*.C 24 tns::TestType::fun2 .*(\n|\r\n|\r)*" }
|
||||
|
||||
103
gcc/testsuite/g++.dg/contracts/contracts11.C
Normal file
103
gcc/testsuite/g++.dg/contracts/contracts11.C
Normal file
@@ -0,0 +1,103 @@
|
||||
// ensure that assert contract predicates that are not convertible to bool
|
||||
// generate an error
|
||||
// ensure the same for instatiated template functions
|
||||
// ensure the same for non-instatiated template functions when the predicate
|
||||
// is not dependent on the template parameters
|
||||
// ensure template parameter dependent, potentially non-boolean, contract
|
||||
// predicates do not generate an error if never instatiated
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
void fun()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void fun2(T a)
|
||||
{
|
||||
[[assert: fun()]]; // { dg-error "could not convert|in argument" }
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void fun3(T a)
|
||||
{
|
||||
[[assert: fun()]]; // { dg-error "could not convert|in argument" }
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void fun4(T a)
|
||||
{
|
||||
[[assert: a.fun()]];
|
||||
}
|
||||
|
||||
struct test
|
||||
{
|
||||
void fun() { }
|
||||
void fun2() { }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void fun5(T a)
|
||||
{
|
||||
[[assert: a.fun2()]]; // { dg-error "could not convert" }
|
||||
}
|
||||
|
||||
struct VoidFun
|
||||
{
|
||||
void fun() { }
|
||||
};
|
||||
struct BoolFun
|
||||
{
|
||||
bool fun() { return true; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void fun6(T a)
|
||||
{
|
||||
[[ assert: a.fun() ]]; // { dg-error "could not convert" }
|
||||
}
|
||||
|
||||
template void fun6(VoidFun);
|
||||
|
||||
template<typename T>
|
||||
void fun7(T a)
|
||||
{
|
||||
[[ assert: a.fun() ]];
|
||||
}
|
||||
|
||||
template void fun7(BoolFun);
|
||||
|
||||
struct ImplicitBool
|
||||
{
|
||||
operator bool() { return true; }
|
||||
};
|
||||
struct ExplicitBool
|
||||
{
|
||||
explicit operator bool() { return true; }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void fun8(T a)
|
||||
{
|
||||
[[ assert: T() ]];
|
||||
}
|
||||
|
||||
template void fun8(ImplicitBool);
|
||||
template void fun8(ExplicitBool);
|
||||
|
||||
void fun9()
|
||||
{
|
||||
[[ assert: ImplicitBool() ]];
|
||||
[[ assert: ExplicitBool() ]];
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
[[assert: fun()]]; // { dg-error "could not convert" }
|
||||
fun2(1);
|
||||
|
||||
test t;
|
||||
fun5(t);
|
||||
return 0;
|
||||
}
|
||||
15
gcc/testsuite/g++.dg/contracts/contracts12.C
Normal file
15
gcc/testsuite/g++.dg/contracts/contracts12.C
Normal file
@@ -0,0 +1,15 @@
|
||||
// ensure that constants for contract levels are inserted into the binary when
|
||||
// used and omitted when the runtime check is not generated
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-build-level=default" }
|
||||
// { dg-final { scan-assembler-not "audit" } }
|
||||
// { dg-final { scan-assembler "default" } }
|
||||
|
||||
int main()
|
||||
{
|
||||
int x = 1;
|
||||
[[assert: x < 0]];
|
||||
[[assert default: x < 0]];
|
||||
[[assert audit: x < 0]];
|
||||
return 0;
|
||||
}
|
||||
51
gcc/testsuite/g++.dg/contracts/contracts13.C
Normal file
51
gcc/testsuite/g++.dg/contracts/contracts13.C
Normal file
@@ -0,0 +1,51 @@
|
||||
// ensure that passing asserts do not affect constexpr functions
|
||||
// ensure that failing asserts generate an error in a constexpr function
|
||||
// { dg-do compile }
|
||||
// { dg-options "-std=c++2a -fcontracts" }
|
||||
|
||||
constexpr int wfun(int a) {
|
||||
[[assert: a > 0]];
|
||||
return a;
|
||||
}
|
||||
|
||||
constexpr int ffun(int a) {
|
||||
[[assert: a > 0]];
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int tfun(T a) {
|
||||
[[assert: a > 0]];
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int wtfun(T a) {
|
||||
[[assert: a > 0]];
|
||||
return a;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int ftfun(T a) {
|
||||
[[assert: a > 0]];
|
||||
return a;
|
||||
}
|
||||
|
||||
constexpr int explicitfn(int a) {
|
||||
[[assert ignore: a > 0]];
|
||||
[[assert check_never_continue: a > 0]];
|
||||
return a;
|
||||
}
|
||||
|
||||
int main(int, char **) {
|
||||
constexpr int a = wfun(10);
|
||||
constexpr int b = ffun(-10); // { dg-message "in .constexpr. expansion" }
|
||||
// { dg-error "contract predicate" "" { target *-*-* } 12 }
|
||||
constexpr int c = wtfun(10);
|
||||
constexpr int d = ftfun(-10); // { dg-message "in .constexpr. expansion" }
|
||||
// { dg-error "contract predicate" "" { target *-*-* } 30 }
|
||||
constexpr int e = explicitfn(-10); // { dg-message "in .constexpr. expansion" }
|
||||
// { dg-error "contract predicate" "" { target *-*-* } 36 }
|
||||
return 0;
|
||||
}
|
||||
|
||||
58
gcc/testsuite/g++.dg/contracts/contracts14.C
Normal file
58
gcc/testsuite/g++.dg/contracts/contracts14.C
Normal file
@@ -0,0 +1,58 @@
|
||||
// ensure that exceptions thrown inside a custom contract violation handler
|
||||
// are catchable up the call stack
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
#include <iostream>
|
||||
#include <experimental/contract>
|
||||
|
||||
void handle_contract_violation(const std::experimental::contract_violation &violation) {
|
||||
std::cerr << "custom std::handle_contract_violation called:"
|
||||
<< " " << violation.line_number()
|
||||
<< " " << violation.file_name()
|
||||
<< std::endl;
|
||||
throw -(int)violation.line_number();
|
||||
}
|
||||
|
||||
int fun() {
|
||||
int x = 0;
|
||||
[[ assert: x < 0 ]];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fun3() {
|
||||
fun();
|
||||
return 2;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
try {
|
||||
int x = 0;
|
||||
[[ assert: x < 0 ]];
|
||||
} catch(int &ex) {
|
||||
std::cerr << "synth caught direct: " << ex << std::endl;
|
||||
}
|
||||
|
||||
try {
|
||||
fun();
|
||||
} catch(int &ex) {
|
||||
std::cerr << "synth caught indirect: " << ex << std::endl;
|
||||
}
|
||||
|
||||
try {
|
||||
fun3();
|
||||
} catch(int &ex) {
|
||||
std::cerr << "synth caught double indirect: " << ex << std::endl;
|
||||
}
|
||||
|
||||
std::cerr << "end main" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "custom std::handle_contract_violation called: 30 .*/contracts14.C(\n|\r\n|\r)*" }
|
||||
// { dg-output "synth caught direct: -30(\n|\r\n|\r)*" }
|
||||
// { dg-output "custom std::handle_contract_violation called: 18 .*/contracts14.C(\n|\r\n|\r)*" }
|
||||
// { dg-output "synth caught indirect: -18(\n|\r\n|\r)*" }
|
||||
// { dg-output "custom std::handle_contract_violation called: 18 .*/contracts14.C(\n|\r\n|\r)*" }
|
||||
// { dg-output "synth caught double indirect: -18(\n|\r\n|\r)*" }
|
||||
// { dg-output "end main" }
|
||||
|
||||
56
gcc/testsuite/g++.dg/contracts/contracts15.C
Normal file
56
gcc/testsuite/g++.dg/contracts/contracts15.C
Normal file
@@ -0,0 +1,56 @@
|
||||
// ensure that exceptions thrown inside a custom contract violation handler
|
||||
// are not catchable up the call stack when failing in a noexcept function
|
||||
// { dg-do run }
|
||||
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on" }
|
||||
#include <iostream>
|
||||
#include <experimental/contract>
|
||||
|
||||
void handle_contract_violation(const std::experimental::contract_violation &violation) {
|
||||
std::cerr << "custom std::handle_contract_violation called:"
|
||||
<< " " << violation.line_number()
|
||||
<< " " << violation.file_name()
|
||||
<< std::endl;
|
||||
throw -(int)violation.line_number();
|
||||
}
|
||||
|
||||
int fun() noexcept {
|
||||
int x = 0;
|
||||
[[ assert: x < 0 ]];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fun3() {
|
||||
fun();
|
||||
return 2;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
try {
|
||||
int x = 0;
|
||||
[[ assert: x < 0 ]];
|
||||
} catch(int &ex) {
|
||||
std::cerr << "synth caught direct: " << ex << std::endl;
|
||||
}
|
||||
|
||||
try {
|
||||
fun();
|
||||
} catch(int &ex) {
|
||||
std::cerr << "synth caught indirect: " << ex << std::endl;
|
||||
}
|
||||
|
||||
try {
|
||||
fun3();
|
||||
} catch(int &ex) {
|
||||
std::cerr << "synth caught double indirect: " << ex << std::endl;
|
||||
}
|
||||
|
||||
std::cerr << "end main" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// { dg-output "custom std::handle_contract_violation called: 30 .*/contracts15.C(\n|\r\n|\r)*" }
|
||||
// { dg-output "synth caught direct: -30(\n|\r\n|\r)*" }
|
||||
// { dg-output "custom std::handle_contract_violation called: 18 .*/contracts15.C(\n|\r\n|\r)*" }
|
||||
// { dg-output "terminate called after throwing an instance of .int.(\n|\r\n|\r)*" }
|
||||
// { dg-shouldfail "throwing in noexcept" }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user