mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 12:51:04 +01:00
Improve precedence computation in stringifier. (#7833)
Assisted-by: Claude via Antigravity
This commit is contained in:
+39
@@ -127,6 +127,45 @@ fn PassConstReferenceToReference(p: const X*) {
|
||||
p->(X.TakeSelf)();
|
||||
}
|
||||
|
||||
// --- fail_print_const_facet_type.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
|
||||
interface I { let T: type; }
|
||||
interface J {}
|
||||
|
||||
// A redeclaration with a mismatched return type prints the original return
|
||||
// type. The operand of `const` is parenthesized if it's a facet type formed
|
||||
// with `&` or `where`.
|
||||
fn F() -> const (I & J);
|
||||
// CHECK:STDERR: fail_print_const_facet_type.carbon:[[@LINE+7]]:1: error: function redeclaration differs because return type is `{}` [FunctionRedeclReturnTypeDiffers]
|
||||
// CHECK:STDERR: fn F() -> {};
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_print_const_facet_type.carbon:[[@LINE-4]]:1: note: previously declared with return type `const (I & J)` [FunctionRedeclReturnTypePrevious]
|
||||
// CHECK:STDERR: fn F() -> const (I & J);
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn F() -> {};
|
||||
|
||||
fn G() -> const (I where .T = ());
|
||||
// CHECK:STDERR: fail_print_const_facet_type.carbon:[[@LINE+7]]:1: error: function redeclaration differs because return type is `{}` [FunctionRedeclReturnTypeDiffers]
|
||||
// CHECK:STDERR: fn G() -> {};
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_print_const_facet_type.carbon:[[@LINE-4]]:1: note: previously declared with return type `const (I where .(I.T) = ())` [FunctionRedeclReturnTypePrevious]
|
||||
// CHECK:STDERR: fn G() -> const (I where .T = ());
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn G() -> {};
|
||||
|
||||
// No parentheses are needed for a single interface.
|
||||
fn H() -> const I;
|
||||
// CHECK:STDERR: fail_print_const_facet_type.carbon:[[@LINE+7]]:1: error: function redeclaration differs because return type is `{}` [FunctionRedeclReturnTypeDiffers]
|
||||
// CHECK:STDERR: fn H() -> {};
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~
|
||||
// CHECK:STDERR: fail_print_const_facet_type.carbon:[[@LINE-4]]:1: note: previously declared with return type `const I` [FunctionRedeclReturnTypePrevious]
|
||||
// CHECK:STDERR: fn H() -> const I;
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn H() -> {};
|
||||
|
||||
// CHECK:STDOUT: --- basic.carbon
|
||||
// CHECK:STDOUT:
|
||||
|
||||
+18
@@ -145,6 +145,24 @@ fn UseM(generic c: C, x: c.M(true));
|
||||
// CHECK:STDERR:
|
||||
fn UseM(generic c: C, x: bool);
|
||||
|
||||
// Parentheses are added around the `self` argument if needed.
|
||||
interface J {
|
||||
eval fn N(self: Self) -> type;
|
||||
}
|
||||
impl type as J {
|
||||
eval fn N(self: Self) -> type { return self; }
|
||||
}
|
||||
|
||||
fn UseN(generic T: type, x: (const T).(J.N)());
|
||||
// CHECK:STDERR: fail_print_call_in_diagnostic.carbon:[[@LINE+7]]:26: error: type `<pattern for bool>` of parameter 2 in redeclaration differs from previous parameter type `<pattern for (const T).N()>` [RedeclParamDiffersType]
|
||||
// CHECK:STDERR: fn UseN(generic T: type, x: bool);
|
||||
// CHECK:STDERR: ^~~~~~~
|
||||
// CHECK:STDERR: fail_print_call_in_diagnostic.carbon:[[@LINE-4]]:26: note: previous declaration's corresponding parameter here [RedeclParamPrevious]
|
||||
// CHECK:STDERR: fn UseN(generic T: type, x: (const T).(J.N)());
|
||||
// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~
|
||||
// CHECK:STDERR:
|
||||
fn UseN(generic T: type, x: bool);
|
||||
|
||||
// CHECK:STDOUT: --- dependent_symbolic_instruction.carbon
|
||||
// CHECK:STDOUT:
|
||||
// CHECK:STDOUT: constants {
|
||||
|
||||
@@ -25,18 +25,82 @@
|
||||
|
||||
namespace Carbon::SemIR {
|
||||
|
||||
// Map an instruction kind representing an expression into an integer describing
|
||||
// the precedence of that expression's syntax. Higher numbers correspond to
|
||||
// higher precedence.
|
||||
static auto GetPrecedence(InstKind kind) -> int {
|
||||
if (kind == ConstType::Kind) {
|
||||
return -1;
|
||||
// Precedence levels for the syntax produced when stringifying an instruction.
|
||||
// Higher numbers correspond to higher precedence. An operand needs to be
|
||||
// parenthesized if its precedence is lower than that required by the enclosing
|
||||
// syntax.
|
||||
//
|
||||
// Carbon's precedence is a partial order, not a total order, but the only
|
||||
// enclosing syntax that we currently check for is prefix and postfix operators,
|
||||
// which have higher precedence than all infix operators, so a total order
|
||||
// suffices for now.
|
||||
enum class Precedence : int8_t {
|
||||
// `A where ...`
|
||||
Where = -5,
|
||||
// `A & B`
|
||||
BitwiseAnd = -4,
|
||||
// `A as B`
|
||||
As = -3,
|
||||
// `T*`
|
||||
PostfixStar = -2,
|
||||
// `const T`, `partial T`
|
||||
PrefixType = -1,
|
||||
// Names, literals, calls, member access, and anything parenthesized or
|
||||
// otherwise bracketed.
|
||||
Primary = 0,
|
||||
};
|
||||
|
||||
// Returns the precedence of the syntax that stringifying `inst_id` produces.
|
||||
static auto GetPrecedence(const File& sem_ir, InstId inst_id) -> Precedence {
|
||||
while (inst_id.has_value()) {
|
||||
auto inst = sem_ir.insts().Get(inst_id);
|
||||
switch (inst.kind()) {
|
||||
case Call::Kind:
|
||||
case TypeOfInst::Kind: {
|
||||
// These print their constant value instead, if it's different.
|
||||
auto const_inst_id =
|
||||
sem_ir.constant_values().GetConstantInstId(inst_id);
|
||||
if (!const_inst_id.has_value() || const_inst_id == inst_id) {
|
||||
return Precedence::Primary;
|
||||
}
|
||||
inst_id = const_inst_id;
|
||||
break;
|
||||
}
|
||||
case FacetAccessType::Kind: {
|
||||
inst_id = inst.As<FacetAccessType>().facet_value_inst_id;
|
||||
break;
|
||||
}
|
||||
case FacetType::Kind: {
|
||||
const auto& info = sem_ir.declared_facet_types().Get(
|
||||
inst.As<FacetType>().declared_facet_type_id);
|
||||
if (info.other_requirements || !info.rewrite_constraints.empty() ||
|
||||
!info.self_impls_constraints.empty() ||
|
||||
!info.self_impls_named_constraints.empty() ||
|
||||
!info.type_impls_interfaces.empty() ||
|
||||
!info.type_impls_named_constraints.empty()) {
|
||||
return Precedence::Where;
|
||||
}
|
||||
if (info.extend_constraints.size() +
|
||||
info.extend_named_constraints.size() >
|
||||
1) {
|
||||
return Precedence::BitwiseAnd;
|
||||
}
|
||||
return Precedence::Primary;
|
||||
}
|
||||
case FacetValue::Kind:
|
||||
case LookupImplWitness::Kind:
|
||||
return Precedence::As;
|
||||
case PointerType::Kind:
|
||||
return Precedence::PostfixStar;
|
||||
case ConstType::Kind:
|
||||
case PartialType::Kind:
|
||||
return Precedence::PrefixType;
|
||||
default:
|
||||
// TODO: Handle other kinds of expressions with precedence.
|
||||
return Precedence::Primary;
|
||||
}
|
||||
}
|
||||
if (kind == PointerType::Kind) {
|
||||
return -2;
|
||||
}
|
||||
// TODO: Handle other kinds of expressions with precedence.
|
||||
return 0;
|
||||
return Precedence::Primary;
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -532,8 +596,7 @@ class Stringifier {
|
||||
}
|
||||
|
||||
// Add parentheses if required.
|
||||
if (GetPrecedence(sem_ir_->insts().Get(inner_id).kind()) <
|
||||
GetPrecedence(ConstType::Kind)) {
|
||||
if (GetPrecedence(*sem_ir_, inner_id) < Precedence::PrefixType) {
|
||||
*out_ << "(";
|
||||
// Note the `inner_id` ends up here.
|
||||
step_stack_->PushString(")");
|
||||
|
||||
Reference in New Issue
Block a user